空运协同里手工改费用、有起运地、锁舱失败或缺参会被误判,报价单已出后再激活又重发模板。按销售侧口径收紧这些分支,并让价格卡和未成交回执带上真实工单号。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,6 +22,8 @@ from agent.policy.air_group_ops import (
|
||||
apply_air_file,
|
||||
apply_air_lock,
|
||||
apply_air_quote,
|
||||
lock_params_from_text,
|
||||
text_has_lock_supplement,
|
||||
apply_air_release,
|
||||
apply_air_tms_choice,
|
||||
current_ticket,
|
||||
@@ -229,10 +231,18 @@ def handle_air_group(
|
||||
if is_handoff_ticket(ticket):
|
||||
logger.info("air_group 转人工不再跟 chat=%s", chat_id)
|
||||
return "handoff_ignore"
|
||||
from agent.policy.system_exception import reply_paused_ticket, ticket_is_paused
|
||||
from agent.policy.system_exception import (
|
||||
cabin_failure_pause,
|
||||
reply_paused_ticket,
|
||||
ticket_is_paused,
|
||||
)
|
||||
|
||||
# 锁舱失败留下的暂停只拦还要打 TMS 的操作。成交、未成交和补原因不调用 TMS。
|
||||
if ticket_is_paused(ticket) and not _deal_ignores_tms_pause(ticket, intent):
|
||||
# 查价失败的暂停仍要拦住。锁舱/释放失败已经告诉过原因,不能挡住后面的报价和成交。
|
||||
if (
|
||||
ticket_is_paused(ticket)
|
||||
and not cabin_failure_pause(ticket)
|
||||
and not _deal_ignores_tms_pause(ticket, intent)
|
||||
):
|
||||
reply_paused_ticket(
|
||||
ticket=ticket,
|
||||
reply=lambda text: send_group_text(group_client_of(engine), chat_id, text),
|
||||
@@ -310,6 +320,29 @@ def _handle_cabin(
|
||||
_ = injected_quote
|
||||
chat_id = message.chat_id
|
||||
if not work_order_no:
|
||||
# 本群已经在锁这张单时,补舱位不用再报工单号。
|
||||
bound = current_ticket(engine, chat_id)
|
||||
if bound is not None and is_air_ticket(bound):
|
||||
ticket = bound
|
||||
if intent == INTENT_RELEASE:
|
||||
return apply_air_release(
|
||||
flow=engine,
|
||||
ticket=ticket,
|
||||
sender_id=message.sender_id,
|
||||
chat_id=chat_id,
|
||||
instruction=message.content or "",
|
||||
)
|
||||
option_no, airline, flight_no = lock_params_from_text(message.content or "")
|
||||
return apply_air_lock(
|
||||
flow=engine,
|
||||
ticket=ticket,
|
||||
sender_id=message.sender_id,
|
||||
chat_id=chat_id,
|
||||
instruction=message.content or "",
|
||||
option_no=option_no,
|
||||
airline=airline,
|
||||
flight_no=flight_no,
|
||||
)
|
||||
send_group_text(group_client_of(engine), chat_id, copy.air_lock_need_work_order())
|
||||
return "cabin_need_work_order"
|
||||
ticket, phase = resolve_cabin_ticket(
|
||||
@@ -328,15 +361,17 @@ def _handle_cabin(
|
||||
chat_id=chat_id,
|
||||
instruction=message.content or "",
|
||||
)
|
||||
# 「WO…锁舱,锁AIR-OPT01 TH」要带上句中的舱位和航司,不能只靠报价里碰巧只有一条。
|
||||
# 「锁 AIR-OPT01 CZ 航班CZ327」要带上舱位、航司、航班号。缺了先补,不能直接锁。
|
||||
option_no, airline, flight_no = lock_params_from_text(message.content or "")
|
||||
return apply_air_lock(
|
||||
flow=engine,
|
||||
ticket=ticket,
|
||||
sender_id=message.sender_id,
|
||||
chat_id=chat_id,
|
||||
instruction=message.content or "",
|
||||
option_no=_option_from_text(message.content or ""),
|
||||
airline=_airline_from_text(message.content or ""),
|
||||
option_no=option_no,
|
||||
airline=airline,
|
||||
flight_no=flight_no,
|
||||
)
|
||||
|
||||
|
||||
@@ -373,19 +408,20 @@ def _handle_bound(
|
||||
if lost:
|
||||
return lost
|
||||
facts = dict(ticket_view(ticket).get("collab_facts") or {})
|
||||
if intent == INTENT_PICK_OPTION or (
|
||||
intent == INTENT_OTHER and str(facts.get("__pending_air_lock") or "") == "1"
|
||||
):
|
||||
option = _option_from_text(message.content or "")
|
||||
if option:
|
||||
pending_lock = str(facts.get("__pending_air_lock") or "") == "1"
|
||||
# 等补舱位时,报价/选线路等意图让路:话里带了舱位、航司或航班号就继续锁。
|
||||
if intent == INTENT_PICK_OPTION or pending_lock or text_has_lock_supplement(message.content or ""):
|
||||
option_no, airline, flight_no = lock_params_from_text(message.content or "")
|
||||
if option_no or airline or flight_no:
|
||||
return apply_air_lock(
|
||||
flow=engine,
|
||||
ticket=ticket,
|
||||
sender_id=message.sender_id,
|
||||
chat_id=chat_id,
|
||||
instruction=message.content or "",
|
||||
option_no=option,
|
||||
airline=_airline_from_text(message.content or ""),
|
||||
option_no=option_no,
|
||||
airline=airline,
|
||||
flight_no=flight_no,
|
||||
)
|
||||
if intent in {INTENT_DEAL, INTENT_LOST, INTENT_NEGOTIATE}:
|
||||
outcome = {
|
||||
@@ -421,6 +457,18 @@ def _handle_bound(
|
||||
sender_id=message.sender_id,
|
||||
chat_id=chat_id,
|
||||
)
|
||||
from agent.policy.sea_group_ops import is_sales
|
||||
from agent.routing.group_intent import resolve_price_intent
|
||||
|
||||
# 航线写出「空运费改为900」是报价。模型若判成调价,这里按说话人改回来。
|
||||
priced = resolve_price_intent(
|
||||
intent,
|
||||
message.content or "",
|
||||
sales=is_sales(ticket, message.sender_id),
|
||||
)
|
||||
if priced != intent:
|
||||
logger.info("air_group 报价/调价改判 %s -> %s chat=%s", intent, priced, chat_id)
|
||||
intent = priced
|
||||
if intent == INTENT_ADJUST:
|
||||
from agent.policy.quote_adjust_ops import apply_group_adjust
|
||||
|
||||
@@ -448,26 +496,3 @@ def _handle_bound(
|
||||
return "group_ignore"
|
||||
|
||||
|
||||
def _option_from_text(text: str) -> str:
|
||||
"""从「锁AIR-OPT01 TH」抽出舱位编号;不要把后面的航司码拼进 optionNo。"""
|
||||
import re
|
||||
|
||||
m = re.search(r"AIR-?OPT-?\d+", (text or "").upper())
|
||||
if not m:
|
||||
return ""
|
||||
raw = m.group(0)
|
||||
# 统一成 TMS 常见写法 AIR-OPT01(中间可保留已有的第二根横杠)
|
||||
if raw.startswith("AIROPT"):
|
||||
return "AIR-OPT" + raw[6:]
|
||||
return raw
|
||||
|
||||
|
||||
def _airline_from_text(text: str) -> str:
|
||||
"""从「AIR-OPT01 TH」抽出两位航司码;没有就不猜。"""
|
||||
import re
|
||||
|
||||
blob = (text or "").upper()
|
||||
m = re.search(r"AIR-?OPT-?\d+\s*([A-Z]{2})(?![A-Z0-9])", blob)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return ""
|
||||
|
||||
@@ -126,9 +126,14 @@ def handle_group_inbound(
|
||||
logger.info("group_inbound 忽略非成员 sender=%s chat=%s", message.sender_id, chat_id)
|
||||
return "group_ignore"
|
||||
|
||||
from agent.policy.system_exception import reply_paused_ticket, ticket_is_paused
|
||||
from agent.policy.system_exception import (
|
||||
cabin_failure_pause,
|
||||
reply_paused_ticket,
|
||||
ticket_is_paused,
|
||||
)
|
||||
|
||||
if ticket_is_paused(ticket):
|
||||
# 锁舱失败不挡海运/陆运群里的后续报价。查价失败的暂停仍要停。
|
||||
if ticket_is_paused(ticket) and not cabin_failure_pause(ticket):
|
||||
reply_paused_ticket(
|
||||
ticket=ticket,
|
||||
reply=lambda text: send_group_text(group_client_of(engine), chat_id, text),
|
||||
@@ -238,6 +243,18 @@ def handle_group_inbound(
|
||||
sender_id=message.sender_id,
|
||||
sess=sess,
|
||||
)
|
||||
from agent.policy.sea_group_ops import is_sales
|
||||
from agent.routing.group_intent import resolve_price_intent
|
||||
|
||||
# 产品写出「报关费改为300」是报价。不是这张单的销售时,不能回调价话术。
|
||||
priced = resolve_price_intent(
|
||||
intent,
|
||||
message.content or "",
|
||||
sales=is_sales(ticket, message.sender_id),
|
||||
)
|
||||
if priced != intent:
|
||||
logger.info("group_inbound 报价/调价改判 %s -> %s chat=%s", intent, priced, chat_id)
|
||||
intent = priced
|
||||
if intent == INTENT_ADJUST:
|
||||
from agent.policy.quote_adjust_ops import apply_group_adjust
|
||||
|
||||
|
||||
@@ -404,6 +404,7 @@ def handle_text_inquiry(
|
||||
return info if ok else ""
|
||||
|
||||
from agent.policy.system_exception import (
|
||||
cabin_failure_pause,
|
||||
extract_work_order_nos,
|
||||
reply_paused_ticket,
|
||||
ticket_is_paused,
|
||||
@@ -420,7 +421,7 @@ def handle_text_inquiry(
|
||||
getter = getattr(ledger, "get_for_agent", None)
|
||||
if callable(getter):
|
||||
ticket = getter(work_order_no=no, sender_id=message.sender_id)
|
||||
if ticket_is_paused(ticket):
|
||||
if ticket_is_paused(ticket) and not cabin_failure_pause(ticket):
|
||||
reply_paused_ticket(ticket=ticket, reply=reply)
|
||||
logger.info("text_inquiry 暂停工单只回兜底 wo=%s sender=%s", no, message.sender_id)
|
||||
return "system_exception_paused"
|
||||
|
||||
Reference in New Issue
Block a user