LangGraph State记忆机制深度解析:短期与长期记忆的实现原理与实战
前言
在构建基于大语言模型的AI智能体时,一个核心痛点始终挥之不去:如何让智能体拥有真正的"记忆"? 传统的无状态问答系统每次交互都是一张白纸,无论你之前和它聊过什么,它转头就忘。而LangGraph作为专为智能体设计的流程编排框架,通过精巧的双层记忆架构------短期记忆(Short-term Memory) 和长期记忆(Long-term Memory),为这一问题提供了工业级的解决方案。
本文将深入剖析LangGraph中状态(State)实现的记忆机制,从短期记忆的Checkpointer原理到长期记忆的Store架构,从代码实现到生产级部署,手把手带你构建真正有状态的AI智能体。
一、LangGraph记忆架构概览:Checkpointer + Store 双层设计
LangGraph的内存架构是一个精心设计的双系统,旨在模拟不同层次的人类记忆:
#mermaid-svg-9c0EkdVLetBuaKzC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9c0EkdVLetBuaKzC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9c0EkdVLetBuaKzC .error-icon{fill:#552222;}#mermaid-svg-9c0EkdVLetBuaKzC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9c0EkdVLetBuaKzC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9c0EkdVLetBuaKzC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9c0EkdVLetBuaKzC .marker.cross{stroke:#333333;}#mermaid-svg-9c0EkdVLetBuaKzC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9c0EkdVLetBuaKzC p{margin:0;}#mermaid-svg-9c0EkdVLetBuaKzC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9c0EkdVLetBuaKzC .cluster-label text{fill:#333;}#mermaid-svg-9c0EkdVLetBuaKzC .cluster-label span{color:#333;}#mermaid-svg-9c0EkdVLetBuaKzC .cluster-label span p{background-color:transparent;}#mermaid-svg-9c0EkdVLetBuaKzC .label text,#mermaid-svg-9c0EkdVLetBuaKzC span{fill:#333;color:#333;}#mermaid-svg-9c0EkdVLetBuaKzC .node rect,#mermaid-svg-9c0EkdVLetBuaKzC .node circle,#mermaid-svg-9c0EkdVLetBuaKzC .node ellipse,#mermaid-svg-9c0EkdVLetBuaKzC .node polygon,#mermaid-svg-9c0EkdVLetBuaKzC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9c0EkdVLetBuaKzC .rough-node .label text,#mermaid-svg-9c0EkdVLetBuaKzC .node .label text,#mermaid-svg-9c0EkdVLetBuaKzC .image-shape .label,#mermaid-svg-9c0EkdVLetBuaKzC .icon-shape .label{text-anchor:middle;}#mermaid-svg-9c0EkdVLetBuaKzC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9c0EkdVLetBuaKzC .rough-node .label,#mermaid-svg-9c0EkdVLetBuaKzC .node .label,#mermaid-svg-9c0EkdVLetBuaKzC .image-shape .label,#mermaid-svg-9c0EkdVLetBuaKzC .icon-shape .label{text-align:center;}#mermaid-svg-9c0EkdVLetBuaKzC .node.clickable{cursor:pointer;}#mermaid-svg-9c0EkdVLetBuaKzC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9c0EkdVLetBuaKzC .arrowheadPath{fill:#333333;}#mermaid-svg-9c0EkdVLetBuaKzC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9c0EkdVLetBuaKzC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9c0EkdVLetBuaKzC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9c0EkdVLetBuaKzC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9c0EkdVLetBuaKzC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9c0EkdVLetBuaKzC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9c0EkdVLetBuaKzC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9c0EkdVLetBuaKzC .cluster text{fill:#333;}#mermaid-svg-9c0EkdVLetBuaKzC .cluster span{color:#333;}#mermaid-svg-9c0EkdVLetBuaKzC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9c0EkdVLetBuaKzC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9c0EkdVLetBuaKzC rect.text{fill:none;stroke-width:0;}#mermaid-svg-9c0EkdVLetBuaKzC .icon-shape,#mermaid-svg-9c0EkdVLetBuaKzC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9c0EkdVLetBuaKzC .icon-shape p,#mermaid-svg-9c0EkdVLetBuaKzC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9c0EkdVLetBuaKzC .icon-shape .label rect,#mermaid-svg-9c0EkdVLetBuaKzC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9c0EkdVLetBuaKzC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9c0EkdVLetBuaKzC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9c0EkdVLetBuaKzC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LangGraph State 核心
长期记忆层 - Store
短期记忆层 - Checkpointer
thread-scoped
线程作用域
自动保存状态快照
支持中断恢复/时间旅行
每个Superstep自动触发
跨线程共享
namespace组织
显式读写操作
语义搜索/向量检索
持久化JSON文档存储
State Graph
状态流转
节点/边执行
消息传递
1.1 两种记忆机制的定位
| 维度 | 短期记忆(Checkpointer) | 长期记忆(Store) |
|---|---|---|
| 核心定位 | 线程级状态持久化,实现"会话记忆" | 跨会话数据持久化,实现"用户知识" |
| 作用域 | 限定在单个thread_id内 | 跨多个thread_id,任意线程可访问 |
| 触发方式 | 自动:每个Superstep自动保存 | 显式:通过put/get/search方法调用 |
| 存储内容 | 对话历史、状态通道值、中间结果 | 用户偏好、知识库、应用配置 |
| 生命周期 | 与线程绑定,线程结束则记忆失效 | 独立持久化存储,与线程无关 |
| 类比 | 浏览器的Session Cookie | 用户配置文件数据库 |
二、短期记忆(STM):通过Checkpointer实现的线程级状态
短期记忆让你的AI智能体能够在单个线程或对话中记住之前的交互。LangGraph将短期记忆作为智能体状态的一部分进行管理,并通过检查点(Checkpointer) 持久化到数据库中。
2.1 Checkpointer核心机制
Checkpointer是LangGraph短期记忆的引擎,其工作模式是自动化的:每当图执行一步(例如调用一个节点函数),checkpointer就会自动将图的当前完整状态保存为一个快照,即"检查点"(Checkpoint)。
#mermaid-svg-REXwGqEdCxDefj5I{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-REXwGqEdCxDefj5I .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-REXwGqEdCxDefj5I .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-REXwGqEdCxDefj5I .error-icon{fill:#552222;}#mermaid-svg-REXwGqEdCxDefj5I .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-REXwGqEdCxDefj5I .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-REXwGqEdCxDefj5I .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-REXwGqEdCxDefj5I .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-REXwGqEdCxDefj5I .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-REXwGqEdCxDefj5I .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-REXwGqEdCxDefj5I .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-REXwGqEdCxDefj5I .marker{fill:#333333;stroke:#333333;}#mermaid-svg-REXwGqEdCxDefj5I .marker.cross{stroke:#333333;}#mermaid-svg-REXwGqEdCxDefj5I svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-REXwGqEdCxDefj5I p{margin:0;}#mermaid-svg-REXwGqEdCxDefj5I .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-REXwGqEdCxDefj5I .cluster-label text{fill:#333;}#mermaid-svg-REXwGqEdCxDefj5I .cluster-label span{color:#333;}#mermaid-svg-REXwGqEdCxDefj5I .cluster-label span p{background-color:transparent;}#mermaid-svg-REXwGqEdCxDefj5I .label text,#mermaid-svg-REXwGqEdCxDefj5I span{fill:#333;color:#333;}#mermaid-svg-REXwGqEdCxDefj5I .node rect,#mermaid-svg-REXwGqEdCxDefj5I .node circle,#mermaid-svg-REXwGqEdCxDefj5I .node ellipse,#mermaid-svg-REXwGqEdCxDefj5I .node polygon,#mermaid-svg-REXwGqEdCxDefj5I .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-REXwGqEdCxDefj5I .rough-node .label text,#mermaid-svg-REXwGqEdCxDefj5I .node .label text,#mermaid-svg-REXwGqEdCxDefj5I .image-shape .label,#mermaid-svg-REXwGqEdCxDefj5I .icon-shape .label{text-anchor:middle;}#mermaid-svg-REXwGqEdCxDefj5I .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-REXwGqEdCxDefj5I .rough-node .label,#mermaid-svg-REXwGqEdCxDefj5I .node .label,#mermaid-svg-REXwGqEdCxDefj5I .image-shape .label,#mermaid-svg-REXwGqEdCxDefj5I .icon-shape .label{text-align:center;}#mermaid-svg-REXwGqEdCxDefj5I .node.clickable{cursor:pointer;}#mermaid-svg-REXwGqEdCxDefj5I .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-REXwGqEdCxDefj5I .arrowheadPath{fill:#333333;}#mermaid-svg-REXwGqEdCxDefj5I .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-REXwGqEdCxDefj5I .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-REXwGqEdCxDefj5I .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-REXwGqEdCxDefj5I .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-REXwGqEdCxDefj5I .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-REXwGqEdCxDefj5I .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-REXwGqEdCxDefj5I .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-REXwGqEdCxDefj5I .cluster text{fill:#333;}#mermaid-svg-REXwGqEdCxDefj5I .cluster span{color:#333;}#mermaid-svg-REXwGqEdCxDefj5I div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-REXwGqEdCxDefj5I .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-REXwGqEdCxDefj5I rect.text{fill:none;stroke-width:0;}#mermaid-svg-REXwGqEdCxDefj5I .icon-shape,#mermaid-svg-REXwGqEdCxDefj5I .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-REXwGqEdCxDefj5I .icon-shape p,#mermaid-svg-REXwGqEdCxDefj5I .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-REXwGqEdCxDefj5I .icon-shape .label rect,#mermaid-svg-REXwGqEdCxDefj5I .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-REXwGqEdCxDefj5I .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-REXwGqEdCxDefj5I .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-REXwGqEdCxDefj5I :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 中断恢复流程
服务重启/中断
通过thread_id加载
恢复上次状态
从断点继续
短期记忆执行流程
是
否
用户输入
图执行Step
有Checkpointer?
自动保存状态快照
持久化到存储
继续下一步
状态丢失
2.2 thread_id:区分对话的"会话钥匙"
thread_id是激活和区分不同对话线程的唯一钥匙。在调用图的invoke或stream方法时,通过configurable字典传入一个thread_id,就等于告诉LangGraph:"这次操作属于这个特定的对话"。
#mermaid-svg-h8zLklgGGNSbOLSs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h8zLklgGGNSbOLSs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h8zLklgGGNSbOLSs .error-icon{fill:#552222;}#mermaid-svg-h8zLklgGGNSbOLSs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h8zLklgGGNSbOLSs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h8zLklgGGNSbOLSs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h8zLklgGGNSbOLSs .marker.cross{stroke:#333333;}#mermaid-svg-h8zLklgGGNSbOLSs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h8zLklgGGNSbOLSs p{margin:0;}#mermaid-svg-h8zLklgGGNSbOLSs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-h8zLklgGGNSbOLSs .cluster-label text{fill:#333;}#mermaid-svg-h8zLklgGGNSbOLSs .cluster-label span{color:#333;}#mermaid-svg-h8zLklgGGNSbOLSs .cluster-label span p{background-color:transparent;}#mermaid-svg-h8zLklgGGNSbOLSs .label text,#mermaid-svg-h8zLklgGGNSbOLSs span{fill:#333;color:#333;}#mermaid-svg-h8zLklgGGNSbOLSs .node rect,#mermaid-svg-h8zLklgGGNSbOLSs .node circle,#mermaid-svg-h8zLklgGGNSbOLSs .node ellipse,#mermaid-svg-h8zLklgGGNSbOLSs .node polygon,#mermaid-svg-h8zLklgGGNSbOLSs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h8zLklgGGNSbOLSs .rough-node .label text,#mermaid-svg-h8zLklgGGNSbOLSs .node .label text,#mermaid-svg-h8zLklgGGNSbOLSs .image-shape .label,#mermaid-svg-h8zLklgGGNSbOLSs .icon-shape .label{text-anchor:middle;}#mermaid-svg-h8zLklgGGNSbOLSs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h8zLklgGGNSbOLSs .rough-node .label,#mermaid-svg-h8zLklgGGNSbOLSs .node .label,#mermaid-svg-h8zLklgGGNSbOLSs .image-shape .label,#mermaid-svg-h8zLklgGGNSbOLSs .icon-shape .label{text-align:center;}#mermaid-svg-h8zLklgGGNSbOLSs .node.clickable{cursor:pointer;}#mermaid-svg-h8zLklgGGNSbOLSs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h8zLklgGGNSbOLSs .arrowheadPath{fill:#333333;}#mermaid-svg-h8zLklgGGNSbOLSs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h8zLklgGGNSbOLSs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h8zLklgGGNSbOLSs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h8zLklgGGNSbOLSs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h8zLklgGGNSbOLSs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h8zLklgGGNSbOLSs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h8zLklgGGNSbOLSs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h8zLklgGGNSbOLSs .cluster text{fill:#333;}#mermaid-svg-h8zLklgGGNSbOLSs .cluster span{color:#333;}#mermaid-svg-h8zLklgGGNSbOLSs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h8zLklgGGNSbOLSs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h8zLklgGGNSbOLSs rect.text{fill:none;stroke-width:0;}#mermaid-svg-h8zLklgGGNSbOLSs .icon-shape,#mermaid-svg-h8zLklgGGNSbOLSs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h8zLklgGGNSbOLSs .icon-shape p,#mermaid-svg-h8zLklgGGNSbOLSs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h8zLklgGGNSbOLSs .icon-shape .label rect,#mermaid-svg-h8zLklgGGNSbOLSs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h8zLklgGGNSbOLSs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h8zLklgGGNSbOLSs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h8zLklgGGNSbOLSs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程隔离示意
隔离
隔离
thread_id: user_001
状态1
对话历史A
thread_id: user_002
状态2
对话历史B
thread_id: user_003
状态3
对话历史C
概念类比:可以将checkpointer和thread_id的关系类比为网站的会话(Session)Cookie。当您登录一个网站后,浏览器会保存一个会话Cookie,网站通过这个Cookie识别您的身份,从而保持您的登录状态。同样,thread_id让LangGraph智能体在多轮对话中能够"记住"您是谁以及你们正在聊什么。
2.3 短期记忆的代码实现
基本用法:
python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, MessagesState
# 1. 创建 checkpointer
checkpointer = InMemorySaver()
# 2. 编译图时启用 checkpointer
graph = builder.compile(checkpointer=checkpointer)
# 3. 第一次对话 - 指定 thread_id
config = {"configurable": {"thread_id": "user_123"}}
graph.invoke({"messages": [("user", "我叫张三")]}, config)
# 4. 第二次对话 - 同一个 thread_id,可以访问之前的消息
graph.invoke({"messages": [("user", "我刚才说我叫什么?")]}, config)
# Agent 可以回答:"你刚才说你叫张三"
生产环境使用数据库Checkpointer:
python
from langgraph.checkpoint.postgres import PostgresSaver
DB_URI = "postgresql://user:password@localhost:5432/dbname"
checkpointer = PostgresSaver.from_conn_string(DB_URI)
# 首次使用时需要初始化表结构
# await checkpointer.setup()
graph = builder.compile(checkpointer=checkpointer)
2.4 Checkpointer的存储后端矩阵
LangGraph提供多种checkpointer实现,可根据场景选择:
| 实现类 | 存储介质 | 适用场景 | 性能特征 |
|---|---|---|---|
MemorySaver |
内存 | 开发测试、单元测试 | 访问延迟<1ms |
FileCheckpointer |
本地文件系统 | 单机生产环境 | 吞吐量约50MB/s |
SqliteSaver |
SQLite数据库 | 小规模部署 | 支持ACID事务 |
PostgresSaver |
PostgreSQL | 生产环境、高可用需求 | 分布式持久化 |
RedisSaver |
Redis集群 | 分布式系统、低延迟场景 | 跨节点同步延迟<10ms |
注意 :使用Postgres等数据库checkpointer前,需要调用
setup()方法创建必要的数据库表结构。
三、长期记忆(LTM):通过Store实现的跨会话持久化
与专注于单次对话的短期记忆不同,长期记忆旨在存储跨越不同对话的用户特定或应用级别的数据。这是智能体积累知识、形成对用户长期认知的地方。
3.1 Store核心机制
Store本质上是一个暴露给图节点和工具的键值数据库 。与checkpointer的自动化快照不同,对Store的操作是显式和主动的 ------开发者需要在节点或工具的逻辑中,通过调用store.put()、store.get()或store.search()等方法来精确地读写信息。
3.2 Namespace:分层组织记忆的目录结构
Store使用层次化的命名空间来组织数据。命名空间是一个元组(类似文件夹路径),用于实现数据隔离和高效检索:
python
# 用户数据存储
["users", "user_123", "preferences"] # 用户偏好
["users", "user_123", "history"] # 用户历史
["users", "user_123", "settings"] # 用户设置
# 应用数据存储
["app", "config", "version"] # 应用配置
["app", "knowledge", "faq"] # 知识库FAQ
# 组织级数据存储
["orgs", "org_456", "members"] # 组织成员
["orgs", "org_456", "policies"] # 组织策略
#mermaid-svg-Bhhb8NnuMlGnuPfy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bhhb8NnuMlGnuPfy .error-icon{fill:#552222;}#mermaid-svg-Bhhb8NnuMlGnuPfy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bhhb8NnuMlGnuPfy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .marker.cross{stroke:#333333;}#mermaid-svg-Bhhb8NnuMlGnuPfy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bhhb8NnuMlGnuPfy p{margin:0;}#mermaid-svg-Bhhb8NnuMlGnuPfy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .cluster-label text{fill:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .cluster-label span{color:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .cluster-label span p{background-color:transparent;}#mermaid-svg-Bhhb8NnuMlGnuPfy .label text,#mermaid-svg-Bhhb8NnuMlGnuPfy span{fill:#333;color:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .node rect,#mermaid-svg-Bhhb8NnuMlGnuPfy .node circle,#mermaid-svg-Bhhb8NnuMlGnuPfy .node ellipse,#mermaid-svg-Bhhb8NnuMlGnuPfy .node polygon,#mermaid-svg-Bhhb8NnuMlGnuPfy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .rough-node .label text,#mermaid-svg-Bhhb8NnuMlGnuPfy .node .label text,#mermaid-svg-Bhhb8NnuMlGnuPfy .image-shape .label,#mermaid-svg-Bhhb8NnuMlGnuPfy .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bhhb8NnuMlGnuPfy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .rough-node .label,#mermaid-svg-Bhhb8NnuMlGnuPfy .node .label,#mermaid-svg-Bhhb8NnuMlGnuPfy .image-shape .label,#mermaid-svg-Bhhb8NnuMlGnuPfy .icon-shape .label{text-align:center;}#mermaid-svg-Bhhb8NnuMlGnuPfy .node.clickable{cursor:pointer;}#mermaid-svg-Bhhb8NnuMlGnuPfy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .arrowheadPath{fill:#333333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bhhb8NnuMlGnuPfy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bhhb8NnuMlGnuPfy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bhhb8NnuMlGnuPfy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bhhb8NnuMlGnuPfy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .cluster text{fill:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy .cluster span{color:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bhhb8NnuMlGnuPfy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bhhb8NnuMlGnuPfy rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bhhb8NnuMlGnuPfy .icon-shape,#mermaid-svg-Bhhb8NnuMlGnuPfy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bhhb8NnuMlGnuPfy .icon-shape p,#mermaid-svg-Bhhb8NnuMlGnuPfy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bhhb8NnuMlGnuPfy .icon-shape .label rect,#mermaid-svg-Bhhb8NnuMlGnuPfy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bhhb8NnuMlGnuPfy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bhhb8NnuMlGnuPfy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bhhb8NnuMlGnuPfy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命名空间树形结构
根命名空间
users
user_123
user_456
preferences
history
settings
app
config
knowledge
faq
orgs
org_456
members
这种设计实现了三大优势:权限隔离 (不同业务模块访问独立命名空间)、版本控制 (通过时间戳实现记忆快照管理)和高效检索(构建空间索引提升查询性能)。
3.3 长期记忆的代码实现
基本用法:
python
from langgraph.store.memory import InMemoryStore
# 1. 创建 store
store = InMemoryStore()
# 2. 保存用户偏好(跨会话)
await store.aput(
["users", "user_123", "preferences"],
{"theme": "dark", "language": "zh-CN"}
)
# 3. 在任何线程中都可以访问
preferences = await store.aget(["users", "user_123", "preferences"])
print(preferences) # {"theme": "dark", "language": "zh-CN"}
将Store集成到Agent中:
python
from langchain.agents import create_agent
# 创建带有长期记忆的agent
store = InMemoryStore()
agent = create_agent(
"claude-sonnet-4-6",
tools=[],
store=store, # Store会自动注入到节点的Runtime对象中
)
LangGraph会自动将store注入到节点函数中,推荐通过
Runtime对象访问。
3.4 语义记忆与向量检索
LangGraph的Store支持基于嵌入向量的语义搜索,让长期记忆能够智能检索相关内容。
配置支持语义搜索的Store:
python
# 开发环境:内存存储 + 嵌入支持
from langgraph.store.memory import InMemoryStore
def embed(texts):
# 实际环境中应使用openai:text-embedding-3-small等模型
return [[0.1, 0.2, ...] for _ in texts]
store = InMemoryStore(index={
"dims": 1536, # 嵌入向量维度
"embed": embed # 嵌入函数
})
# 生产环境:使用PostgreSQL + 向量扩展
from langgraph.store.postgres import AsyncPostgresStore
store = AsyncPostgresStore(
connection_string="postgresql://user:pass@host:5432/db",
index={
"dims": 1536,
"embed": "openai:text-embedding-3-small"
}
)
语义搜索查询:
python
# 使用语义搜索查找相关记忆
namespace = ("memories", "user_123")
results = store.search(
namespace,
query="用户关于黑暗模式的偏好",
limit=5
)
3.5 LangMem:进阶的记忆管理
LangMem是建立在LangGraph BaseStore之上的高级记忆管理SDK,支持三种核心记忆模式:
| 模式 | 用途 | 命名空间示例 | 特性 |
|---|---|---|---|
| Collection | 语义记忆、情景记忆 | ("memories", "{user_id}") |
多文档,支持搜索和更新 |
| Profile | 用户画像、系统配置 | ("profile", "{user_id}") |
单文档,原地更新 |
| Rules | 行为规则、指令约束 | ("rules", "system") |
静态配置,几乎无变更 |
python
from langmem import create_memory_manager
# 创建记忆管理器 - Collection模式
manager = create_memory_manager(
"anthropic:claude-3-5-sonnet-latest",
namespace=("memories", "{langgraph_user_id}"),
enable_inserts=True, # 允许添加新记忆
enable_deletes=True # 允许删除过期记忆
)
四、短期记忆与长期记忆的核心对比
#mermaid-svg-C6Q30D7j8Lxw4iVa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-C6Q30D7j8Lxw4iVa .error-icon{fill:#552222;}#mermaid-svg-C6Q30D7j8Lxw4iVa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-C6Q30D7j8Lxw4iVa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .marker.cross{stroke:#333333;}#mermaid-svg-C6Q30D7j8Lxw4iVa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-C6Q30D7j8Lxw4iVa p{margin:0;}#mermaid-svg-C6Q30D7j8Lxw4iVa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .cluster-label text{fill:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .cluster-label span{color:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .cluster-label span p{background-color:transparent;}#mermaid-svg-C6Q30D7j8Lxw4iVa .label text,#mermaid-svg-C6Q30D7j8Lxw4iVa span{fill:#333;color:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .node rect,#mermaid-svg-C6Q30D7j8Lxw4iVa .node circle,#mermaid-svg-C6Q30D7j8Lxw4iVa .node ellipse,#mermaid-svg-C6Q30D7j8Lxw4iVa .node polygon,#mermaid-svg-C6Q30D7j8Lxw4iVa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .rough-node .label text,#mermaid-svg-C6Q30D7j8Lxw4iVa .node .label text,#mermaid-svg-C6Q30D7j8Lxw4iVa .image-shape .label,#mermaid-svg-C6Q30D7j8Lxw4iVa .icon-shape .label{text-anchor:middle;}#mermaid-svg-C6Q30D7j8Lxw4iVa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .rough-node .label,#mermaid-svg-C6Q30D7j8Lxw4iVa .node .label,#mermaid-svg-C6Q30D7j8Lxw4iVa .image-shape .label,#mermaid-svg-C6Q30D7j8Lxw4iVa .icon-shape .label{text-align:center;}#mermaid-svg-C6Q30D7j8Lxw4iVa .node.clickable{cursor:pointer;}#mermaid-svg-C6Q30D7j8Lxw4iVa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .arrowheadPath{fill:#333333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C6Q30D7j8Lxw4iVa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-C6Q30D7j8Lxw4iVa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C6Q30D7j8Lxw4iVa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-C6Q30D7j8Lxw4iVa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .cluster text{fill:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa .cluster span{color:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-C6Q30D7j8Lxw4iVa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-C6Q30D7j8Lxw4iVa rect.text{fill:none;stroke-width:0;}#mermaid-svg-C6Q30D7j8Lxw4iVa .icon-shape,#mermaid-svg-C6Q30D7j8Lxw4iVa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C6Q30D7j8Lxw4iVa .icon-shape p,#mermaid-svg-C6Q30D7j8Lxw4iVa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-C6Q30D7j8Lxw4iVa .icon-shape .label rect,#mermaid-svg-C6Q30D7j8Lxw4iVa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C6Q30D7j8Lxw4iVa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-C6Q30D7j8Lxw4iVa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-C6Q30D7j8Lxw4iVa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 长期记忆层
短期记忆层
会话1
会话2
会话3
跨线程访问
上下文注入
thread_id: user_001
对话历史
对话历史
对话历史
user_id: user_001
namespace: users/user_001/preferences
namespace: users/user_001/history
namespace: users/user_001/settings
| 对比维度 | 短期记忆 | 长期记忆 |
|---|---|---|
| 核心组件 | Checkpointer | Store |
| 作用域标识 | thread_id |
namespace(通常含user_id) |
| 操作方式 | 自动(每个superstep) | 显式(put/get/search方法) |
| 状态类型 | 状态通道值(对话历史、中间结果) | JSON文档(用户偏好、知识库) |
| 存储后端 | MemorySaver/PostgresSaver/RedisSaver | InMemoryStore/PostgresStore |
| 检索能力 | 基于thread_id精确加载 | 精确匹配 + 语义向量搜索 |
| 生命周期 | 线程绑定 | 独立持久化 |
从存储底层看,两者共享类似的后端选择,但数据模型和访问模式截然不同 :Checkpointer按superstep自动保存状态通道值,Store按namespace显式管理JSON文档;Postgres既可以作为Checkpointer的存储后端用于短期记忆,也可以作为Store的后端用于长期记忆,但使用方式和意图不同。
五、完整实战:构建有记忆的AI客服系统
下面我们将构建一个具备长短期记忆能力的AI客服系统,实现:
- 用短期记忆跟踪当前对话上下文
- 用长期记忆存储和检索用户偏好
5.1 完整架构流程图
#mermaid-svg-FSY1HhkddH4wVpsA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FSY1HhkddH4wVpsA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FSY1HhkddH4wVpsA .error-icon{fill:#552222;}#mermaid-svg-FSY1HhkddH4wVpsA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FSY1HhkddH4wVpsA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FSY1HhkddH4wVpsA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FSY1HhkddH4wVpsA .marker.cross{stroke:#333333;}#mermaid-svg-FSY1HhkddH4wVpsA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FSY1HhkddH4wVpsA p{margin:0;}#mermaid-svg-FSY1HhkddH4wVpsA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FSY1HhkddH4wVpsA .cluster-label text{fill:#333;}#mermaid-svg-FSY1HhkddH4wVpsA .cluster-label span{color:#333;}#mermaid-svg-FSY1HhkddH4wVpsA .cluster-label span p{background-color:transparent;}#mermaid-svg-FSY1HhkddH4wVpsA .label text,#mermaid-svg-FSY1HhkddH4wVpsA span{fill:#333;color:#333;}#mermaid-svg-FSY1HhkddH4wVpsA .node rect,#mermaid-svg-FSY1HhkddH4wVpsA .node circle,#mermaid-svg-FSY1HhkddH4wVpsA .node ellipse,#mermaid-svg-FSY1HhkddH4wVpsA .node polygon,#mermaid-svg-FSY1HhkddH4wVpsA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FSY1HhkddH4wVpsA .rough-node .label text,#mermaid-svg-FSY1HhkddH4wVpsA .node .label text,#mermaid-svg-FSY1HhkddH4wVpsA .image-shape .label,#mermaid-svg-FSY1HhkddH4wVpsA .icon-shape .label{text-anchor:middle;}#mermaid-svg-FSY1HhkddH4wVpsA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FSY1HhkddH4wVpsA .rough-node .label,#mermaid-svg-FSY1HhkddH4wVpsA .node .label,#mermaid-svg-FSY1HhkddH4wVpsA .image-shape .label,#mermaid-svg-FSY1HhkddH4wVpsA .icon-shape .label{text-align:center;}#mermaid-svg-FSY1HhkddH4wVpsA .node.clickable{cursor:pointer;}#mermaid-svg-FSY1HhkddH4wVpsA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FSY1HhkddH4wVpsA .arrowheadPath{fill:#333333;}#mermaid-svg-FSY1HhkddH4wVpsA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FSY1HhkddH4wVpsA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FSY1HhkddH4wVpsA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FSY1HhkddH4wVpsA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FSY1HhkddH4wVpsA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FSY1HhkddH4wVpsA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FSY1HhkddH4wVpsA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FSY1HhkddH4wVpsA .cluster text{fill:#333;}#mermaid-svg-FSY1HhkddH4wVpsA .cluster span{color:#333;}#mermaid-svg-FSY1HhkddH4wVpsA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FSY1HhkddH4wVpsA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FSY1HhkddH4wVpsA rect.text{fill:none;stroke-width:0;}#mermaid-svg-FSY1HhkddH4wVpsA .icon-shape,#mermaid-svg-FSY1HhkddH4wVpsA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FSY1HhkddH4wVpsA .icon-shape p,#mermaid-svg-FSY1HhkddH4wVpsA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FSY1HhkddH4wVpsA .icon-shape .label rect,#mermaid-svg-FSY1HhkddH4wVpsA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FSY1HhkddH4wVpsA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FSY1HhkddH4wVpsA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FSY1HhkddH4wVpsA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 处理流程
长期记忆层
短期记忆层
用户交互层
是
否
用户输入
AI客服Agent
获取thread状态
Checkpointer加载上次对话
对话上下文注入
检索用户记忆
Store语义搜索
用户偏好/历史信息
LLM推理
需要更新记忆?
写入Store
输出回复
Checkpointer保存新状态
5.2 完整代码示例
python
import asyncio
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore
from typing import Literal
# 1. 配置嵌入函数(实际应调用OpenAI或本地嵌入模型)
def simple_embed(texts):
# 简化示例,生产环境请使用真实的嵌入模型
return [[hash(t[:10]) % 1000 / 1000.0] for t in texts]
# 2. 创建短期记忆(Checkpointer)和长期记忆(Store)
short_term_memory = InMemorySaver()
long_term_memory = InMemoryStore(
index={"dims": 1, "embed": simple_embed}
)
# 3. 定义节点函数(Runtime自动注入store)
async def call_model(state: MessagesState, runtime):
"""调用LLM的核心节点,同时集成长期记忆"""
user_id = runtime.config["configurable"].get("user_id", "unknown")
# 从Store检索用户长期记忆
memories = await runtime.store.asearch(
["memories", user_id],
query=state["messages"][-1].content if state["messages"] else "",
limit=5
)
# 构建包含记忆的prompt
memory_context = "\n".join([m.dict() for m in memories]) if memories else ""
messages = state["messages"]
# 实际调用LLM...
# response = llm.invoke(messages)
return {"messages": messages + [("assistant", "AI回复内容")]}
async def update_memories(state: MessagesState, runtime):
"""更新长期记忆节点"""
user_id = runtime.config["configurable"].get("user_id", "unknown")
last_message = state["messages"][-1]
# 分析并提取需要长期记忆的信息
# 实际应使用LLM判断是否需要记忆
# 示例:存储用户偏好
if "偏好" in last_message.content:
await runtime.store.aput(
["memories", user_id, "preferences"],
{"content": last_message.content, "timestamp": "..."}
)
return state
# 4. 构建图并集成记忆
builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_node("update_memories", update_memories)
builder.add_edge(START, "call_model")
builder.add_edge("call_model", "update_memories")
builder.add_edge("update_memories", END)
# 5. 编译图,同时传入checkpointer和store
graph = builder.compile(
checkpointer=short_term_memory,
store=long_term_memory
)
# 6. 使用:传入thread_id(短期记忆)和user_id(长期记忆)配置
config = {
"configurable": {
"thread_id": "session_001", # 用于短期记忆隔离
"user_id": "user_123" # 用于长期记忆命名空间
}
}
# 运行
async def main():
# 第一轮对话
await graph.ainvoke(
{"messages": [("user", "我喜欢简洁的回答风格")]},
config
)
# 第二轮对话,短期记忆保留上下文,长期记忆已保存偏好
result = await graph.ainvoke(
{"messages": [("user", "你还记得我喜欢的回答风格吗?")]},
config
)
print(result)
# asyncio.run(main())
六、生产级部署建议
6.1 存储后端选型指南
| 部署规模 | 短期记忆(Checkpointer) | 长期记忆(Store) |
|---|---|---|
| 开发/测试 | MemorySaver(内存) |
InMemoryStore(内存) |
| 单机生产 | PostgresSaver |
PostgresStore |
| 分布式高可用 | RedisSaver |
AsyncPostgresStore + 集群 |
| 超大规模 | 分库分表 + 自定义实现 | 向量数据库(如Pinecone/Milvus) |
实测表明,合理配置的检查点可将系统故障恢复时间从分钟级降至秒级。
6.2 安全审计与权限隔离
python
# 使用命名空间实现数据隔离
class SecurityAudit:
def get_user_namespace(self, user_id: str, permission: str):
"""基于RBAC构建命名空间"""
if permission == "read":
return ["read", user_id] # 只读命名空间
elif permission == "write":
return ["write", user_id] # 读写命名空间
else:
raise PermissionError("无权访问")
# 三级命名空间体系示例
memory_config = {
"user_profile": {
"basic_info": {}, # 一级:用户身份
"preferences": {} # 二级:用户偏好
},
"session_context": {
"current_intent": "", # 会话级记忆
"last_response": ""
}
}
6.3 TTL自动清理策略
json
{
"store": {
"ttl": 2592000 // 30天过期,单位:秒
}
}
配置TTL策略可自动管理数据生命周期,防止存储无限累积。
6.4 性能优化建议
- 缓存策略:使用LangMem的语义缓存(Redis LangCache)减少LLM API调用,可将Token消耗降低30%-50%
- 滑动窗口机制:短期记忆默认保留最近8-16轮对话,建议根据业务场景调优窗口大小
- 混合检索:结构化数据用PostgreSQL,非结构化数据用向量数据库,结合语义搜索和精确匹配
七、总结
LangGraph通过Checkpointer(短期记忆) 和Store(长期记忆) 的巧妙设计,为AI智能体提供了工业级的记忆管理方案:
| 记忆类型 | 核心组件 | 作用域标识 | 操作方式 | 核心用途 |
|---|---|---|---|---|
| 短期记忆 | Checkpointer | thread_id |
自动保存每个Superstep | 多轮对话上下文连续性 |
| 长期记忆 | Store | namespace(如user_id) |
显式put/get/search | 用户偏好跨会话持久化 |
两者的核心差异在于:短期记忆回答"这次对话我们说了什么",长期记忆回答"关于这个用户我们都知道什么"。通过将短期记忆(Checkpointer)和长期记忆(Store)同时编译到图中,您可以构建既能维持多轮对话连贯性、又能跨会话沉淀用户知识的智能体系统------这正是LangGraph从"流程编排框架"进化为"智能体操作系统"的关键一步。