Day 024|条件路由:让 Agent 根据结果选择下一步

系列:100 天系统学习 AI Agent 开发
当前阶段:LangChain 与 LangGraph 工程化
今日目标:条件路由可以根据工具结果、置信度、用户权限或错误类型决定流程分支。

真正让流程像 Agent 的,不是节点,而是岔路口

检索到充分证据时回答;问题含糊时追问;工具暂时超时可以再试一次;用户无权限则立即终止。节点本身只是做事,条件路由才把观察结果变成下一步。

这里也最容易失控。有人会写“confidence > 0.8 就回答”,仿佛 0.8 是天然真理。可不同检索器的分数不可直接比较,相似度也不等于答案可靠。阈值必须从自己的标注数据校准,而且不能单独承担权限和安全决策。

今天要实现的路由规则

可重试且未重试

不可重试/预算用尽

无错误

充分

不足且问题可补充

知识库无资料

检索或工具结果

用户有权限?

终止并审计

工具是否报错?

重试一次

失败并解释

证据是否充分?

生成带引用回答

追问一个关键字段

拒答并说明范围

注意顺序:权限判断在置信度之前。再“相关”的片段,只要用户无权访问,就不能被用来回答。

路由输入要结构化

fromdataclassesimportdataclassfromenumimportEnumclassNextStep(str,Enum):ANSWER="answer"ASK_USER="ask_user"RETRY_TOOL="retry_tool"FAIL="fail"REJECT="reject"@dataclass(frozen=True)classRouteState:has_permission:booltool_error_kind:str|Noneattempts:intevidence_complete:boolmissing_user_fields:tuple[str,...]defchoose_next(state:RouteState)->NextStep:ifnotstate.has_permission:returnNextStep.REJECTifstate.tool_error_kind=="retryable":returnNextStep.RETRY_TOOLifstate.attempts<1elseNextStep.FAILifstate.tool_error_kind:returnNextStep.FAILifstate.evidence_complete:returnNextStep.ANSWERifstate.missing_user_fields:returnNextStep.ASK_USERreturnNextStep.FAIL

这段代码故意没有 similarity_score。第一版用“evidence_complete”这个经过证据检查的布尔值,更容易测试。以后如果加入分数,应同时考虑来源权威、版本匹配、证据覆盖和重排结果,而不是只看向量距离。

“证据充分”如何定义

以部署问题为例,用户要环境和命令,检索结果只覆盖命令,即使相似度很高也不完整。可以先将需求拆为 evidence_points:

{"required_points":["环境要求","安装命令"],"covered_points":["安装命令"],"evidence_complete":false,"missing_points":["环境要求"]}

这时系统可继续检索、追问版本,或回答“现有资料只确认了命令,环境要求暂无依据”。不要把“低置信度”当成一句笼统提示。

路由测试表

场景输入状态期望下一步
高质量证据且有权限complete=trueanswer
缺产品版本missing_fields=[version]ask_user
首次短暂超时retryable, attempts=0retry_tool
第二次仍超时retryable, attempts=1fail
参数非法validation_errorfail,不重试
无权限permission=falsereject
无资料且无可追问项complete=falsefail/unsupported

真实接 LangGraph 时,条件边的 API 写法要按官方文档核对;上面的 RouteState 与纯函数可以先独立测试,再映射到图节点名。

三种失败模式

  1. 分支条件重叠。同时缺权限又工具超时,如果判断顺序不固定,可能错误重试敏感工具。
  2. 返回任意字符串。模型生成不存在的节点名,图运行到未知位置。
  3. 无默认出口。新增错误类型没有匹配分支,任务卡在 running。

每个路由都应有穷尽的枚举和兜底失败状态,返工还要有总预算。下一篇做 checkpoint 时,路由结果与 attempts 必须被持久化,否则恢复后可能再次获得一次重试机会。

面试官会追问:条件路由应该交给规则还是模型?

能由确定字段判断的就用规则:权限、金额阈值、错误类型、schema 是否通过、最大轮数。只有语义分类或开放任务才让模型参与,而且模型输出仍要落到有限枚举。

例如客服路由可以分两层:代码先拦截无权限与高风险动作;模型再在 faq、order_query、complaint 三类意图中选择;低置信度进入 ask_user。这样模型不会越过硬边界,规则也不用穷举全部语言表达。

测试时不能只覆盖“正确分支”,还要覆盖路由震荡:同一任务在两个节点之间来回跳。给 state 增加 route_history 与 step_budget,一旦重复模式超过阈值就终止并输出诊断,而不是无限消耗 Token。