Files
inquiry_robot/inquiry-agent/agent/routing/__init__.py
T

169 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
意图路由:DeepSeek A → 唯一 RouteDecision。
禁止:在本包产生业务副作用(建单、发消息、改六态)。
正式路径:DeepSeekARouter(strict tool);route_stub 仅兼容旧自检名。
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
from typing import Optional
from agent.llm import LlmGateway, LlmMode
from agent.routing.decision import RouteDecision
from agent.routing.deepseek_a import DeepSeekARouter
from agent.routing.dispatch import HANDLER_REGISTRY, dispatch_inbound, resolve_handler
from agent.routing.ordered_actions import (
ORDERED_ACTIONS_TOOL_NAME,
OrderedActionsError,
OrderedActionsResult,
ordered_actions_tool_definition,
parse_ordered_actions_arguments,
)
# IntentRouter 定义见下
logger = logging.getLogger(__name__)
@dataclass
class IntentRouter:
"""
路由门面。
- route():优先 DeepSeek A(默认 stub,不开费)
- enqueue_ordered_actions():快回路径入 LLM Stream
"""
llm: Optional[LlmGateway] = None
deepseek_a: Optional[DeepSeekARouter] = None
allow_network: bool = False
def _a(self) -> DeepSeekARouter:
if self.deepseek_a is not None:
return self.deepseek_a
return DeepSeekARouter.from_settings(allow_network=self.allow_network)
def route_stub(self, *, text: str, sender_id: str) -> RouteDecision:
"""
兼容旧名:等价于无网络的 A stub fixture。
正式生产请用 route() 并在 Worker 开 allow_network + 数据确认。
"""
return self._a().route_stub_fixture(text=text, sender_id=sender_id)
def route(
self,
*,
text: str,
sender_id: str,
session_quoted: bool = False,
fee_names: Optional[list[str]] = None,
work_order_no: str = "",
) -> RouteDecision:
"""一条入站一个 RouteDecision。已报价时把书签事实交给 A 认调价。"""
return self._a().route(
text=text,
sender_id=sender_id,
session_quoted=session_quoted,
fee_names=fee_names,
work_order_no=work_order_no,
)
def route_inbound(
self,
*,
text: str,
sender_id: str,
event: str = "",
) -> RouteDecision:
"""
入站后再路由。
点卡片是结构化事件,不进大模型。
自由文字交给 DeepSeek A 认意图(续办 / 新询价 / 调价 / 闲聊),不要求固定口令。
"""
if not sender_id:
return RouteDecision(intent="reject_no_sender", confidence=1.0)
kind = (event or "").strip().lower()
if kind in {"template_card_event", "click", "templatecardevent"}:
return RouteDecision(intent="card_action", confidence=1.0)
from agent.handlers.text_inquiry import is_land_confirm_action
from agent.channel.wecom.models import InboundMessage
from agent.schema.land_options import looks_like_confirm
if looks_like_confirm(text or ""):
if is_land_confirm_action(
InboundMessage(
sender_id=sender_id,
content=text or "",
message_id="route-land-confirm",
)
):
logger.info("route 陆运核对卡确定:跳过闲聊模型 sender=%s", sender_id)
return RouteDecision(intent="ordinary_text_inquiry", confidence=1.0)
logger.info("route 确定类短句但无陆运核对卡,仍走模型 sender=%s", sender_id)
quoted = False
names: list[str] = []
wo = ""
try:
from agent.policy.quote_adjust_ops import quoted_adjust_flow
from agent.schema.quote_adjust import quote_fee_rows
flow = quoted_adjust_flow(sender_id)
sess = flow.session_of(sender_id) if flow is not None else None
if sess is not None:
quoted = True
wo = str(getattr(sess, "work_order_no", "") or "")
names = [str(r.get("name") or "") for r in quote_fee_rows(getattr(sess, "quote", None))]
except Exception:
logger.exception("route 收集调价书签失败 sender=%s", sender_id)
return self.route(
text=text,
sender_id=sender_id,
session_quoted=quoted,
fee_names=names,
work_order_no=wo,
)
def enqueue_ordered_actions(
self,
*,
text: str,
sender_id: str,
idempotency_key: str,
) -> str:
"""
将 A 路由任务入 LLM 队列(快回路径用)。
返回 stream entry id;无 LlmGateway 时抛错。
"""
if self.llm is None:
raise RuntimeError("IntentRouter 未注入 LlmGateway")
result = self.llm.enqueue(
mode=LlmMode.ORDERED_ACTIONS,
payload={"text": text, "sender_id": sender_id},
idempotency_key=idempotency_key,
)
if not result.accepted:
raise RuntimeError(result.reason or "enqueue_rejected")
return result.entry_id
__all__ = [
"RouteDecision",
"IntentRouter",
"DeepSeekARouter",
"ORDERED_ACTIONS_TOOL_NAME",
"OrderedActionsError",
"OrderedActionsResult",
"ordered_actions_tool_definition",
"parse_ordered_actions_arguments",
"HANDLER_REGISTRY",
"dispatch_inbound",
"resolve_handler",
]