空运协同里手工改费用、有起运地、锁舱失败或缺参会被误判,报价单已出后再激活又重发模板。按销售侧口径收紧这些分支,并让价格卡和未成交回执带上真实工单号。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -26,6 +26,8 @@ from agent.routing.group_intent import (
|
||||
_hint_confirm_tms,
|
||||
_hint_deal_outcome,
|
||||
_hint_tms_choice,
|
||||
_prefer_staff_quote,
|
||||
text_is_staff_fee_quote,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -95,6 +97,11 @@ def classify_air_group_text(
|
||||
locked = _hint_lock_or_release(raw)
|
||||
if locked:
|
||||
return locked
|
||||
# 补舱位不限写法。凑齐舱位编号、航司、航班号就当选舱,不要先当成报价。
|
||||
from agent.policy.air_group_ops import text_has_lock_supplement
|
||||
|
||||
if text_has_lock_supplement(raw):
|
||||
return INTENT_PICK_OPTION
|
||||
# 本单已有 TMS 报价时,航线回「2」是选不用 TMS,不是把价格改成 2。
|
||||
exact = _exact_tms_choice(raw)
|
||||
if exact:
|
||||
@@ -118,12 +125,15 @@ def classify_air_group_text(
|
||||
if use_net and getattr(settings, "llm_data_usage_confirmed", False):
|
||||
got = _classify_with_model(raw, settings)
|
||||
if got in _VALID:
|
||||
return got
|
||||
return _prefer_staff_quote(got, raw)
|
||||
except Exception:
|
||||
logger.exception("空运群意图模型失败,回退规则")
|
||||
from agent.llm.extract_text import extract_air_collab_fields, extract_product_quote
|
||||
from agent.schema.quote_adjust import extract_adjust
|
||||
|
||||
# 费用金额先当航线报价。「空运费改为900」不是销售调价。
|
||||
if text_is_staff_fee_quote(raw):
|
||||
return INTENT_QUOTE
|
||||
if extract_adjust(raw).is_adjust:
|
||||
return INTENT_ADJUST
|
||||
if extract_product_quote(raw):
|
||||
@@ -176,10 +186,10 @@ def _classify_with_model(text: str, settings) -> str:
|
||||
"你是空运协同群意图分类。只回复一个英文词,不要解释。"
|
||||
"air_lock=航线要锁舱;air_release=航线要释放舱位;"
|
||||
"air_activate=只是指定/切换工单号;"
|
||||
"air_pick_option=在选舱位选项编号;"
|
||||
"air_pick_option=在补充或选择舱位编号、航司、航班号,写法不限;"
|
||||
"collab_fields=在补是否含电/是否含磁;"
|
||||
"quote_adjust=销售在调已有费用,口令不写死(上升、往上抬、加一成、改到某金额都算),不是新产品报价;"
|
||||
"product_quote=航线在报价格或费用(含具体金额);"
|
||||
"quote_adjust=销售在按比例改已有费用(上调、上浮、加10%、减5%、加一成),或只改总价/总销售价;"
|
||||
"product_quote=航线在报某一项费用的金额。写出「空运费9900」「空运费改为900USD」是报价,不是调价;"
|
||||
"quote_confirm_tms=确认沿用当前TMS报价;"
|
||||
"quote_keep_tms=要在已有TMS报价上改;"
|
||||
"quote_drop_tms=不要TMS、只用刚报的价;"
|
||||
@@ -188,7 +198,7 @@ def _classify_with_model(text: str, settings) -> str:
|
||||
"other=闲聊。"
|
||||
"一句话里既有锁舱又有工单号,回 air_lock。"
|
||||
"一句话里既有工单号又确认成交/未成交/协商中,回 deal_won/deal_lost/deal_negotiate,不要回 air_activate。"
|
||||
"改已有费用优先 quote_adjust;新报价格才是 product_quote。"
|
||||
"有具体费用金额就是 product_quote。只有比例或只改总价才是 quote_adjust。"
|
||||
)
|
||||
data = client.chat(
|
||||
mode=LlmMode.ORDERED_ACTIONS,
|
||||
|
||||
@@ -117,6 +117,63 @@ _DROP_HINTS = (
|
||||
)
|
||||
|
||||
|
||||
def text_is_staff_fee_quote(text: str) -> bool:
|
||||
"""
|
||||
航线/产品在报某一项费用的金额。
|
||||
|
||||
「空运费 9900」「空运费改为900USD」「报关费改为300CNY」都算手工报价。
|
||||
比例调价和只改总价/总销售价不算,那两句仍留给销售调价。
|
||||
句里没有费用名、只剩一个数字,也不算,避免把选项编号当成报价。
|
||||
不打模型。分类和入站改判都会用。
|
||||
"""
|
||||
raw = (text or "").strip()
|
||||
if not raw:
|
||||
return False
|
||||
from agent.schema.quote_adjust import text_has_relative_adjust, text_has_total_only_adjust
|
||||
|
||||
if text_has_relative_adjust(raw) or text_has_total_only_adjust(raw):
|
||||
return False
|
||||
from agent.llm.extract_text import extract_product_quote
|
||||
|
||||
quote = extract_product_quote(raw)
|
||||
rows = [row for row in (quote.get("fee_rows") or []) if isinstance(row, dict)]
|
||||
if not rows:
|
||||
return False
|
||||
# 「费用合计」里的「费」不能单独把改总价认成报价;上面已经排除只改总价。
|
||||
if "费" in raw:
|
||||
return True
|
||||
names = [str(row.get("name") or "").strip() for row in rows]
|
||||
return any(name and name != "海运费" for name in names)
|
||||
|
||||
|
||||
def resolve_price_intent(intent: str, text: str, *, sales: bool) -> str:
|
||||
"""
|
||||
按说话人把「报价」和「调价」分开。
|
||||
|
||||
航线/产品写出费用金额:按报价办,不能回「只有销售能调价」。
|
||||
销售在做比例、改总价,或点名把某项费用改到某个金额:仍按调价办。
|
||||
不打模型。群入站分发前调用,避免手工报价被调价话术拦住。
|
||||
"""
|
||||
if intent not in {INTENT_ADJUST, INTENT_QUOTE}:
|
||||
return intent
|
||||
from agent.schema.quote_adjust import extract_adjust
|
||||
|
||||
# 这里只用本地规则。正式调价抽取仍在调价处理里,必要时再问模型。
|
||||
if sales and extract_adjust(text, allow_model=False).is_adjust:
|
||||
return INTENT_ADJUST
|
||||
if text_is_staff_fee_quote(text):
|
||||
return INTENT_QUOTE
|
||||
return intent
|
||||
|
||||
|
||||
def _prefer_staff_quote(intent: str, text: str) -> str:
|
||||
"""模型或本地规则把费用金额判成调价时,改回报价。比例和改总价不动。"""
|
||||
if intent == INTENT_ADJUST and text_is_staff_fee_quote(text):
|
||||
logger.info("判成调价,但这是费用报价,改回报价 text=%s", (text or "")[:40])
|
||||
return INTENT_QUOTE
|
||||
return intent
|
||||
|
||||
|
||||
def classify_group_text(
|
||||
text: str,
|
||||
*,
|
||||
@@ -163,12 +220,15 @@ def classify_group_text(
|
||||
if hinted_deal and got in {INTENT_DISSOLVE, INTENT_OTHER, INTENT_FIELDS}:
|
||||
logger.warning("群意图模型把成交判成%s,改回 %s text=%s", got, hinted_deal, raw[:40])
|
||||
return hinted_deal
|
||||
return got
|
||||
return _prefer_staff_quote(got, raw)
|
||||
except Exception:
|
||||
logger.exception("群意图模型失败,回退标签字段")
|
||||
from agent.llm.extract_text import extract_product_quote
|
||||
from agent.schema.quote_adjust import extract_adjust
|
||||
|
||||
# 费用金额先当报价。「改为900」是航线/产品常用写法,不能先被调价规则抢走。
|
||||
if text_is_staff_fee_quote(raw):
|
||||
return INTENT_QUOTE
|
||||
if extract_adjust(raw).is_adjust:
|
||||
return INTENT_ADJUST
|
||||
if extract_product_quote(raw):
|
||||
@@ -249,8 +309,8 @@ def _classify_with_model(text: str, settings) -> str:
|
||||
system = (
|
||||
"你是海运协同群意图分类。只回复一个英文词,不要解释。"
|
||||
"collab_fields=在补货好时间/商品海关编码/含油含电含磁;"
|
||||
"quote_adjust=销售在调已有费用,口令不写死(上升、上浮、往上抬、加一成、减、改到某金额都算),不是新产品报价;"
|
||||
"product_quote=产品在报价格或费用(含具体金额);"
|
||||
"quote_adjust=销售在按比例改已有费用(上调、上浮、加10%、减5%、加一成),或只改总价/总销售价;"
|
||||
"product_quote=产品在报某一项费用的金额。写出「运费1000」「报关费改为300CNY」是报价,不是调价;"
|
||||
"quote_confirm_tms=产品确认沿用当前TMS报价,没有报新金额;"
|
||||
"quote_keep_tms=要在已有TMS报价上改,没有报新金额;"
|
||||
"quote_drop_tms=不要TMS、只用产品刚报的价,没有报新金额;"
|
||||
@@ -260,7 +320,8 @@ def _classify_with_model(text: str, settings) -> str:
|
||||
"dissolve=要把这个协同群解散;"
|
||||
"other=闲聊、问候、对询价港口的随口更正(不当改单)。"
|
||||
"一句话里既补字段又报价,回 product_quote。"
|
||||
"有金额就是 product_quote,不要判成 quote_keep_tms。"
|
||||
"有具体费用金额就是 product_quote,不要判成 quote_adjust 或 quote_keep_tms。"
|
||||
"只有比例或只改总价才是 quote_adjust。"
|
||||
)
|
||||
data = client.chat(
|
||||
mode=LlmMode.ORDERED_ACTIONS,
|
||||
|
||||
Reference in New Issue
Block a user