落地私聊空运询价与TMS报价回填,按后台模板出单。

查到价后把币种、单位、数量、单价、备注写入报价单,避免销售只看到光秃秃的金额。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-14 20:25:35 +08:00
co-authored by Cursor
parent cea3a6e51e
commit 99da1faf30
78 changed files with 8838 additions and 321 deletions
+44 -13
View File
@@ -13,6 +13,7 @@ from typing import Callable, Optional
from agent.channel.wecom.models import InboundMessage
from agent.handlers import abnormal, card_action, close_deal, continue_thread, new_inquiry
from agent.handlers.echo_text import handle_echo_text
from agent.handlers.text_inquiry import handle_text_inquiry, handle_text_other
from agent.routing.decision import RouteDecision
logger = logging.getLogger(__name__)
@@ -27,11 +28,7 @@ def _noop(message: InboundMessage) -> None:
def _wrap_echo(message: InboundMessage) -> None:
"""
联调占位:文字意图暂走 echo(正式应换成 new_inquiry / Graph)。
框架接线用;不含字段抽取。
"""
"""联调残留:仅 echo_text 意图使用。"""
from agent.channel.queue import get_message_store
from agent.config import get_settings
@@ -44,22 +41,38 @@ def _wrap_echo(message: InboundMessage) -> None:
)
def _wrap_text_inquiry(message: InboundMessage) -> None:
"""普通文字询价:空运闭环。"""
handle_text_inquiry(message)
def _wrap_text_other(message: InboundMessage) -> None:
"""闲聊:回首次引导;若正等按钮则当按钮。"""
handle_text_other(message)
def _wrap_new(message: InboundMessage) -> None:
new_inquiry.handle_new_inquiry(message)
def _wrap_continue(message: InboundMessage) -> None:
continue_thread.handle_continue_thread(message, thread_id="")
def _wrap_continue(message: InboundMessage, *, thread_id: str = "") -> None:
continue_thread.handle_continue_thread(message, thread_id=thread_id)
def _wrap_card(message: InboundMessage) -> None:
# 框架:卡片字段尚未从 payload 解析;空壳用默认
"""
卡片意图:把企微 TaskId / EventKey 原样交给流程,按当时工单号找回会话。
action 用正文(EventKey 或按钮文案),禁止写死 unknown。
"""
from agent.policy import inquiry_copy as copy
action = copy.canonical_button(message.content or "") or (message.content or "").strip()
card_action.handle_card_action(
sender_id=message.sender_id,
thread_id="",
wait_version=0,
action="unknown",
payload={},
action=action or "unknown",
payload=dict(message.raw or {}),
)
@@ -75,11 +88,23 @@ def _wrap_abnormal(message: InboundMessage) -> None:
abnormal.handle_abnormal(sender_id=message.sender_id, inquiry_no="", reason="dispatch")
def _wrap_route_failed(message: InboundMessage) -> None:
"""模型没给出路由结论:请销售再说一次,不假装听懂。"""
from agent.channel.queue import get_message_store
from agent.policy import inquiry_copy as copy
get_message_store().enqueue_outbound(
touser=message.sender_id,
content=copy.ROUTE_MODEL_FAILED,
dedupe_key=f"route-fail:{message.message_id}",
)
# 意图 → handler(空壳或联调 echo);扩展时只改表,不改 ChannelRuntime 分支森林
HANDLER_REGISTRY: dict[str, HandlerFn] = {
"echo_text": _wrap_echo,
"ordinary_text_inquiry": _wrap_echo, # 联调:有回复;正式换业务 handler
"ordinary_text_other": _wrap_echo,
"ordinary_text_inquiry": _wrap_text_inquiry,
"ordinary_text_other": _wrap_text_other,
"attachment_inquiry": _wrap_new,
"image_inquiry": _wrap_new,
"multi_segment_transport": _wrap_new,
@@ -92,6 +117,8 @@ HANDLER_REGISTRY: dict[str, HandlerFn] = {
"reject_no_sender": _noop,
"ignore_empty": _noop,
"stub_needs_model": _wrap_echo,
"route_model_failed": _wrap_route_failed,
"route_parse_failed": _wrap_route_failed,
}
@@ -119,10 +146,14 @@ def dispatch_inbound(message: InboundMessage, decision: RouteDecision) -> str:
return "unknown_intent"
logger.info(
"dispatch intent=%s sender=%s msg=%s",
"dispatch intent=%s sender=%s msg=%s wo=%s",
decision.intent,
message.sender_id,
message.message_id,
decision.thread_id or "-",
)
if decision.intent == "continue_thread":
continue_thread.handle_continue_thread(message, thread_id=decision.thread_id)
return decision.intent
fn(message)
return decision.intent