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
99 lines
4.0 KiB
Python
99 lines
4.0 KiB
Python
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
|