Agent 调完工具后要回到 LLM 继续推理,还是直接结束?这不能写死一条边,需要 router 读 state 决定下一跳。add_conditional_edges 把 router 返回值经 path_map 映射到节点名或 END。
1. 定位
| 维度 | 内容 |
|---|---|
| 角色 | 运行时分支与环的控制 |
| 输入 → 输出 | router(state) -> str → path_map → 下一节点 |
| 核心 API | add_conditional_edges(source, router, path_map) |
| 依赖 LangChain | router 可读 messages 最后一条 |
2. 图拓扑
节点表
| 节点名 | 职责 | 读 State | 写 State |
|---|---|---|---|
llm |
模拟决策 | messages, step |
messages, step |
tool |
模拟工具 | step |
messages, step |
边表
| 源 | 目标 | 类型 | router 返回值 |
|---|---|---|---|
| START | llm | 固定 | — |
| llm | tool / END | 条件 | "tool" / "end" |
| tool | llm | 固定 | — |
path_map:{"tool": "tool", "end": END}
3. invoke 生命周期(含环 ≥2 轮)
1 | 轮1: llm step=1 → router=tool → tool step=2 |
每轮 superstep 可能含 1~2 个节点(llm 或 llm+tool)。
4. 原理
4.1 router 与 path_map 契约
router 返回的字符串必须是 path_map 的键;值是目标节点名或 END。
4.2 形成环
tool → llm 固定边 + llm 条件边回 tool 或 END,即 ReAct 骨架。
4.3 Literal 类型注解
-> Literal["tool", "end"] 便于静态检查与文档。
5. 最小可运行示例
1 | from typing import Annotated, Literal, TypedDict |
6. 执行追踪
| 轮次 | 节点 | step |
路由 |
|---|---|---|---|
| 1 | llm | 1 | → tool |
| 1 | tool | 2 | → llm |
| 2 | llm | 3 | → tool |
| 2 | tool | 4 | → llm |
| 3 | llm | 5 | → END |
重要配置参数
| 参数 | 类型 / 默认 | 作用与影响 | 参考起点 | 配置指导 |
|---|---|---|---|---|
router |
Callable | 返回 path 键 | 读 messages/step | 保持纯函数 |
path_map |
dict | 键→节点/END | 全覆盖 router 输出 | 漏键即报错 |
| source 节点 | str | 条件边起点 | "llm" |
可 START |
END 作值 |
常量 | 提前终止 | "end": END |
勿写字符串 |
| 环 + step 上限 | 业务 | 防死循环 | max_steps | 生产必加 |
Literal[...] |
类型 | 自文档 | 两三分支 | 推荐 |
7. 易踩坑
- router 返回
"tools"但 path_map 只有"tool":KeyError 类路由错误。 - 无退出条件的环:step 永远不满足 end,直到递归限制。
- path_map 值写节点名字符串错拼:compile 不一定捕获。
小结
- conditional_edges = router + path_map;ReAct 靠 llm ↔ tool 环 + 条件到 END。
- router 返回值与 path_map 键集合一致。
- 含环图务必 ≥2 轮 验证并加上限计数。