❝
大模型本身是"过目就忘"的——每次调用都是一张白纸。可你用 LangGraph 搭的 Agent,却能续聊、能恢复、甚至能"回到三步之前重新跑"。这背后靠的是两个核心概念:「thread(一次对话)」 和 「Checkpoint(每一步的状态快照)」。这篇文章用 6 张图把 LangGraph 的会话管理彻底讲透,并在最后引申一下生产级框架 DeepAgents 在它之上做了哪些加固。
❞
开场:模型没记性,记性是"框架"给的
先说个反直觉的事实:大模型 API 是**无状态(stateless)**的。它每次被调用,只能看到这一次请求里塞进去的内容,**「根本不记得」**上一轮你说过什么。
那"连续对话"是怎么来的?靠框架在背后维护一份历史,每次调用都重新递给模型。这层"管历史"的基础设施,就是**「会话管理(Session Management)」**。
不同框架给出的答案差异很大。LangGraph 的答案尤其有特点——它没有单独造一个"会话模块",而是把会话管理**「收进了一个更通用的抽象」**:状态图(StateGraph)+ 检查点(Checkpoint)。理解了这套抽象,你不仅懂了它怎么记对话,还顺带理解了它为什么能做到时间旅行、断点续跑这些别家做不到的事。
我们从最外层的 thread 开始往里剥。
一、一次对话 = 一个 thread
在 LangGraph 里,一段连续对话被称为一个 「thread」(线程,但跟操作系统的线程没关系,理解成"对话线"更准确)。每个 thread 由一个 thread_id 唯一标识。

你调用 Agent 时,thread_id 不是写在请求体里,而是放在一个叫 config.configurable 的配置包里传进去:
config = {"configurable": {"thread_id": "abc-123"}}# 第一轮await agent.ainvoke({"messages": [HumanMessage("帮我建个项目")]}, config=config)# 第二轮——只要带上同一个 thread_id,它就接着上一轮聊await agent.ainvoke({"messages": [HumanMessage("再加个测试")]}, config=config)
注意第二轮:我们只塞了一句新消息,「完全没有手动把第一轮的历史拼进去」。LangGraph 自己根据 thread_id 把这个 thread 之前的状态捞了出来、接着往后跑。这就是 thread 的意义——「它是"自动续聊"的钥匙」。
换一个 thread_id,就是开一段全新的、互不干扰的对话。多用户、多会话的隔离,本质上就是发不同的 thread_id。(DeepAgents 里用 UUID7 来生成新的 thread id。)
那么问题来了:LangGraph 凭什么能根据 thread_id 把历史"捞出来"?答案在下一层——Checkpoint。
二、核心机制:每走一步,拍一张快照
这是 LangGraph 会话管理**「最该理解的一点」:它的存储模型不是"一个消息数组",而是「一连串状态快照」**。

LangGraph 把 Agent 的运行看成一张图,每执行一个节点(比如"调一次模型"“跑一个工具”),就把当时的**「完整状态」**拍一张快照存下来,这张快照就叫一个 「Checkpoint」。
每个 Checkpoint 有三个关键标识:
thread_id:属于哪段对话;checkpoint_id:这是该对话里的第几张快照(单调递增);parent_checkpoint_id:上一张快照是谁——靠它,所有快照串成一条**「链」**。
落到存储里(以 SQLite 为例),就是这样一张表,主键是三元组 (thread_id, checkpoint_ns, checkpoint_id):
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)-- 还有 parent_checkpoint_id 字段,把快照前后串起来
❝
checkpoint_ns(namespace)是给子图用的命名空间,主 Agent 默认是空串,先不用管它。❞
为什么要存"快照链"而不是"消息数组"?因为快照链能干两件数组干不了的事:「精确恢复到任意一步」,以及**「从任意一步岔出新分支」**(第六节细说)。代价是体积更大——这点 DeepAgents 有专门优化,最后一节会讲。
三、快照里到底装了什么:State 与 channels
既然每个 Checkpoint 是"完整状态的快照",那这个"状态"是什么?
在 LangGraph 里,状态(State)是一个**「带类型的字典」**,里面是若干个字段,每个字段叫一个 「channel(通道)」。对一个对话 Agent 来说,最核心的 channel 就是 messages——对话历史本身。

channel 有个很关键的特性:「它不是简单赋值,而是带"归并规则(reducer)"的」。比如 messages 这个 channel 的规则是"追加"——你这一步返回几条新消息,框架就把它们**「追加」**到已有历史后面,而不是覆盖。这就是为什么你每轮只用返回新消息,历史会自动累积。
除了 messages,State 里还能放别的 channel。DeepAgents 就额外塞了几个私有字段,比如:
_summarization_event:记录"上次在哪儿压缩过、摘要是什么";_context_tokens:当前上下文用了多少 token。
这些私有 channel 跟着 Checkpoint 一起持久化,但**「不会」**直接发给模型——它们是会话管理自己的"账本"。
一句话记住:「Checkpoint 存的是整个 State(所有 channel),而不只是聊天记录。」 这也是为什么 LangGraph 能恢复的不仅是"说过的话",还有 Agent 运行到一半的各种中间状态。
四、Checkpoint 存哪儿?可插拔的 Checkpointer
负责"把 Checkpoint 写进去、读出来"的组件,叫 「Checkpointer」。它是一个接口,后端可以随意替换:

| Checkpointer | 存储介质 | 典型场景 |
|---|---|---|
MemorySaver | 进程内存 | 测试、临时跑、demo(重启即丢) |
AsyncSqliteSaver | 本地 SQLite 文件 | 单机持久化(DeepAgents CLI 默认) |
AsyncPostgresSaver | PostgreSQL | 生产、多实例共享 |
用法就是编译 graph 时把它传进去:
from langgraph.checkpoint.memory import MemorySaveragent = create_agent(model, tools, checkpointer=MemorySaver())
「关键点:没有 checkpointer,就没有记忆。」 如果你编译 graph 时不传 checkpointer,那它就是无状态的——每次调用都从头开始,thread_id 也救不了你。这是新手最容易踩的坑:明明传了 thread_id 却发现不记事,十有八九是忘了配 checkpointer。
DeepAgents 的 CLI 在生产模式下默认用 AsyncSqliteSaver,把所有对话落到 ~/.deepagents/sessions.db 这个文件里。它甚至还会直接查这张表来实现"会话列表""删除会话"这类管理功能,绕过 Agent 直接读底层数据,图个快。
五、恢复对话:同一个 thread_id 就够了
有了 thread + checkpointer,"恢复对话"这件听起来很复杂的事,就变得朴素得不像话:

「继续聊」:用同一个 thread_id 再发一次请求就行。LangGraph 自动找到这个 thread 的最新 Checkpoint,把状态恢复出来,接着往后跑。程序重启过?没关系,只要 checkpointer 是持久化的(SQLite/Postgres),历史还在磁盘上。
「把历史读出来」(比如刷新 UI 要重新渲染对话):调 aget_state:
state = await agent.aget_state(config) # config 里带着 thread_idmessages = state.values["messages"] # 历史消息都在这儿
「手动改状态」(比如插入一条系统提示、或人工干预):调 aupdate_state,它会基于当前快照生成一张新快照。
❝
一个工程细节:DeepAgents 在客户端-服务端模式下,服务器重启后可能出现"磁盘上有 Checkpoint、但内存里的 thread 记录没了"的错位,它专门写了个
aensure_thread来先确保 thread 记录存在,再操作状态。生产级框架的麻烦,往往都藏在这种边角料里。❞
六、杀手锏:时间旅行与分叉
快照链相比"消息数组"最大的优势,在这一节兑现。

因为每一步都留了快照、快照之间又有父子链,所以你能做两件很酷的事:
「① 时间旅行(time-travel)」:在 config 里指定一个**「历史」**的 checkpoint_id,就能让 Agent"回到那一刻"的状态。配合 get_state_history 把整条链列出来,你可以挑任意一张快照重放。
# 列出这个 thread 的所有历史快照async for snapshot in agent.aget_state_history(config): print(snapshot.config["configurable"]["checkpoint_id"])# 指定回到某一张old_config = {"configurable": {"thread_id": "abc-123", "checkpoint_id": "xxx"}}await agent.ainvoke(new_input, config=old_config)
「② 分叉(fork)」:从历史某张快照重新跑、且给不同的输入,就会**「岔出一条新的快照分支」**——原来的历史完好无损,新的探索另起一条线。这对"我想试试换个方向会怎样"的场景极其有用。
值得说明的是:「这是 LangGraph 框架层提供的能力」。建立在它之上的 DeepAgents 出于产品简洁性考虑,目前**「没有把时间旅行/分叉暴露给终端用户」**——它只用 checkpoint_id 做了"会话列表缓存失效"之类的小事。能力在底座,用不用是上层产品的选择。
七、别混淆:Session(短期)vs Store(长期记忆)
聊到这里要澄清一个特别容易搞混的概念。我们前面讲的所有东西——thread、Checkpoint——管的都是**「一段对话内部」的记忆,是「短期」**的、按 thread 隔离的。
但 Agent 还需要另一种记忆:「跨对话的长期记忆」。比如"这个用户偏好用中文"“上个月帮他建过一个电商项目”——这些信息不该绑死在某一个 thread 里,换一段新对话也得记得。

LangGraph 把这两件事**「分得很干净」**:
| Checkpointer | Store | |
|---|---|---|
| 管什么 | 一段对话的状态(messages 等) | 跨对话的长期知识 |
| 隔离维度 | 按 thread_id | 按命名空间(如按 user) |
| 生命周期 | 跟随这段会话 | 长期留存 |
| 典型内容 | 对话历史、运行中间态 | 用户画像、学到的技能/事实 |
设计你自己的 Agent 时,先想清楚每条信息该进哪边:「"只在这次对话有用"的进 Checkpoint,"换个对话也得记得"的进 Store。」 想不清楚,后面就是数据串台和隐私泄露的坑。
八、引申:DeepAgents 在 LangGraph 之上做了什么加固
LangGraph 给的是通用底座,但直接拿去跑长任务(比如让 Agent 写一整个项目)会遇到两个现实问题。DeepAgents 作为生产级框架,针对性地做了加固:
[图 6:两种记忆 + DeepAgents 的生产级加固] (看下半部分:DeltaChannel + Summarization)
「加固一:DeltaChannel——给快照"瘦身"。」 前面说快照链体积大,到底有多大?如果每张快照都把全量 messages 拷一遍,存储量会随对话轮数按 「O(N²)」 膨胀(第 N 轮要存前 N 条 × N 张快照)。DeepAgents 用一个叫 DeltaChannel 的机制:平时只存"这一步新增了什么(delta)",每隔 50 步才拍一张全量快照,把增长压回 「O(N)」。对长任务来说,这是从"撑不住"到"扛得住"的区别。
「加固二:SummarizationMiddleware——上下文压缩 + 落盘。」 对话太长撑爆窗口时,它会:
- 把要丢弃的旧历史**「先归档」**到一个 Markdown 文件(按 thread 一个文件,路径形如
/conversation_history/{thread_id}.md); - 用一段 LLM 摘要替换掉模型输入里的旧消息;
- 在 State 的
_summarization_event字段里记下"在哪儿切的、归档到哪了"。
妙处在于:压缩是**「非破坏式」的——原始历史落了盘,Agent 随时能 read_file 翻回来;摘要、切点都跟着 Checkpoint 一起持久化,所以「恢复对话时连"压缩状态"也能一并恢复」**。(这部分我之前单独写过一篇深读,感兴趣的可以翻文末延伸阅读。)
一句话总结这一节:「LangGraph 负责"能记、能恢复",DeepAgents 负责"长期跑也不崩、不丢"。」
结语:把会话管理"藏"进通用抽象
回头看,LangGraph 处理会话管理的思路很有辨识度:「它不把"对话记忆"当成一个特殊模块,而是当成"状态图 + 检查点"这套通用机制的一个自然结果。」
- 一次对话 = 一个
thread; - 记忆 = 每一步的
Checkpoint快照; - 续聊 = 同一个
thread_id; - 后悔药 = 沿快照链时间旅行 / 分叉;
- 长期记忆 = 另一套
Store,跟会话分开。
这套设计的好处是**「统一而强大」**——你为"让 Agent 跑复杂流程"配的检查点机制,顺手就把会话管理给解决了,还白送了时间旅行。代价是抽象层级偏高、快照体积偏大,需要 DeepAgents 这类上层框架再做一层调教,才能稳稳地扛住真实的长任务。
下次你用 LangGraph 搭 Agent,发现"它怎么不记事"时,先别慌——大概率只是忘了给它配一个 checkpointer 而已。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

383

被折叠的 条评论
为什么被折叠?



