记录一下那天晚上的排查过程。langgraph 0.2.28,给 StateGraph 挂了 MemorySaver,invoke 时只传 input,报 Checkpointer requires one or more of the following configurable keys: thread_id,换成 stream 也一样。

刚开始盯着「用了异步 ainvoke 但 config 结构还是同步写法」,折腾了半个多小时,排掉了。

这类报错背后常见的原因有几处,逐个排除就行。带 checkpointer 的图必须在 config 里给 thread_id。也可能是config 传进了 state 那一层,被当成普通字段。

还有一种情况,checkpointer 挂在子图上,主图没有继承配置。

第一件事是确认图是否真的挂了 checkpointer

python -c "from app.graph import build;g=build();print(g.checkpointer is not None)"

带上 configurable 再调用

python -c "from app.graph import build;g=build();print(g.invoke({'q':'hi'},{'configurable':{'thread_id':'t-1'}}))"

然后用同一个 thread 续跑第二轮

python -c "from app.graph import build;g=build();print(g.invoke({'q':'继续'},{'configurable':{'thread_id':'t-1'}}))"

等前面都确认过,再打印最终状态确认记忆生效

python -c "from app.graph import build;g=build();print(g.get_state({'configurable':{'thread_id':'t-1'}}))"

这类改动我都写进变更记录,过两周再出问题时能对上号。

我习惯把每一步的执行时间和结果记在一边,后面复盘省事。

别只看报错消失,验证要多做一步。两次调用共享同一会话上下文,state 里能看到上一轮消息,不再抛 thread_id 相关错误。

thread_id 是会话主键不是请求 id,每次请求换新值等于每轮都从空状态开始。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189