系列: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=true | answer |
| 缺产品版本 | missing_fields=[version] | ask_user |
| 首次短暂超时 | retryable, attempts=0 | retry_tool |
| 第二次仍超时 | retryable, attempts=1 | fail |
| 参数非法 | validation_error | fail,不重试 |
| 无权限 | permission=false | reject |
| 无资料且无可追问项 | complete=false | fail/unsupported |
真实接 LangGraph 时,条件边的 API 写法要按官方文档核对;上面的 RouteState 与纯函数可以先独立测试,再映射到图节点名。
三种失败模式
- 分支条件重叠。同时缺权限又工具超时,如果判断顺序不固定,可能错误重试敏感工具。
- 返回任意字符串。模型生成不存在的节点名,图运行到未知位置。
- 无默认出口。新增错误类型没有匹配分支,任务卡在 running。
每个路由都应有穷尽的枚举和兜底失败状态,返工还要有总预算。下一篇做 checkpoint 时,路由结果与 attempts 必须被持久化,否则恢复后可能再次获得一次重试机会。
面试官会追问:条件路由应该交给规则还是模型?
能由确定字段判断的就用规则:权限、金额阈值、错误类型、schema 是否通过、最大轮数。只有语义分类或开放任务才让模型参与,而且模型输出仍要落到有限枚举。
例如客服路由可以分两层:代码先拦截无权限与高风险动作;模型再在 faq、order_query、complaint 三类意图中选择;低置信度进入 ask_user。这样模型不会越过硬边界,规则也不用穷举全部语言表达。
测试时不能只覆盖“正确分支”,还要覆盖路由震荡:同一任务在两个节点之间来回跳。给 state 增加 route_history 与 step_budget,一旦重复模式超过阈值就终止并输出诊断,而不是无限消耗 Token。