机器人显示名与站点标题可配;多段联运列表投影;协同群监听对账;工单附件落存;模板与工单后台补强
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -279,6 +279,15 @@ def normalize_facts(facts: dict[str, Any] | None) -> dict[str, str]:
|
||||
val = str(raw_val).strip()
|
||||
if not val or val in _PLACEHOLDER_DASHES:
|
||||
continue
|
||||
# 补问提示「请复制粘贴…」等不是日期,入库前丢掉,避免当成已填走出「线路」。
|
||||
if key == "报价日期" and is_blank_quote_date(val):
|
||||
continue
|
||||
# 「请确认并补充以下信息」等标题不是品名(空栏后没换行会粘上)。
|
||||
if key in {"品名", "货物品名"}:
|
||||
from agent.llm.extract_text import is_clarify_ui_prompt
|
||||
|
||||
if is_clarify_ui_prompt(val):
|
||||
continue
|
||||
out[key] = val
|
||||
_fold_land_vehicle_qty(out)
|
||||
return out
|
||||
@@ -319,6 +328,29 @@ def _harvest_packaging(raw: str) -> str:
|
||||
return last
|
||||
|
||||
|
||||
def is_blank_quote_date(raw: str | None) -> bool:
|
||||
"""
|
||||
报价日期是否等于「没填」。
|
||||
|
||||
核对卡空行会写成「报价日期(非必填):」。若把「(非必填):」收成字段值,
|
||||
会挡住选舱写回航班日期(WO202610160033 运行证据)。横杠、装饰词都当空。
|
||||
补问提示「请复制粘贴…」曾被跨行收成日期(核对卡挤行),一律当空。
|
||||
"""
|
||||
text = str(raw or "").strip()
|
||||
if not text:
|
||||
return True
|
||||
if text in {"-", "—", "-", "/", "无", "没有", "暂无"}:
|
||||
return True
|
||||
# 补问 UI 文案、下一栏编号,不是日期。
|
||||
if "请复制粘贴" in text or "告知我以下字段" in text:
|
||||
return True
|
||||
if re.match(r"^\d+[\.、.]", text):
|
||||
return True
|
||||
cleaned = re.sub(r"[((]\s*非必填\s*[))]", "", text)
|
||||
cleaned = cleaned.strip(" \t::,,。;;、")
|
||||
return not cleaned
|
||||
|
||||
|
||||
def _quote_date_labeled_value(raw: str) -> str:
|
||||
"""
|
||||
原话里「报价日期」或同义词后面的值。
|
||||
@@ -327,20 +359,25 @@ def _quote_date_labeled_value(raw: str) -> str:
|
||||
能写成 yyyy-MM-dd 就规范;「明天」这类留原文。
|
||||
没有这些标签则空串,交给句子里的裸日期。
|
||||
同一句出现多次,用最后一次,避免前面的货好时间把日期抢走。
|
||||
标签后可带「(非必填)」;值为空或只剩装饰词时不当作已填。
|
||||
只认冒号后同一行:空行后面的「请复制粘贴…」不能跨行收成日期。
|
||||
"""
|
||||
text = raw or ""
|
||||
labels = ("报价日期",) + QUOTE_DATE_SYNONYMS
|
||||
best_at = -1
|
||||
best_val = ""
|
||||
for label in labels:
|
||||
# 允许「报价日期(非必填):」;禁止把「(非必填):」收成日期值。
|
||||
# [^\S\n] 只吃同行空白,避免 \s 跨行吞粘贴提示。
|
||||
pattern = re.compile(
|
||||
rf"(?<![\u4e00-\u9fffA-Za-z0-9]){re.escape(label)}\s*[::]?\s*(\S+)"
|
||||
rf"(?<![\u4e00-\u9fffA-Za-z0-9]){re.escape(label)}"
|
||||
rf"(?:[^\S\n]*[((]\s*非必填\s*[))])?[^\S\n]*[::]?[^\S\n]*([^\s\n]*)"
|
||||
)
|
||||
for hit in pattern.finditer(text):
|
||||
if hit.start() >= best_at:
|
||||
best_at = hit.start()
|
||||
best_val = hit.group(1).strip().strip(",,。;;、")
|
||||
if not best_val:
|
||||
if is_blank_quote_date(best_val):
|
||||
return ""
|
||||
return _harvest_quote_date(best_val) or _excel_serial_date(best_val) or best_val
|
||||
|
||||
@@ -603,6 +640,16 @@ def harvest_spoken_trade_term(text: str) -> dict[str, str]:
|
||||
_CONTAINER_SPEC = re.compile(
|
||||
r"(?i)(20|40|45)\s*['\"′尺]?\s*(HQ|GP|NOR|RH|HC|高柜|平柜|柜)"
|
||||
)
|
||||
# 陆运车型口语:45HQ(13.5厢车)、9米6/1、13.5厢车、3T。不收 9.11 日期、20件。
|
||||
_RE_LAND_VEHICLE = re.compile(
|
||||
r"(?i)(?:"
|
||||
r"(?:20|40|45)\s*(?:HQ|GP|HC|NOR|RH|高柜|平柜)"
|
||||
r"(?:\s*[((][^))]{1,24}[))])?"
|
||||
r"|(?:\d+(?:\.\d+)?)\s*米\s*[0-9六]?(?:\s*[//xX×*]\s*\d+)?"
|
||||
r"|(?:\d+(?:\.\d+)?)\s*厢车"
|
||||
r"|(?<![\d.])(\d+(?:\.\d+)?)\s*[Tt](?![A-Za-z])"
|
||||
r")"
|
||||
)
|
||||
|
||||
|
||||
def looks_like_container_spec(text: str) -> bool:
|
||||
@@ -610,6 +657,42 @@ def looks_like_container_spec(text: str) -> bool:
|
||||
return bool(_CONTAINER_SPEC.search(text or ""))
|
||||
|
||||
|
||||
def promote_land_vehicle_from_container(facts: dict[str, str] | None) -> dict[str, str]:
|
||||
"""
|
||||
陆运时把误落入「箱型箱量」的车型改写到「车型/数量」。
|
||||
|
||||
DeepSeek B 常把 45HQ/厢车当成海运箱型;陆运必填是车型/数量。
|
||||
已有车型则不覆盖。返回新 dict,不改入参。
|
||||
"""
|
||||
merged = dict(facts or {})
|
||||
vehicle = (merged.get("车型/数量") or merged.get("车型数量") or "").strip()
|
||||
container = (merged.get("箱型箱量") or "").strip()
|
||||
if not vehicle and container:
|
||||
merged["车型/数量"] = container
|
||||
return merged
|
||||
|
||||
|
||||
def harvest_oral_land_vehicle(text: str, facts: dict[str, str] | None = None) -> dict[str, str]:
|
||||
"""
|
||||
从陆运原话机读车型/数量。
|
||||
|
||||
已有「车型/数量」不覆盖。命中 45HQ(13.5厢车)、9米6、厢车、3T 等。
|
||||
只填空字段,不猜港口。返回新 dict。
|
||||
"""
|
||||
merged = dict(facts or {})
|
||||
if (merged.get("车型/数量") or "").strip():
|
||||
return merged
|
||||
raw = _without_clarify_examples(text or "")
|
||||
hit = _RE_LAND_VEHICLE.search(raw)
|
||||
if not hit:
|
||||
return merged
|
||||
token = hit.group(0).strip()
|
||||
if not token:
|
||||
return merged
|
||||
merged["车型/数量"] = token
|
||||
return merged
|
||||
|
||||
|
||||
def project_sea_cargo_qty(facts: dict[str, str] | None) -> dict[str, str]:
|
||||
"""
|
||||
海运货物数量投影。
|
||||
@@ -655,6 +738,8 @@ def shape_facts_for_chosen_mode(facts: dict[str, str] | None, business_line: str
|
||||
merged.pop(key, None)
|
||||
return merged
|
||||
if line == "LAND":
|
||||
# 先把误入箱型箱量的车型改写过来,再删海运外键。
|
||||
merged = promote_land_vehicle_from_container(merged)
|
||||
for key in _LAND_DROP_FOREIGN:
|
||||
merged.pop(key, None)
|
||||
return merged
|
||||
|
||||
Reference in New Issue
Block a user