ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

一文理解 LangChain 长期记忆:Store、跨会话与用户画像

一文理解 LangChain 长期记忆:Store、跨会话与用户画像 上一篇介绍短期记忆时我们讨论的范围始终没有离开 Thread在 Thread 中State 通过 Checkpointer 保存下一次调用再根据 thread_id 恢复。但实际应用很快会遇到新的问题。比如用户结束当前会话下一次重新创建一个 Thread。Agent 还应该知道他习惯使用 Python、喜欢简洁回答或者之前已经告诉过系统自己的项目背景。这些信息显然不适合绑定在某个 Thread 上。LangChain 把这类需要跨 Thread、跨会话继续存在的数据归入长期记忆Long-term Memory。它不再主要依赖 Checkpointer而是建立在 LangGraph 的 Store 之上。一、长期记忆的定位先看一个简单场景。用户在一次会话中告诉 Agent我主要使用 Python 开发。回答代码问题时尽量直接一些。几天之后用户创建了新的会话帮我写一个读取 CSV 的例子。虽然已经换了 Thread但系统仍然应知道或记住用户的偏好主要语言 Python回答风格 concise这类信息描述的是用户长期属性而不是当前会话的执行状态。因此它更适合存入长期记忆User │ ├── Thread A │ ├── Thread B │ └── Long-term Memory ↓ Store多个 Thread 可以相互独立但只要它们属于同一个用户就可以访问相同的长期数据。这也是长期记忆最重要的能力让数据脱离单个 Thread 的生命周期并在多个会话之间共享。长期记忆保存的内容通常包括用户画像用户偏好历史事实项目背景Agent 学到的规则过去的重要事件它们是否需要长期存在取决于业务而不是由也不应由 LangChain 自动决定。二、Store 与 CheckpointerLangChain 中有两个都与“保存数据”有关的组件CheckpointerStore两者很容易混淆但职责并不相同。通过上一篇短期记忆的学习我们已了解 Checkpointer 保存的是某个 Thread 的 Agent State。例如messagescurrent_steptool resultinterruptnext node...这些数据共同描述这段会话现在是什么状态Store 保存的则是独立于具体 Thread 的长期数据。例如用户喜欢 Python用户偏好简洁回答用户当前项目使用 FastAPI用户选择了深色主题它回答的是有哪些信息应该在不同会话之间继续存在可以把两者理解为两条数据链路Agent 执行 ↓State ↓Checkpoint ↓Checkpointer以及长期信息 ↓Namespace ↓Key ↓Value ↓Store一个 Agent 可以同时使用两者agent create_agent( model..., tools..., checkpointercheckpointer, storestore,)此时Checkpointer 负责当前会话状态Store 负责跨会话信息。它们解决的是两个层级的问题。三、Store 的数据模型Store 并不是简单的全局字典。LangGraph Store 中最重要的三个概念是NamespaceKeyValue一条数据可以表示为Namespace Key → Value例如namespace (users, u1001, profile) key basic value { name: Bob, language: Python,}可以先做一个不完全严格、但容易理解的类比Namespace ≈ 目录 Key ≈ 文件名 Value ≈ 文件内容Store 中实际保存的单位称为 Item。一个 Item 除了业务数据之外还会包含namespacekeyvaluecreated_atupdated_at因此Store 保存的一条数据属于哪个 Namespace、对应哪个 Key、它的 Value以及创建和更新时间。Namespace 划分数据范围Namespace 是字符串组成的元组(users, u1001)也可以继续增加层级(users, u1001, profile)(users, u1001, preferences)(users, u1001, memories)这种设计比固定的一层 Key 更适合长期数据。例如一个系统同时需要保存用户记忆组织知识Agent 指令项目上下文就可以分别设计成(users, u1001, memories) (organizations, org-01, knowledge) (agents, support-agent, instructions) (projects, project-100, context)因此Namespace 更像一个逻辑作用域。它决定一组 Item 属于谁、属于哪类数据。实际设计中比“Key 起什么名字”更应该先考虑 Namespace。Key 定位具体 ItemNamespace 确定范围后Key 用于定位这个范围中的具体记录。例如Namespace(users, u1001, preferences) Keylanguage Value{value: Python}同一个 Namespace 下还可以存在answer-stylethemetimezone于是形成(users, u1001, preferences) │ ├── language ├── answer-style ├── theme └── timezoneNamespace 与 Key 组合起来才真正确定一条数据。设计长期记忆时可以先回答三个问题这条数据属于谁 它属于哪一类信息 它在这一类数据中如何唯一识别前两个问题通常决定 Namespace最后一个问题决定 Key。四、Store 的基本操作和短期记录中的InMemorySaver一样LangGraph 提供InMemoryStore适合学习、测试和本地开发。from langgraph.store.memory import InMemoryStore store InMemoryStore()有了 Store 后可以进行最基本的写入、读取、搜索和删除。写入数据写入使用put()namespace (users, u1001, profile) store.put( namespace, basic, { name: Bob, language: Python, answer_style: concise, },)put()的基本结构是store.put( namespace, key, value,)其中namespace是字符串元组key是字符串value是一个可以 JSON 序列化的字典。如果相同的Namespace Key再次执行put()这条 Item 会被更新。例如store.put( namespace, basic, { name: Bob, language: Python, answer_style: detailed, },)这里不是创建第二个basic而是更新原有 Item。这非常适合保存固定结构的 Profile 或 Preferences。精确读取如果已经知道 Namespace 和 Key可以使用item store.get(namespace, basic) print(item.value)运行结果{name: Bob, language: Python, answer_style: detailed}从结果可以看到第二次put()已经更新了原有数据。如果指定的 Key 不存在get()返回None。因此在实际代码中通常需要判断item store.get(namespace, basic) if item is not None: print(item.value)get()适合我明确知道要读哪一条数据例如用户 Profile账户配置某个项目设置指定 Preference搜索一组数据如果不知道具体 Key可以使用search()items store.search(namespace) for item in items: print(item.key, item.value)例如 Namespace 中存在languageanswer-styletheme搜索后就可以返回这一范围中的多条 Item。这与store.get(namespace, key)的使用场景不同。get()是精确定位。search()是从一个 Namespace 中寻找一组符合条件的数据。删除数据如果某条长期信息已经失效可以使用store.delete( namespace, basic,)删除之后item store.get(namespace, basic) print(item)运行结果None长期记忆必须具备删除能力。因为长期保存不等于永久保存。如果只能不断写入无法删除旧事实和错误信息Memory 的质量会随着使用时间不断下降。五、Store 的搜索能力Store 与普通 Key-Value Storage 的一个重要区别是它可以进一步支持语义搜索。假设一个用户已经积累了很多长期信息用户主要使用 Python 和 FastAPI用户喜欢深色主题用户正在学习 LangChain用户使用 PostgreSQL用户希望技术回答尽量直接当前问题是我的后端项目应该怎么组织如果每次把全部 Memory 都放进 Prompt数据越积越多上下文就会不断膨胀。更合理的方式是当前问题 ↓搜索相关 Memory ↓选择少量相关记录 ↓加入模型上下文Store 可以配置 Embedding使搜索从简单的 Namespace 查询进一步变成语义检索。例如from langchain.embeddings import init_embeddingsfrom langgraph.store.memory import InMemoryStore embeddings init_embeddings( openai:text-embedding-3-small) store InMemoryStore( index{ embed: embeddings, dims: 1536, })随后写入几条记忆namespace (users, u1001, memories) store.put( namespace, memory-1, { text: 用户主要使用 Python 和 FastAPI 开发后端 },) store.put( namespace, memory-2, { text: 用户喜欢深色界面 },) store.put( namespace, memory-3, { text: 用户经常使用 PostgreSQL },)然后进行语义搜索results store.search( namespace, query后端开发技术栈, limit2,) for item in results: print(item.value)示例输出{text: 用户主要使用 Python 和 FastAPI 开发后端}{text: 用户经常使用 PostgreSQL}从结果可以看到搜索并不依赖 Key 是否包含“后端”这个词。Embedding 会把当前 Query 与 Store 中的数据转换成向量再根据语义相关性寻找更合适的 Item。需要注意InMemoryStore默认并不会自动具备语义搜索能力。必须配置indexembeddims等索引相关信息。因此Store 可以有两种常见使用方式已知 Key ↓get() 不知道具体 Key ↓search()如果长期数据规模较小而且结构稳定精确读取已经足够。如果 Memory 是不断增加的 Collection通常更适合使用搜索。六、跨 Thread 共享数据长期记忆真正进入 Agent 后需要解决另一个问题当前是谁在访问 Store一种常见方式是通过 Runtime Context 传入user_id。例如定义from dataclasses import dataclass dataclassclass Context: user_id: str随后在 Tool 中通过ToolRuntime获得当前 Runtime 和 Storefrom typing_extensions import TypedDictfrom langchain.tools import ToolRuntime, tool class UserInfo(TypedDict): name: str tooldef save_user_info( user_info: UserInfo, runtime: ToolRuntime[Context],) - str: Save user information. assert runtime.store is not None runtime.store.put( (users,), runtime.context.user_id, dict(user_info), ) return User information saved.再提供读取 Tooltooldef get_user_info( runtime: ToolRuntime[Context],) - str: Read user information. assert runtime.store is not None item runtime.store.get( (users,), runtime.context.user_id, ) if item is None: return No user information found. return str(item.value)创建 Agentfrom langchain.agents import create_agentfrom langgraph.store.memory import InMemoryStore store InMemoryStore() agent create_agent( modelopenai:gpt-5.5, tools[ save_user_info, get_user_info, ], storestore, context_schemaContext,)这里真正决定长期数据归属的是user_id例如user_id u1001这个用户之后可以创建很多 Threadu1001 │ ├── thread-001 ├── thread-002 └── thread-003虽然会话不同但三个 Thread 都可以通过runtime.context.user_id定位到相同的长期数据。因此thread_id描述的是“哪一段会话”。而user_id描述的是“谁”。长期记忆的跨会话能力实际上来自多个 Thread ↓使用相同业务身份 ↓访问同一 Namespace ↓读取相同 Store 数据当然Namespace 并不一定非要按 User 划分。还可以按organization_idproject_idagent_idtenant_id组织。这也是 Store 比单纯“用户记忆数据库”更通用的地方。七、长期记忆保存什么Store 只提供保存和查询能力并不会自动判断什么值得记住。长期记忆通常可以分成几类。Semantic Memory语义记忆保存事实和知识。例如用户主要使用 Python用户喜欢简洁回答用户所在团队使用 PostgreSQL当前项目基于 FastAPI这是大多数应用最先需要实现的一类 Memory。Episodic Memory情景记忆保存过去发生过的事件或经历。例如用户之前完成过一次数据库迁移某种 Tool 调用方式曾经失败过去某个案例使用方案 A 获得了较好结果它更接近以前发生过什么而不是单纯的这个用户是什么样Procedural Memory程序性记忆描述 Agent 应该如何完成某类任务。例如根据用户反馈逐步调整生成代码时不要加入过多解释技术文章中先说明机制再给代码执行删除操作之前必须确认这类内容影响的是 Agent 的行为方式。实际项目不必一开始就完整实现三类 Memory。通常可以先从用户事实和偏好开始再根据业务需求增加其他类型。Profile 与 Collection即使只保存用户信息也还存在两种常见组织方式。第一种是 Profile{ name: Bob, language: Python, answer_style: concise, frameworks: [ FastAPI, LangChain ]}整个用户画像作为一条或少量几条 Item 保存。优点是结构清晰读取简单。缺点是 Profile 越来越大之后更新某一项时需要处理整份结构。另一种是 Collectionmemory-001用户主要使用 Python memory-002用户正在开发 FastAPI 项目 memory-003用户希望回答尽量简洁每一个事实独立存在。这样更容易新增搜索删除局部更新也更适合语义检索。但 Collection 需要额外处理重复冲突过期召回排序因此如果数据字段稳定例如语言、时区、界面主题Profile 更简单。如果信息会持续增加并且每次只需要检索其中的一部分Collection 通常更适合。八、什么时候写入记忆真正困难的通常不是store.put(...)而是什么时候应该调用 store.put()长期记忆主要有两种写入方式。当前执行中写入第一种是在 Agent 当前请求中立即判断并保存也就是 Hot Path。例如用户以后代码示例都使用 Python。 ↓ Agent 判断这是长期偏好 ↓ 写入 Store ↓ 继续回答这样做最大的好处是立即生效。如果用户马上新建 Thread也能够读取到刚刚保存的偏好。但当前 Agent 同时需要处理回答问题识别值得记忆的信息确定 Memory 类型处理已有 Memory执行写入请求链路会变得更复杂。后台提取另一种方式是先完成当前任务再单独执行 Memory Extraction。例如Conversation ↓正常响应 ↓Memory Processor ↓分析多轮消息 ↓提取长期事实 ↓去重与合并 ↓Store这种方式更适合从一段较长的会话中提取用户画像技术栈项目背景长期目标历史事实它不会增加主请求中的记忆处理负担。但 Memory 不会立即出现。如果后台提取还没有完成新的 Thread 暂时可能读不到刚产生的信息。实际应用中两种方式可以组合。例如以后都使用中文回答这是一条明确、稳定而且应该立即生效的偏好可以同步写入。而一段几十轮的项目讨论则更适合结束后统一提取项目使用 FastAPI数据库是 PostgreSQL准备迁移到 Kubernetes当前主要关注性能问题长期记忆系统的质量很大程度上取决于写入规则是否足够克制清晰、明确、有用。不是所有出现过的信息都值得保存。九、Memory 与 RAGStore 支持 Embedding 和语义搜索后看起来与 RAG 很接近。例如它们的技术链路确实很类似数据 ↓Embedding ↓检索 ↓Context ↓LLM但它们管理的数据来源和生命周期不同。RAG 通常回答系统知道哪些外部知识例如技术文档产品手册企业知识库合同代码库FAQMemory 则更关注这个 Agent 需要长期记住什么例如Bob 使用 PythonBob 喜欢简洁回答Bob 当前在开发 FastAPI 项目Bob 曾经做过某个决定一个简单的判断方法是看信息描述的主体。例如FastAPI 如何处理依赖注入属于外部知识更适合 RAG。而Bob 的项目使用 FastAPI。描述的是用户历史信息更适合 Memory。真实 Agent 中两者经常同时存在用户问题 │ ├── Memory Retrieval │ ↓ │ 用户背景 │ └── RAG Retrieval ↓ 外部知识 ↓ Context ↓ LLM例如用户询问某个数据库设计问题。RAG 提供当前 PostgreSQL 文档和内部项目规范。Memory 提供用户使用 Python项目基于 FastAPI数据库是 PostgreSQL偏好简单方案最终模型同时得到知识用户上下文两者并不是替代关系。十、长期记忆的工程问题把 Store 接入 Agent 并不困难。真正上线后需要长期处理的是 Memory 的生命周期。记忆污染用户说最近想试试 Java。如果系统直接保存为preferred_language Java就可能制造错误记忆。用户可能只是临时尝试并没有改变长期偏好。类似的问题还包括模型推断成事实玩笑被写入 Memory临时状态被当成长期属性旧信息覆盖新事实因此重要 Memory 可以附带额外业务字段{ value: Python, type: preferred_language, source: explicit_user_statement, confidence: 1.0}这些字段不是 LangGraph Store 强制要求的结构而是应用层可以自行设计的 Schema。Store 负责保存读取搜索删除至于某条数据是否可信需要应用自己控制。冲突与更新如果使用固定 Key(users, u1001, preferences) ↓programming-language那么新的确定信息可以直接更新原来的 Item。Collection 会复杂一些。例如同时存在用户主要使用 Python用户主要使用 Java用户主要使用 C#如果这些数据没有时间、来源和状态信息检索时就很难判断应该相信哪一条。因此 Collection 通常需要进一步考虑新增覆盖合并失效删除而不是简单执行store.put(...)过期与删除长期记忆也存在时效性。例如姓名通常变化较少。而最近正在开发某个项目当前学习 LangChain最近关注某项技术几个月之后可能已经不再成立。Store 提供删除 Item 的能力。部分具体 Store 实现还可以支持 TTL但是否支持 TTL 取决于底层实现不能假设所有 Store 都具备相同能力。因此生产系统最好明确哪些数据长期保留哪些数据需要定期确认哪些数据自动过期哪些数据被新值覆盖哪些数据允许用户主动删除长期 Memory 如果只有 Write Path却没有 Update 和 Delete Path最终很容易变成一份越来越大的历史垃圾集合。存储实现InMemoryStore适合学习本地开发测试Demo数据保存在 Python 进程内存中。进程结束后数据就会消失。生产环境如果真正要求长期保存需要选择持久化 Store例如数据库支持的 Store 实现。因此“Long-term Memory”描述的是数据脱离 Thread 长期存在并不等于InMemoryStore 天然永久保存长期记忆的逻辑作用域与底层存储是否真正持久化是两个不同的问题。总结LangChain 的长期记忆可以理解成一套建立在 Store 之上的跨 Thread 数据管理机制。最基本的数据组织方式是Namespace ↓Key ↓Value ↓StoreNamespace 负责划分逻辑范围Key 定位其中的一条 ItemValue 保存真正的业务数据。在此基础上Store 提供putgetsearchdelete等基本操作并可以通过索引和 Embedding 支持语义搜索。实际项目中更需要花时间设计的并不是 API而是 Memory 本身什么值得保存怎样划分 Namespace使用 Profile 还是 Collection什么时候写入什么时候检索怎样处理冲突什么时候更新和删除如果保存的是稳定、明确的数据可以使用固定 Key 的结构化 Profile。如果信息会不断积累而且每次只需要读取其中一部分可以采用 Collection并结合语义搜索。Memory 也不需要替代 RAG。前者保存用户、Agent 和历史上下文中的长期信息后者负责检索外部知识。两种数据共同进入模型上下文通常比单独依赖任何一种方式更符合真实 Agent 应用的需要。学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%免费】
返回列表