feat: 可配置方案引擎通用原型(Mock 检索)
需求未定稿时可先跑通追问/判定/出方案;槽位与判定树外置 YAML,后续可接 Ragflow。 Co-authored-by: haijd <haijd@163.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .config_loader import business_config
|
||||
from .decision import evaluate_tree
|
||||
from .retrieval import retrieve_for_scheme
|
||||
from .session_store import (
|
||||
Session,
|
||||
looks_like_handoff,
|
||||
naive_fill_slots,
|
||||
next_slot_prompt,
|
||||
store,
|
||||
)
|
||||
|
||||
|
||||
def create_session() -> Session:
|
||||
return store.create()
|
||||
|
||||
|
||||
def handle_message(session_id: str, message: str) -> dict[str, Any]:
|
||||
session = store.get(session_id)
|
||||
if not session:
|
||||
raise KeyError("session_not_found")
|
||||
|
||||
text = (message or "").strip()
|
||||
session.messages.append({"role": "user", "content": text})
|
||||
|
||||
if looks_like_handoff(text, business_config.handoff_phrases):
|
||||
session.phase = "handoff"
|
||||
reply = "已为您标记转人工。请留下称呼与联系方式,销售同事会跟进。(原型:仅记录意图,未接 Chatwoot)"
|
||||
return _finish(session, reply, need_handoff=True)
|
||||
|
||||
# fill slots while collecting
|
||||
if session.phase in ("collecting", "scheme_ready"):
|
||||
session.slots = naive_fill_slots(text, business_config.slots, session.slots)
|
||||
|
||||
nxt = next_slot_prompt(business_config.slots, session.slots)
|
||||
if nxt and session.phase == "collecting":
|
||||
# first turn with empty slots: greet + first question
|
||||
if len(session.messages) == 1 and not any(session.slots.values()):
|
||||
# put first user utterance into first slot if they already answered something
|
||||
session.slots = naive_fill_slots(text, business_config.slots, session.slots)
|
||||
nxt = next_slot_prompt(business_config.slots, session.slots)
|
||||
if nxt:
|
||||
reply = nxt["prompt"]
|
||||
return _finish(session, reply, asking_slot=nxt["id"])
|
||||
|
||||
# all required (+ optional asked or filled) → scheme
|
||||
decision = evaluate_tree(business_config.rules, session.slots)
|
||||
confidence = float(decision.get("confidence") or 0)
|
||||
need_handoff = bool(decision.get("handoff")) or confidence < business_config.handoff_confidence_below
|
||||
bundle = list(decision.get("product_bundle") or [])
|
||||
retrieval = retrieve_for_scheme(session.slots, bundle)
|
||||
|
||||
scheme = {
|
||||
"tech_line": decision.get("tech_line"),
|
||||
"product_bundle": bundle,
|
||||
"summary": decision.get("summary"),
|
||||
"confidence": confidence,
|
||||
"rule_id": decision.get("rule_id"),
|
||||
"citations": [
|
||||
{"id": c.get("id"), "title": c.get("title"), "content": c.get("content")}
|
||||
for c in retrieval.chunks
|
||||
],
|
||||
"slots": dict(session.slots),
|
||||
}
|
||||
session.last_scheme = scheme
|
||||
session.phase = "handoff" if need_handoff else "scheme_ready"
|
||||
|
||||
lines = [
|
||||
business_config.closing_prompt,
|
||||
"",
|
||||
f"技术路线:{scheme['tech_line']}",
|
||||
f"推荐组合:{', '.join(bundle)}",
|
||||
f"说明:{scheme['summary']}",
|
||||
f"置信度:{confidence:.0%}",
|
||||
]
|
||||
if scheme["citations"]:
|
||||
lines.append("")
|
||||
lines.append("知识依据(原型检索):")
|
||||
for c in scheme["citations"][:3]:
|
||||
lines.append(f"- {c['title']}:{c['content']}")
|
||||
if need_handoff:
|
||||
lines.append("")
|
||||
lines.append("系统建议转人工确认后再对外发送方案。回复「转人工」可进入人工流程。")
|
||||
else:
|
||||
lines.append("")
|
||||
lines.append("可继续补充约束,或回复「转人工」交给销售。")
|
||||
|
||||
reply = "\n".join(lines)
|
||||
return _finish(session, reply, scheme=scheme, need_handoff=need_handoff)
|
||||
|
||||
|
||||
def _finish(
|
||||
session: Session,
|
||||
reply: str,
|
||||
*,
|
||||
asking_slot: str | None = None,
|
||||
scheme: dict[str, Any] | None = None,
|
||||
need_handoff: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
session.messages.append({"role": "assistant", "content": reply})
|
||||
store.touch(session)
|
||||
return {
|
||||
"session_id": session.id,
|
||||
"reply": reply,
|
||||
"phase": session.phase,
|
||||
"slots": session.slots,
|
||||
"asking_slot": asking_slot,
|
||||
"scheme": scheme or session.last_scheme,
|
||||
"need_handoff": need_handoff,
|
||||
}
|
||||
Reference in New Issue
Block a user