LangGraph 智能体与长期记忆:从无状态对话到有记忆的协作者
摘要:本文基于 DeepLearning.AI《LangGraph 中的 AI 代理》与《基于 LangGraph 的长期代理记忆》两门课程实践,拆解 LangGraph 智能体的三大核心机制:StateGraph 状态流、自定义 reducer 与检查点(Checkpointing)、语义记忆与程序性记忆。通过邮件助手从分类→起草→记住用户细节的演进路径,展示如何让 Agent 拥有跨会话的长期记忆能力。适合正在构建 Agent 应用、需要处理多轮状态与记忆的开发者。
1. 为什么 Agent 需要"记忆"
大多数 LLM 应用是无状态的:每次调用都是独立会话,上下文必须显式传入。但真实业务场景要求 Agent 具备两类记忆:
- 短期记忆(工作记忆):单个会话内的状态流转,如多轮对话、工具调用结果
- 长期记忆(跨会话):记住用户偏好、历史决策、业务上下文,下次会话仍可复用
LangGraph 把这个问题抽象为两个核心原语:StateGraph(状态图)+Checkpointer(检查点),外加基于消息存储的语义记忆。
2. StateGraph:把 Agent 变成状态机
LangGraph 的核心思想是把 Agent 流程建模为有向状态图:
fromlanggraph.graphimportStateGraph,ENDfromtypingimportTypedDict,Annotatedimportoperator# 定义状态结构classAgentState(TypedDict):messages:Annotated[list,operator.add]# 消息列表,默认追加graph=StateGraph(AgentState)2.1 踩坑 1:默认 reducer 只追加不替换
课程明确指出一个关键陷阱:messages状态键用默认的operator.add(或+)reducer,总是把新消息追加到数组末尾。当你想"替换"某条消息(如修正一条 AI 回复)时,需要自定义 reducer:
defreplace_or_append(left,right):"""按 id 替换已有消息,否则追加"""ifnotleft:returnright message_id=right[0].idifany(m.id==message_idforminleft):return[mforminleftifm.id!=message_id]+rightreturnleft+rightclassAgentState(TypedDict):messages:Annotated[list,replace_or_append]这在实现"AI 自主修正自己"或"人工介入修改回复"时是刚需。
3. 检查点:Human-in-the-Loop 的基础设施
LangGraph 的检查点(Checkpointing)机制把每一次状态转换都持久化下来,这是实现**人在回路(Human-in-the-Loop)**的基础:
fromlanggraph.checkpoint.sqliteimportSqliteSaver memory=SqliteSaver.from_conn_string(":memory:")# 在编译图时传入 checkpointerapp=graph.compile(checkpointer=memory)# 用 thread_id 区分会话config={"configurable":{"thread_id":"1"}}3.1 新版 LangGraph 的两个关键变化
课程提醒:较新版本与拍摄时版本有两处差异:
- 额外状态信息会存储到内存,并在
get_state()/get_state_history()时展示 - 状态在每次状态转换时存储(旧版只在 interrupt 或结束时存储)
这些变化让状态历史更完整,但也会让输出略有不同——注意版本差异导致的运行结果变化。
4. 长期记忆:邮件助手演进案例
《长期代理记忆》课程以邮件助手为主线,展示了记忆能力的逐步叠加:
4.1 阶段一:无记忆的邮件助手
- 对来信分类(回复 / 忽略 / 通知)
- 起草回复
- 安排会议
4.2 阶段二:加入语义记忆(Semantic Memory)
关键改进:助手能记住之前邮件中的细节。实现上采用语义记忆——把用户与助手的交互沉淀为可检索的记忆条目:
# 语义记忆的核心思路# 1. 定义 memory 工具:写入记忆条目# 2. 定义 recall 工具:检索相关记忆# 3. Agent 在对话前先 recall 相关记忆,再决定如何响应tools=[write_memory,recall_memory]这样当用户说"我上次说过我们 3 月要发布新版本",助手能检索到历史记忆并理解上下文,而不是重新询问。
4.3 长期记忆的类型划分
课程区分了几类长期记忆,各有适用场景:
| 记忆类型 | 内容 | 存储方式 | 典型场景 |
|---|---|---|---|
| 语义记忆 | 事实性知识 | 向量库/键值存储 | 用户偏好、业务事实 |
| 程序性记忆 | 流程/技能 | 代码/工具定义 | 业务操作规范 |
| 情景记忆 | 历史事件序列 | 时间线存储 | 会话回放、审计 |
5. 可复现实验:带记忆的邮件助手
课程配套代码(lesson_3)给出了完整路径:
importosfromdotenvimportload_dotenv _=load_dotenv()# 加载第三方 API token# 1. 构建邮件助手图(分类/起草/安排会议)# 2. 添加 memory 工具与 recall 工具# 3. 用 SqliteSaver 持久化会话状态# 4. 多轮对话验证记忆能力6. 深度洞察与踩坑汇总
洞察一:检查点是"认知脚手架"而非单纯容错。每次状态转换可追溯,回滚不只是技术操作,更是对提示质量、上下文完整性、问题拆解合理性的即时复盘。
洞察二:长期记忆要区分"事实"与"流程"。语义记忆解决"它知道什么",程序性记忆解决"它知道怎么做"。把业务流程硬塞进语义记忆会导致检索混乱。
洞察三:会话隔离(thread_id)是生产环境必配。不同用户、不同任务的对话必须用独立 thread_id,否则记忆会串线。
| 坑 | 表现 | 解法 |
|---|---|---|
| 默认 reducer 只追加 | 无法替换/修正消息 | 自定义按 id 去重 reducer |
| 版本差异 | 运行结果与课程不一致 | 关注 checkpoint 存储时机差异 |
| 无 thread_id | 多用户记忆串线 | 按会话/用户隔离 |
| 记忆无限膨胀 | 检索噪声增大 | 记忆条目去重 + 时效管理 |
| LLM 输出不确定性 | 每次结果不同 | 复现时固定温度/种子 |
7. 总结
一句话总结:LangGraph 把 Agent 的记忆能力工程化为"状态图 + 检查点 + 语义记忆"三件套——短期记忆靠 StateGraph 流转,跨会话记忆靠 Checkpointer 持久化,长期知识靠 Semantic Memory 检索,三者叠加才构成真正的"有记忆的协作者"。
互动收尾:你的 Agent 应用目前处理到哪一层记忆?是无状态、会话内状态、还是跨会话长期记忆?在实现记忆时踩过哪些坑(记忆污染、检索噪声、存储膨胀)?评论区分享你的方案,我们下一期拆解"记忆的检索策略"。
本文基于 DeepLearning.AI 课程学习实验,代码步骤可复现。