用户问“取消预订要收费吗”,需要查政策;用户说“查一下我的订单”,需要读取个人订单;用户说“免费就帮我取消”,需要先查事实,再判断条件,最后进入操作流程。三句话都围绕酒店和取消,处理路径却不同。
意图路由根据识别结果选择相应的服务或处理流程。它需要一起考虑用户目标、当前状态、参数和业务约束。本文以酒店助手为例,介绍知识检索、只读工具、写操作和澄清四类处理路径,代码见完整工程。
一、意图识别与路由的分工
分类器返回 cancel_booking,只说明用户表达了取消目标。路由层仍需知道取消哪笔订单、订单属于谁、是否允许取消、费用是多少,以及用户是否确认了当前方案。
如果这些问题全部交给标签处理,就会不断新增“取消但缺订单号”“取消且收费”“取消已经确认”等类别。标签数量会随流程状态迅速膨胀,标注也变得难以稳定维护。
更清楚的结构是:意图描述目标,槽位描述参数,状态描述当前进展,业务规则决定可用动作。路由层根据这些信息决定下一步操作,不能只按标签查找对应的处理函数。
二、定义业务能力与意图映射
能力目录说明系统实际上能提供哪些服务。一个意图可以映射到多步流程;多个咨询意图也可以共享同一个检索能力。
| 用户目标 | 标签 | 处理能力 | 返回依据 |
|---|---|---|---|
| 早餐、入住或取消政策 | policy_query | 知识检索 | 文档内容与来源 |
| 查询房源与价格 | room_search | 只读房源工具 | 当前可用信息 |
| 查询个人订单 | order_query | 受权限约束的订单工具 | 具体订单事实 |
| 预订、修改、取消 | 对应业务标签 | 写操作流程 | 参数、核验、确认与执行结果 |
| 参数缺失或对象不明确 | 目标可能已知 | 澄清策略 | 当前状态与候选项 |
| 当前能力不支持 | 范围外状态 | 说明范围或转交 | 已发布的能力目录 |
闲聊是否需要单独接收,取决于产品。综合助手可以把闲聊交给通用对话能力;本系列酒店助手则提供能力说明。不要因为每个输入都需要回复,就把闲聊伪装成某个酒店意图。
三、知识检索与订单查询
知识库适合保存相对稳定的规则和说明,例如早餐时间、入住政策、材料要求。个人订单状态则来自业务系统,带有用户归属、时效和访问权限。
把“我的订单取消了吗”送去普通文档检索,系统可能找到“如何取消订单”,却没有回答这笔订单实际处于什么状态。检索结果语义相关,不代表它拥有问题所需的事实。
因此可以先问两个问题:答案主要来自公共说明,还是来自当前用户的业务对象?如果是后者,先定位对象并调用受权限约束的工具;必要时再检索政策解释工具结果。
例如订单查询返回“取消费 50”,政策检索负责解释费用依据。两者共同构成答案,但不能用知识库里“部分房型可免费取消”的概括覆盖订单的具体费用。
四、结合上下文改写查询
用户问“那个能退吗”,直接拿这几个字检索通常信息不足。应先根据会话状态确认“那个”指的是哪笔订单或哪种产品,再生成明确的检索查询。
一种常见顺序是:
当前输入 + 对话状态
→ 判断目标与指代
→ 确定知识主题或业务对象
→ 在知识分支改写查询
→ 检索、组织证据、生成回答
但这不是所有系统必须遵循的固定流水线。有些问题需要先做轻量检索才能确认能力,有些分类器本身也会使用检索到的标签示例。关键是区分检索的用途:它是在帮助选择标签,还是在为最终答案寻找证据?不同用途应有独立输入和评测。
改写时要保留否定和条件。“收费就别取消”不能被压缩成“取消订单”。对写操作尤其要避免把检索优化过程中生成的简短查询,再当成用户完整授权。
五、写操作的校验与确认
取消订单时,需要先识别取消意图、确定订单,再查询状态和费用,最后向用户展示方案并等待确认。
确认记录至少绑定操作类型、关键参数、对象版本和有效条件。例如用户确认的是“取消 DEMO-A,当前费用 0”,就不应在费用或订单对象变化后继续沿用旧确认。
附件的模拟流程保存待确认方案;用户回复“确认”后,再读取订单版本和费用。如果条件发生变化,返回冲突,让应用展示新方案。这样,识别结果、用户确认和真正提交之间有明确对应关系。
识别取消目标
→ 补齐订单号
→ 查询所属用户、状态和费用
→ 判断“免费才取消”是否成立
→ 展示方案
→ 用户确认
→ 重新核验关键事实
→ 提交并保存结果
“确认参数”和“确认办理”也需要区分。用户回答“对,是这张订单”,可能只确认了对象,并没有确认取消费用与实际操作。产品应把确认问题说完整,让用户知道回复会触发什么。
六、Workflow 与 Agent 的分工
对于已知、稳定、需要严格约束的操作,固定 Workflow 很合适。取消订单有哪些检查、哪些条件必须成立,应由可检查的代码表达。
Agent 更适合处理路径不确定的目标,例如在多个信息来源之间补充查询、比较候选方案、组织不同工具返回的材料。它可以提出下一步,但工具权限、参数校验和写操作约束仍应由运行环境执行。
两者可以组合:Agent 负责理解复杂需求与收集证据,具体预订或取消由固定流程完成。这样既能保留灵活性,也能把关键业务约束集中在少数接口中。
选择方式时,看的是不确定性在哪里。如果只是表达多样,往往只需更强的识别器;如果处理路径也需要探索,才更需要动态规划。不能因为入口用了 LLM,就让每一次订单修改都变成自由规划。
七、多任务的依赖与执行顺序
“查订单,如果免费就取消,同时看看明天的房间”包含多个目标。路由层应保留各自任务 ID、参数和依赖,而不是将整个输入压成一个标签。
任务依赖文章中的图,可以先执行订单查询,把结果变成可信事实;取消节点检查这个事实,查房节点则根据它自己的依赖继续处理。
如果取消条件不成立,只跳过取消节点。查房不依赖取消成功,就不应该一起停止。反过来,某个任务必须依赖前一步成功时,也不能仅因“前一步已经返回”就继续。
附件的 batch3/planner.py 用模拟订单结果推进这张图:只读节点返回结果,写节点返回待确认方案。程序根据查询结果选择后续节点,需要确认的写操作会暂停等待。
八、权限控制与提示词注入防护
知识文档可能包含“忽略前面的要求”之类文本,用户也可能在需求中声称自己有管理员权限。这些内容可以作为待分析的数据,却不能改变服务端的访问控制或工具能力。
提示词注入之所以危险,是因为模型处理自然语言时可能混淆数据与指令。路由层应将身份、权限、能力白名单和写操作门槛保存在可信程序中,避免仅依靠模型自觉遵守。OWASP 提示词注入说明
例如查询订单时,权限检查应使用服务端已认证身份。用户在聊天里输入“这张订单属于我”,不能成为读取依据。模型返回一个有效的订单 ID,也不证明用户有权访问它。
九、异常处理与服务降级
检索没有证据时,返回“当前资料不足”并引导补充;订单工具超时时,进入有限重试或服务降级;目标不明确时,提出具体问题。这三种结果不能都被压成“没听懂”。
服务降级也不应随意改变目标。如果取消工具不可用,可以保存待办理状态并说明结果尚未确定;不能为了给出流畅回复,换成一段取消政策说明后声称任务完成。
工程中的典型路由结果包括 knowledge、read_tool、clarify、confirm、condition_false 和 mock_write。只有最后一种表示模拟业务写入已经完成,其他结果都各自对应明确的下一步。
从工程根目录运行:
python batch3/assistant.py
python batch3/planner.py
更完整的检索、Agent 运行与流式服务设计,可以回看生产级 RAG 与 Agent 系统。本篇关注请求为什么走向某种能力;下一篇服务上线继续讨论同一条请求怎样可靠完成。