Files
tw2/qingjian_cs/app/orchestrator.py
jimi 4e5557bcc3
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
perf: fast-route orchestration and short-reply guard for qingjian
2026-03-02 19:12:32 +08:00

115 lines
4.5 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.
from __future__ import annotations
from typing import Any
from .agents import AfterSalesAgent, PreSalesAgent, QuoteAgent, RiskAgent, RouterAgent
from .config import FAST_ROUTE_ENABLED
from .models import Decision
from .rules import (
detect_intent,
detect_order_status,
has_map_or_political_risk,
has_porn_risk,
requests_external_contact,
)
from .state_machine import evolve_after_sales_state, migrate_state_schema
from .store import ConversationStore
class Orchestrator:
def __init__(self) -> None:
self.router = RouterAgent()
self.pre_sales = PreSalesAgent()
self.quote = QuoteAgent()
self.after_sales = AfterSalesAgent()
self.risk = RiskAgent()
self.store = ConversationStore()
async def decide(self, context: dict[str, Any]) -> tuple[str, Decision, dict[str, Any]]:
customer_key = context["customer_key"]
session = self.store.get_session(customer_key)
prev_state = migrate_state_schema(session.get("state", {}))
prev_route = session.get("route", "pre_sales")
intent = detect_intent(str(context.get("msg", "") or ""))
order_status = detect_order_status(str(context.get("goods_order", "") or ""))
merged_ctx = {
**context,
"session_state": prev_state,
"previous_route": prev_route,
"intent": intent,
"order_status": order_status,
}
msg = str(context.get("msg", "") or "")
goods_name = str(context.get("goods_name", "") or "")
risk_hit = has_map_or_political_risk(msg, goods_name) or has_porn_risk(msg) or requests_external_contact(msg)
# 命中硬风控才调用 RiskAgent避免每条消息都先走一轮模型。
if risk_hit:
risk_decision = await self.risk.decide(merged_ctx)
route = "risk"
new_state = evolve_after_sales_state(
{**prev_state, **(risk_decision.state_patch or {})},
route=route,
action=risk_decision.action,
intent=intent,
order_status=order_status,
msg=str(context.get("msg", "") or ""),
)
self.store.upsert_session(customer_key, context.get("acc_id", ""), context.get("customer_id", ""), route, new_state)
self.store.append_event(customer_key, "decision", {"route": route, "action": risk_decision.action, "reason": risk_decision.reason})
return route, risk_decision, new_state
route = ""
route_reason = ""
if FAST_ROUTE_ENABLED:
pending_images = int(context.get("pending_images", 0) or 0)
auto_quote_trigger = bool(context.get("auto_quote_trigger", False))
if intent in {"pricing", "finish_or_quote_trigger"} and (pending_images > 0 or auto_quote_trigger):
route = "quote"
route_reason = "fast_route_quote_with_pending_images"
elif order_status == "refund":
route = "after_sales"
route_reason = "fast_route_refund"
elif intent in {"image", "greeting", "nonsense", "pricing", "finish_or_quote_trigger", "unknown"}:
route = "pre_sales"
route_reason = "fast_route_common_presales"
if not route:
route, route_reason = await self.router.route(merged_ctx)
if route == "quote":
decision = await self.quote.decide(merged_ctx)
elif route == "after_sales":
decision = await self.after_sales.decide(merged_ctx)
elif route == "risk":
decision = await self.risk.decide(merged_ctx)
else:
decision = await self.pre_sales.decide(merged_ctx)
merged_state = {**prev_state, **(decision.state_patch or {})}
new_state = evolve_after_sales_state(
merged_state,
route=route,
action=decision.action,
intent=intent,
order_status=order_status,
msg=str(context.get("msg", "") or ""),
)
self.store.upsert_session(customer_key, context.get("acc_id", ""), context.get("customer_id", ""), route, new_state)
self.store.append_event(
customer_key,
"decision",
{
"route": route,
"route_reason": route_reason,
"action": decision.action,
"reason": decision.reason,
"after_sales_stage": new_state.get("after_sales_stage", "new"),
},
)
return route, decision, new_state