到上一篇为止,我们的图有一个共同的弱点:它是"一次性"的。invoke 一返回,执行过程中的一切就消失了——用户第二次提问,Agent 不记得上一轮;某个节点中途失败,只能从头再跑一遍;想知道"第三步的时候 State 长什么样",也无从查起。这一篇讲 LangGraph 的持久化机制 Checkpointer。它只做一件简单的事:每走一步,存一次档。而会话记忆、故障恢复、时间旅行,乃至下一篇的人机协作,都建立在这个"存档"之上。

一、存档这件事,应该在什么时候做#

先想一个问题:如果让你给图加上"存档"功能,什么时候存最合适?

每个节点执行完就存?听起来很自然,但第一篇讲过,同一个超步里的节点是并行执行的,它们的写入要到超步结束时才统一合并。在超步中间存档,存下的是一个"合并了一半"的不完整状态。

所以 LangGraph 选择在每个超步结束时存一次档,这份存档叫 checkpoint(检查点)。用第一篇的并行问答图实测一下,一次完整运行留下了这样四份存档:

存档超步当时 State 里已有的字段下一步要执行
1-1(接收输入)无__start__
20questionsearch_kb、search_web
31question、kb、webanswer
42全部字段无(执行结束)

两个并行的检索节点共享第 1 步的那一份存档,它们的结果是一起出现的。每份存档不仅记下了"当时的数据",还记下了"接下来该执行谁"——这一点是后面所有能力的关键:只要有一份存档,就能从那个位置接着跑。

二、开启持久化:Checkpointer 与线程#

开启持久化只需要两处改动:

Python
from langgraph.checkpoint.memory import InMemorySaver

graph = builder.compile(checkpointer=InMemorySaver())       # 1. 编译时装上存档器

config = {"configurable": {"thread_id": "alice"}}
graph.invoke({"messages": [("user", "我叫小睦")]}, config)    # 2. 调用时指定线程

Checkpointer(存档器) 决定存档放在哪里,thread_id(线程) 决定存档归到哪一组。可以把线程理解成一个独立的存档槽:同一个 thread_id 的多次调用,共享同一串存档;不同的 thread_id 互不干扰。