落地陆运私聊文字询价:选项核对后再建单查价,并按线路拉产品进群。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-17 16:23:30 +08:00
co-authored by Cursor
parent 83b907c4b8
commit be95d823a2
52 changed files with 4534 additions and 76 deletions
+39 -7
View File
@@ -132,8 +132,8 @@ def pick_quote_template(
quote: Optional[dict[str, Any]] = None,
) -> Optional[TemplatePick]:
"""
同业务线:先关键词命中,再默认模板。
同业务线:先按关键词打分(运输类型上的词优先),再退回默认模板。
「陆运/国内」不当关键词,避免中港模板抢走拼车单。
参数 templates:主账 listEnabled 行或 TemplateEntry。
返回 None 时销售侧用 FAIL_MESSAGE。
"""
@@ -141,7 +141,9 @@ def pick_quote_template(
haystack = build_inquiry_haystack(
business_line=business_line, facts=facts, quote=quote
).lower()
type_val = str((facts or {}).get("运输类型") or "").strip().lower()
keyword_hit: Optional[dict[str, Any]] = None
best: tuple[int, int] = (-1, -1)
default_hit: Optional[dict[str, Any]] = None
for item in templates or []:
row = _row_of(item)
@@ -150,11 +152,12 @@ def pick_quote_template(
continue
if default_hit is None and _is_default(row.get("isDefault")):
default_hit = row
if keyword_hit is None:
for kw in _as_keywords(row.get("keywords")):
if kw.lower() in haystack:
keyword_hit = row
break
score = _keyword_score(row.get("keywords"), haystack, type_val)
if score == (0, 0):
continue
if score > best:
best = score
keyword_hit = row
hit = keyword_hit or default_hit
if hit is None:
return None
@@ -165,3 +168,32 @@ def pick_quote_template(
match_type=match_type,
raw=hit,
)
_GENERIC_KEYWORDS = frozenset(
{"海运", "空运", "陆运", "sea", "air", "land", "国内"}
)
def _keyword_score(raw: Any, haystack: str, type_val: str) -> tuple[int, int]:
"""
运输类型上的命中优先,其次全文词长。
「陆运/国内」跳过,避免中港、国内整车抢走拼车单。
"""
type_hits = 0
length = 0
any_hit = False
for kw in _as_keywords(raw):
k = kw.lower()
if k in _GENERIC_KEYWORDS:
continue
if type_val and k in type_val:
type_hits += 1
length += len(k)
any_hit = True
elif k in haystack:
length += len(k)
any_hit = True
if not any_hit:
return (0, 0)
return (type_hits, length)