确定运输方式前的多句询价带到所选方式并按该方式抽字段;成交已定后放舱不再推跟进,未成交原因不再被当成新询价。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -516,6 +516,36 @@ def harvest_oral_collab(text: str) -> dict[str, str]:
|
||||
return out
|
||||
|
||||
|
||||
# 原话里单独出现才收下。举例行「(如:DDP、DAP、CIF、CIP)」整行跳过,避免示例被当成已填。
|
||||
_TRADE_TERM_CODES = ("DDP", "DAP", "DDU", "CIF", "CIP", "CPT", "CFR", "FOB", "FCA", "EXW")
|
||||
_TRADE_TERM_RE = re.compile(
|
||||
r"(?i)(?<![A-Za-z])(" + "|".join(_TRADE_TERM_CODES) + r")(?![A-Za-z])"
|
||||
)
|
||||
_TRADE_TERM_EXAMPLE_LINE = re.compile(r"^\s*[((]如\s*[::]")
|
||||
|
||||
|
||||
def harvest_spoken_trade_term(text: str) -> dict[str, str]:
|
||||
"""
|
||||
原话里单独写出的 DAP、DDP、CIF 等记成贸易条款。
|
||||
|
||||
只认这一组术语本身,不从「门到门」猜。同一句出现两个不同术语时不选。
|
||||
补问举例行整行不看。已有「贸易条款」标签值的,由调用方决定不覆盖。
|
||||
"""
|
||||
kept: list[str] = []
|
||||
for line in (text or "").splitlines() or [""]:
|
||||
if _TRADE_TERM_EXAMPLE_LINE.match((line or "").strip()):
|
||||
continue
|
||||
kept.append(line)
|
||||
found: list[str] = []
|
||||
for match in _TRADE_TERM_RE.finditer("\n".join(kept)):
|
||||
code = match.group(1).upper()
|
||||
if code not in found:
|
||||
found.append(code)
|
||||
if len(found) != 1:
|
||||
return {}
|
||||
return {"贸易条款": found[0]}
|
||||
|
||||
|
||||
# 箱型原文:40HQ*1、1x40HQ、40尺高柜。货物数量不能收这种串。
|
||||
_CONTAINER_SPEC = re.compile(
|
||||
r"(?i)(20|40|45)\s*['\"′尺]?\s*(HQ|GP|NOR|RH|HC|高柜|平柜|柜)"
|
||||
@@ -549,6 +579,39 @@ def project_sea_cargo_qty(facts: dict[str, str] | None) -> dict[str, str]:
|
||||
return merged
|
||||
|
||||
|
||||
# 选定方式后,不属于该方式首次询价的键拿掉。海运件数先投影成货量再删。
|
||||
_SEA_DROP_AFTER_PROJECT = ("件数", "毛重", "体积", "包装方式", "包装类型")
|
||||
_LAND_DROP_FOREIGN = ("件数", "货量", "货物数量", "整柜或拼柜", "箱型箱量", "贸易条款")
|
||||
_AIR_DROP_FOREIGN = ("货量", "货物数量", "整柜或拼柜", "箱型箱量")
|
||||
|
||||
|
||||
def shape_facts_for_chosen_mode(facts: dict[str, str] | None, business_line: str) -> dict[str, str]:
|
||||
"""
|
||||
运输方式刚定下来时,按该方式的询价字段收口。
|
||||
|
||||
海运留下货物数量、整柜或拼柜、箱型箱量、贸易条款、运输分类;件数里的数量先写入货量。
|
||||
陆运留下运输类型、线路类别、运输分类和重量体积或车型,不留海运箱型、空运件数。
|
||||
空运留下件数、毛重、体积、包装方式,不留海运箱型。
|
||||
不猜新值,只删不属于本方式的键。返回新 dict。
|
||||
"""
|
||||
line = (business_line or "").strip().upper()
|
||||
merged = {str(k): str(v).strip() for k, v in dict(facts or {}).items() if str(v).strip()}
|
||||
if line == "SEA":
|
||||
merged = project_sea_cargo_qty(merged)
|
||||
for key in _SEA_DROP_AFTER_PROJECT:
|
||||
merged.pop(key, None)
|
||||
return merged
|
||||
if line == "LAND":
|
||||
for key in _LAND_DROP_FOREIGN:
|
||||
merged.pop(key, None)
|
||||
return merged
|
||||
if line == "AIR":
|
||||
for key in _AIR_DROP_FOREIGN:
|
||||
merged.pop(key, None)
|
||||
return merged
|
||||
return merged
|
||||
|
||||
|
||||
def apply_quote_date_default(facts: dict[str, str]) -> dict[str, str]:
|
||||
"""销售未提供报价日期时填当天;已有则不覆盖。"""
|
||||
merged = dict(facts)
|
||||
|
||||
@@ -144,6 +144,33 @@ def harvest_sea_trade_terms(text: str, facts: dict[str, str] | None = None) -> d
|
||||
return merged
|
||||
|
||||
|
||||
# 补问举例让销售直接回这两个词。整句只有其中一个才记成贸易条款,长句里的港到港仍给运输分类。
|
||||
_SHORT_SEA_TRADE_REPLY = re.compile(
|
||||
r"^\s*(门到门|港到港|door\s*to\s*door|door-to-door|d2d)\s*[.。!!??]?\s*$",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def short_sea_trade_reply(text: str) -> str:
|
||||
"""
|
||||
销售整句只回门到门或港到港时,收成贸易条款。
|
||||
|
||||
举例行不算。长句里没写这两个词时,不能从这里编出一个条款。
|
||||
返回「门到门」或「港到港」;对不上返回空串。
|
||||
"""
|
||||
cleaned = _without_clarify_examples(text or "").strip()
|
||||
hit = _SHORT_SEA_TRADE_REPLY.match(cleaned)
|
||||
if not hit:
|
||||
return ""
|
||||
raw = (hit.group(1) or "").strip()
|
||||
folded = re.sub(r"[\s-]+", "", raw).lower()
|
||||
if raw == "港到港":
|
||||
return "港到港"
|
||||
if raw == "门到门" or folded in {"doortodoor", "d2d"}:
|
||||
return "门到门"
|
||||
return ""
|
||||
|
||||
|
||||
def drop_invented_door_trade_terms(text: str, facts: dict[str, str] | None = None) -> dict[str, str]:
|
||||
"""
|
||||
丢掉冒充贸易条款的「门到门」。
|
||||
@@ -169,6 +196,20 @@ def drop_invented_door_trade_terms(text: str, facts: dict[str, str] | None = Non
|
||||
return merged
|
||||
|
||||
|
||||
def apply_spoken_sea_trade_reply(text: str, facts: dict[str, str] | None = None) -> dict[str, str]:
|
||||
"""
|
||||
先丢掉模型冒充的门到门,再收下销售整句回复的门到门或港到港。
|
||||
|
||||
测服 2026-10-03:补问举例是门到门、港到港,销售回「门到门」仍被当成没填,并提示参数提取不全。
|
||||
原话没出现这两个词时,旧草稿里的门到门继续丢掉。返回新 dict。
|
||||
"""
|
||||
merged = drop_invented_door_trade_terms(text, facts)
|
||||
spoken = short_sea_trade_reply(text)
|
||||
if spoken:
|
||||
merged["贸易条款"] = spoken
|
||||
return merged
|
||||
|
||||
|
||||
def sea_class_prompt() -> str:
|
||||
"""缺运输分类时只列两项,不再追问「其他是什么」。"""
|
||||
return "运输分类(必填):请回复 Port to Port 或 其他。"
|
||||
|
||||
Reference in New Issue
Block a user