【Agent智能体】40DialogueEngine设计实现
章节四十:DialogueEngine设计与实现
一、DialogueEngine设计
1.1 设计定位
DialogueEngine:一轮消息处理的调度中心。接收用户消息和对话状态(DialogueState),判断本轮走哪条处理路径,并返回机器人回复。
它不负责具体业务逻辑,不直接查询知识库,也不直接执行flow。只负责判断本轮消息应该交给谁处理,并把处理结果写入当前对话轮次。
涉及的组件:
| 组件 | 简要作用 |
|---|---|
| DialogueState | 保存会话、任务、聚焦对象和历史记录 |
| Turn | 保存本轮用户输入和机器人输出 |
| TurnPlanner | 根据文本消息和状态生成本轮计划 |
| TurnPlanValidator | 检查本轮计划是否可靠、是否可执行 |
| ClarifyResponder | 在计划不清晰时生成追问 |
| TaskHandler | 处理业务任务 |
| KnowledgeHandler | 处理知识问答 |
| ChitchatHandler | 处理闲聊 |
1.2 完整流程步骤
第一步:准备会话
核心问题是当前会话还能不能继续使用。如果会话超时(超过1小时未活动),引擎清理运行状态:清空活跃任务、暂停任务、系统流程和聚焦对象,然后关闭旧session开启新session。
第二步:创建本轮记录
一条用户消息对应一轮对话(Turn)。处理过程中暂存于DialogueState.pending_turn,本轮结束后写入当前Session.turns。
第三步:判断消息类型
引擎区分消息是文本还是业务对象。文本消息需要理解用户想做什么;对象消息(如点击订单卡片)记录用户当前关注的对象。
第四步:处理对象消息
对象消息来自前端点击。引擎先把对象写入DialogueState.focused_object,然后判断:
- 如果对象刚好能补齐当前任务需要的信息,就继续业务任务。
- 如果只能知道"用户点了对象"但不知道"想做什么",就追问用户意图。
第五步:处理文本消息
文本消息交给TurnPlanner理解。理解时参考:最近对话、active_task、paused_tasks、focused_object、系统支持的flows和knowledge_intents。
TurnPlanner不是凭空理解,而是在系统支持的能力范围内选择最合适的处理方向。本轮计划(TurnPlan)可以是:
- 业务任务:启动/恢复/取消哪个工作流、设置哪个槽位
- 知识问答:问哪类知识问题
- 闲聊:用户不是办业务也不是问知识,只需自然回复
第六步:检查理解结果
TurnPlanner的理解结果需要TurnPlanValidator检查。常见需要澄清的情况:意图不明确、缺少对象、多个方向冲突、表达太短或上下文不足。
第七步:澄清处理
当系统无法确定用户目的时,交给ClarifyResponder追问,补齐关键信息。追问后本轮不会继续执行业务处理。
第八步:进入业务任务
用户明确要办理业务时,引擎把本轮交给TaskHandler。TaskHandler根据当前状态推进业务流程,把回复写回本轮Turn。
第九步:进入知识问答
用户问规则、政策、商品信息等问题时,引擎把本轮交给KnowledgeHandler。进入前会先暂停active_task,避免破坏原来的业务流程。
第十步:进入闲聊
用户只是问候或简单聊天时,交给ChitchatHandler。同样会先暂停active_task。
第十一步:提交本轮记录
无论本轮是业务、知识、闲聊还是追问,最后都要提交本轮记录,成为会话历史的一部分。返回结果包含用户ID、消息ID和机器人回复。
二、DialogueEngine实现
2.1 初始化
DialogueEngine通过构造函数依赖注入接收各核心组件:TurnPlanner、TaskHandler、KnowledgeHandler、ChitchatHandler、ClarifyResponder、TurnPlanValidator。
核心是依赖注入:DialogueEngine不自己创建这些组件,而是由外部组装好后传进来。好处是每个组件职责独立,DialogueEngine只负责调度。
2.2 process主流程
process()是DialogueEngine的入口方法,本身不处理具体业务,只做分发:
第一步:process(message, state)接收消息和状态。
第二步:_prepare_session准备会话(检查是否超时,处理session创建/重置)。
第三步:_begin_turn开启本轮Turn。
第四步:判断消息类型。TEXT消息走_handle_text_message;OBJECT消息先记录focused_object再走_handle_object_message。
第五步:把返回的BotMessage列表写入pending_turn.assistant_messages。
第六步:commit_pending_turn提交本轮。
第七步:返回ProcessResult。
2.3 TEXT消息处理
_handle_text_message()的处理流程:
第一步:调用TurnPlanner.predict()预测本轮计划。
第二步:调用TurnPlanValidator.validate()校验计划是否可执行。
第三步:如果校验不通过,调用_clarify()生成澄清回复。
第四步:如果校验通过,根据TurnPlan分发:
- task不为None:交给TaskHandler
- knowledge不为None:暂停当前任务,交给KnowledgeHandler
- 其他情况:暂停当前任务,交给ChitchatHandler
2.4 OBJECT消息处理
对象消息是用户发送的结构化对象(如商品或订单)。系统先记录聚焦对象到DialogueState.focused_object,然后进入_handle_object_message()。
两种处理结果:
- 当前任务正在收集这个对象ID,就把对象转换成SetSlotsCommand,交给TaskHandler继续推进。
- 不能直接填槽,就触发OBJECT_REQUIRES_INTENT澄清,让用户说明想做什么。
判断当前正在收集哪个槽位的方法:先看active_system_task是否在收集,再看active_task的当前step是否是CollectSlotStep。
2.5 澄清回复
DialogueEngine不自己写澄清话术,只把reason交给ClarifyResponder。职责分工清晰:TurnPlanValidator判断为什么不能执行,DialogueEngine决定需要澄清,ClarifyResponder生成具体回复。
2.6 Builder组装
DialogueEngine的组件由build_dialogue_engine()统一创建和组装,主要完成四件事:
第一步:加载flow配置(user_flows.yml和system_flows.yml)。
第二步:创建TaskHandler及其依赖(CommandProcessor、FlowExecutor、ActionRunner)。
第三步:创建KnowledgeHandler和知识Provider注册表(ProductAPIProvider、OrderAPIProvider、FAQProvider、RAGProvider)。
第四步:创建ChitchatHandler、ClarifyResponder、TurnPlanner、TurnPlanValidator,并注入到DialogueEngine。
Builder把对象创建逻辑集中到一个地方,DialogueEngine本身只关心调度,不关心组件如何创建。