from __future__ import annotations from typing import Any from .agents import AfterSalesAgent, PreSalesAgent, QuoteAgent, RiskAgent from .image_quote_analyzer import analyze_image_for_quote from .models import Decision from .state_machine import evolve_after_sales_state, migrate_state_schema from .store import ConversationStore class Orchestrator: def __init__(self) -> None: self.pre_sales = PreSalesAgent() self.quote = QuoteAgent() self.after_sales = AfterSalesAgent() self.risk = RiskAgent() self.store = ConversationStore() @staticmethod def _single_stage_route(context: dict[str, Any]) -> tuple[str, str]: msg = str(context.get("msg", "") or "") lower = msg.lower() if bool(context.get("auto_quote_trigger")): return "quote", "fast:auto_quote_trigger" current_urls = context.get("current_image_urls") or [] if isinstance(current_urls, list) and len(current_urls) > 0: return "quote", "fast:current_image" if int(context.get("pending_images", 0) or 0) > 0 and any(k in lower for k in ("报价", "多少钱", "价格", "找得到", "找图", "能做", "有没有")): return "quote", "fast:pending_image_and_pricing" if any(k in lower for k in ("退款", "退钱", "不满意", "重做", "售后", "返工", "重发")): return "after_sales", "fast:after_sales_keyword" return "pre_sales", "single_stage:default_pre_sales" 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 = "unknown" order_status = "unknown" merged_ctx = { **context, "session_state": prev_state, "previous_route": prev_route, "intent": intent, "order_status": order_status, } route, route_reason = self._single_stage_route(merged_ctx) if route == "quote": latest_image_url = str(context.get("latest_image_url", "") or "").strip() if latest_image_url: analysis = await analyze_image_for_quote( image_url=latest_image_url, customer_text=str(context.get("msg", "") or ""), goods_name=str(context.get("goods_name", "") or ""), ) merged_ctx["image_quote_analysis"] = analysis 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 {})} if route == "quote": analysis_obj = merged_ctx.get("image_quote_analysis") if isinstance(analysis_obj, dict) and analysis_obj: merged_state["last_image_quote_analysis"] = analysis_obj 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