Files
baiyue-logistics-chat/logistics_chat/tracking.py
T

79 lines
2.9 KiB
Python
Raw Normal View History

"""Extracted tracking rules with unchanged business behavior."""
import json
import random
import re
from datetime import datetime, timedelta
CARD_PREFIX = "__CARD__"
def card_to_text(content):
"""把存库的卡片标记转成人话,避免原始 JSON 进入模型上下文"""
if not content.startswith(CARD_PREFIX):
return content
try:
card = json.loads(content[len(CARD_PREFIX):])
return f"(已为运单 {card.get('no', '')} 查询过轨迹,当前状态:{card.get('status', '')})"
except Exception:
return "(已为该运单查询过轨迹)"
def is_tracking_no(text):
"""判断一句话是不是"整句运单号"
规则(逐条收紧,避免误判):
1. 去掉空格和横杠后,长度 8~20 位,且只能由字母数字组成
2. 至少包含 3 个数字(英文句子通常只有 1 个数字)
3. 字母数量不能多于数字("i want to go 1t how money" 这类会被排除)
纯数字单号(如 15252125)全部满足上述条件。
"""
# 日期格式(如 2026-10-23、2026/10/23)不算运单号
if re.fullmatch(r"\d{4}[-/.]\d{1,2}[-/.]\d{1,2}", text.strip()):
return False
t = re.sub(r"[\s\-]", "", text)
if not (8 <= len(t) <= 20):
return False
if not re.fullmatch(r"[A-Za-z0-9]+", t):
return False
digits = sum(c.isdigit() for c in t)
letters = sum(c.isalpha() for c in t)
if digits < 3:
return False
if letters > digits:
return False
return True
def extract_tracking_no(text):
"""从一句话里提取运单号:先看整句是不是,再从句子里找最长的字母数字串"""
t = text.strip()
if is_tracking_no(t):
return t
# 找句子里最长的 8~20 位字母数字连续串
candidates = re.findall(r"[A-Za-z0-9]{8,20}", t)
best = None
for c in candidates:
if is_tracking_no(c):
if best is None or len(c) > len(best):
best = c
return best
def build_track(no):
"""生成固定的模拟轨迹:同一个单号每次查都一样"""
rnd = random.Random(no)
steps = [
("已揽收", "义乌仓"),
("出口清关放行", "义乌/宁波口岸"),
("已发车", "中欧班列/卡航 · 阿拉山口"),
("到达欧洲中转仓", "波兰 · 华沙"),
("欧盟清关放行", "德国 · 杜伊斯堡"),
("派送中", "收件人所在城市"),
]
gaps = [rnd.randint(6, 40) for _ in steps] # 相邻节点间隔(小时)
t = datetime.now() - timedelta(hours=sum(gaps))
nodes = []
for (text, place), gap in zip(steps, gaps):
t = t + timedelta(hours=gap)
nodes.append({"time": t.strftime("%Y-%m-%d %H:%M"),
"text": text, "place": place})
nodes.reverse() # 最新的排最上面
return {"no": no, "status": nodes[0]["text"], "nodes": nodes}