""" 意图:普通文字询价(空运闭环)。 本文件职责:入站文字 → 流程编排;回复只写入 outbox。 禁止:在本文件抽字段语义(交给 extract);禁止直连 TMS;禁止回调线程外的额外阻塞。 """ from __future__ import annotations import logging from typing import Any, Optional from agent.channel.queue import get_message_store from agent.channel.wecom.models import InboundMessage from agent.config import get_settings from agent.policy.air_text_flow import get_air_text_flow logger = logging.getLogger(__name__) def handle_text_inquiry( message: InboundMessage, *, injected_facts: Optional[dict[str, Any]] = None, injected_mode: str = "", b_result: Optional[dict[str, Any]] = None, chat_fn=None, flow=None, store=None, ) -> str: """ 处理一条私聊文字询价。 须已有 sender_id。副作用:outbox 多条回复;主账经 flow.ledger。 返回:流程阶段名。 """ if not message.has_sender(): logger.warning("text_inquiry 拒绝:无 sender_id") return "reject_no_sender" settings = get_settings() msg_store = store or get_message_store() engine = flow or get_air_text_flow() replies: list[str] = [] def reply(text: str, extra: dict | None = None) -> str: # 销售询价正文不加 [test];[test] 只给运维告警 body = text replies.append(body) ok, info = msg_store.enqueue_outbound( touser=message.sender_id, content=body, dedupe_key=f"inq:{message.message_id}:{len(replies)}", payload=dict(extra or {}), ) logger.info("text_inquiry 出站 ok=%s info=%s phase_reply=%s", ok, info, body[:80]) return info if ok else "" phase = engine.on_text( sender_id=message.sender_id, text=message.content or "", reply=reply, injected_facts=injected_facts, injected_mode=injected_mode, b_result=b_result, allow_b=bool(settings.llm_allow_network and settings.llm_data_usage_confirmed), chat_fn=chat_fn, inbound_raw=message.raw, ) logger.info("text_inquiry phase=%s sender=%s", phase, message.sender_id) return phase def handle_text_other(message: InboundMessage, *, store=None) -> str: """ 闲聊/其它:若有待点按钮则当按钮;否则回首次单聊话术。 """ from agent.policy import inquiry_copy as copy from agent.policy.air_text_flow import get_air_text_flow if not message.has_sender(): return "reject_no_sender" msg_store = store or get_message_store() engine = get_air_text_flow() sess = engine.session_for_action( message.sender_id, message.raw, text=message.content or "" ) btn = engine.match_button(message.content or "", sess) if btn: return handle_text_inquiry(message, store=msg_store) body = copy.FIRST_CHAT msg_store.enqueue_outbound( touser=message.sender_id, content=body, dedupe_key=f"other:{message.message_id}", ) return "text_other"