学习笔记 · Obsidian

LangGraph 状态、持久化与子图错误排障

LangChainLangGraphPython

六类错误对应六个不变量

错误被破坏的不变量修复方向
GRAPH_RECURSION_LIMIT图必须在允许步数内到达停止条件先找无界循环,再评估提高 limit
INVALID_CHAT_HISTORY每个 AI tool call 都有匹配 ToolMessage修正历史或从 interrupt 正确恢复
INVALID_CONCURRENT_GRAPH_UPDATE同一步并行写同一 key 必须有合并规则为 state key 定义 reducer
INVALID_GRAPH_NODE_RETURN_VALUE节点的所有路径都返回 state update dict修正返回类型与 key
MISSING_CHECKPOINTER使用内建 persistence 时必须配置 checkpointercompile/entrypoint 时注入
MULTIPLE_SUBGRAPHS同节点多次调用持久子图时 namespace 必须不冲突选择正确 checkpointer 模式/唯一节点名

循环与步数上限

递归上限表示 StateGraph 在达到 stop condition 前执行了过多步骤。优先检查边条件、计数器、失败重试边和 A↔B 循环;只有确认复杂图确实需要更多合法步骤时,才在 invoke config 中提高 recursion_limit。

上限是防止失控成本的安全阀。提高到很大值必须同时设置模型/工具调用预算、wall-clock timeout、取消与告警,否则只是把无限循环推迟。

Chat history 与 interrupt 恢复

create_agent 的 call_model 收到带未回答 tool calls 的历史会报错。常见来源:手工构造了不完整消息,或图在 tools 前中断/工具异常后,又带一条新 HumanMessage 从 START 调用。

处理有两条路径:

  1. 为未回答的每个 tool call 补匹配 ToolMessage;直接以这些消息 invoke 会把它们追加并从 START 运行。
  2. 读取 get_state(config),删除未回答 tool calls 或补齐 ToolMessage,update_state 后用 invoke(None, config) 从 interrupt 恢复。

生产系统应优先使用第二条恢复语义,保留 checkpoint 与审计;不能在不理解状态位置时简单追加新用户消息。

并行更新与节点返回值

同一 superstep 的 fan-out 节点若同时返回同一 state key,而该 key 没有 reducer,LangGraph 无法确定覆盖顺序。将字段声明为带 reducer 的 Annotated 类型,例如 list 使用 append/operator.add;reducer 必须满足确定性与尽量可结合,否则重放或并行顺序变化会产生不同结果。

每个节点必须返回 dict,且 key 属于 state schema。复杂分支、异常处理和提前返回都要覆盖;不要返回裸 list、模型消息或 None。单测应逐分支断言返回 shape,而不只测 happy path。

Checkpointer 与 persistence

直接使用 OSS Graph API 的内建持久化时,在 StateGraph.compile(checkpointer=...) 或 Functional API @entrypoint(checkpointer=...) 注入 checkpointer。InMemorySaver 适合测试,不适合进程重启后的生产恢复;Agent Server 可托管持久化基础设施。

thread ID、checkpoint namespace、序列化版本和数据库迁移必须作为运行契约。配置了 checkpointer 但每次随机 thread ID,与完全没有会话恢复在效果上近似。

多次调用子图的三种模式

同一节点内多次调用子图时,按需求选择:

  • 不需要 interrupt:checkpointer=False,完全关闭子图 checkpoint。
  • 需要 interrupt、但不需要跨调用持久:省略参数,继承父图;每次调用获得独立 namespace。
  • 需要跨调用持久:checkpointer=True。系统使用位置后缀区分调用;若需要稳定、基于名称的 namespace,把每个子图包在唯一命名节点中。

不要因为遇到 MULTIPLE_SUBGRAPHS 就一律关闭 checkpoint;这会同时失去 interrupt/恢复能力。应先决定业务所需的持久化范围。

生产验证

  • 构造故意循环,验证 recursion limit、成本上限与取消。
  • 让并行节点同时写同一 key,验证 reducer 在不同完成顺序下结果一致。
  • 在 tool 前 interrupt,分别测试补 ToolMessage、update_state + invoke(None)。
  • 重启进程/实例后恢复同一 thread,验证真实持久 checkpointer,而非内存假象。
  • 并发调用同一子图,检查 namespace、checkpoint 隔离和 time travel 结果。