显示页面讨论过去修订反向链接回到顶部 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 ====== 第十章:核心能力详解 - Memory ====== “记忆”是很多 LLM 应用最容易被误用的能力之一。很多初学者会把 Memory 理解成“把历史聊天全塞进去”,但工程上真正的问题不是“有没有保存记录”,而是:**系统应该记住什么、保存多久、以什么形式保存、在何时取回使用。** 现代 LangChain 和 LangGraph 语境下,Memory 与其说是一个“会话缓存功能”,不如说是 **状态管理 + 上下文压缩 + 长期档案设计** 的组合。本章将系统讲解短期记忆、长期记忆、摘要记忆、档案记忆、线程状态、检查点,以及如何把这些概念落到具体代码里。 ===== 10.1 为什么需要 Memory ===== 在一次性问答里,Memory 的收益很小;但在以下场景中,记忆会显著提升体验: * 多轮客服; * 长周期任务协作; * 项目跟进助手; * 个性化办公助手; * 带状态的 Agent 工作流。 没有记忆,系统每轮都像“第一次见你”;记忆用错,则系统会变得啰嗦、昂贵、容易泄露信息,还可能记住过期事实。 ===== 10.2 记忆不是单一概念 ===== ==== 10.2.1 短期记忆 ==== 短期记忆解决的是当前会话的连续性,例如: * 用户刚刚给出的订单号; * 当前对话中确认过的条件; * 本次任务的中间步骤。 ==== 10.2.2 长期记忆 ==== 长期记忆保存跨会话依然有价值的信息,例如: * 用户所属部门; * 常用语言偏好; * 经常关注的项目; * 已确认的操作习惯。 ==== 10.2.3 摘要记忆 ==== 不是保存原文,而是压缩成关键结论,例如: * 用户负责华东区渠道业务; * 当前项目目标是搭建知识库问答系统; * 偏好简洁回答,最好带表格。 ==== 10.2.4 档案记忆 ==== 档案记忆更像结构化档案或用户画像,适合存: * 账号属性; * 权限等级; * 组织归属; * 固定配置。 ===== 10.3 记忆究竟应该存什么 ===== 一个实用原则是:**只存对未来决策仍有帮助的信息。** 适合保存: * 稳定偏好; * 已确认事实; * 当前任务摘要; * 下轮仍会用到的状态字段。 不适合保存: * 冗长闲聊; * 未确认猜测; * 敏感原文全文; * 一次性噪声输出。 ===== 10.4 会话历史缓冲:最简单的短期记忆 ===== ==== 10.4.1 直接保留最近几轮消息 ==== <code python> from langchain_core.messages import SystemMessage, HumanMessage, AIMessage history = [ SystemMessage(content="你是企业 IT 助手。"), HumanMessage(content="VPN 连不上怎么办?"), AIMessage(content="先检查网络和账号状态,如果仍失败联系 IT 服务台。"), HumanMessage(content="那我昨天已经重置过密码了。") ] </code> 这种方式最适合: * 原型阶段; * 较短会话; * 临时 Demo。 ==== 10.4.2 简单裁剪函数 ==== <code python> def trim_history(messages, keep_last=8): system_messages = messages[:1] others = messages[1:] return system_messages + others[-keep_last:] </code> 但要注意,这种方式很快会遇到两个问题: * 旧消息越来越长; * 重要结论会在裁剪时丢失。 ===== 10.5 用摘要记忆压缩长对话 ===== 对于较长会话,更好的方式通常是“近期原文 + 历史摘要”。 ==== 10.5.1 一个简单摘要函数示意 ==== <code python> from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate summary_prompt = ChatPromptTemplate.from_template(""" 请把以下对话总结成给后续助手看的会话摘要。 要求保留: 1. 用户目标 2. 已确认事实 3. 未解决问题 4. 用户偏好 对话: {conversation} """) summarizer = summary_prompt | ChatOpenAI(model="gpt-4o-mini", temperature=0) </code> ==== 10.5.2 摘要如何接回主链路 ==== <code python> summary_message = SystemMessage( content="会话摘要:用户为财务部员工,正在咨询差旅报销规则,已确认出差日期为 4 月 1 日至 4 月 3 日。" ) </code> 之后把 `summary_message` 插入消息列表,再拼接最近几轮原文,就能在有限 token 预算里保持连续性。 ===== 10.6 长期记忆与用户档案 ===== 长期记忆更适合存成结构化字段,而不是大量历史原文。 ==== 10.6.1 一个用户档案示例 ==== <code python> user_profile = { "user_id": "u_001", "department": "finance", "language": "zh-CN", "preferred_response_style": "concise", "frequent_topics": ["报销", "预算", "采购流程"] } </code> ==== 10.6.2 在 Prompt 中使用档案信息 ==== <code python> from langchain_core.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_template(""" 你是企业办公助手。 用户档案:{profile} 请根据用户档案和问题给出回答。 问题:{question} """) </code> 这类设计的优点是: * 稳定; * 易于审计; * 容易更新和失效; * 不会像聊天原文那样越来越冗长。 ===== 10.7 在 Agent 中使用短期记忆 ===== 根据 LangChain 官方短期记忆文档,现代 Agent 往往借助 **checkpointer + thread_id** 来实现线程级状态保存。 ==== 10.7.1 一个带 InMemorySaver 的 Agent ==== <code python> from langgraph.checkpoint.memory import InMemorySaver from langchain.agents import create_agent checkpointer = InMemorySaver() agent = create_agent( model="openai:gpt-4o-mini", tools=[], checkpointer=checkpointer, system_prompt="你是一个支持多轮对话的企业助手。" ) config = {"configurable": {"thread_id": "thread-001"}} agent.invoke( {"messages": [{"role": "user", "content": "我来自财务部。"}]}, config=config, ) agent.invoke( {"messages": [{"role": "user", "content": "我刚才说我是哪个部门的?"}]}, config=config, ) </code> 在这个例子里: * `thread_id` 用于标识同一个会话线程; * `checkpointer` 负责保存状态; * 第二次调用时,Agent 可以基于第一次的状态继续回答。 ===== 10.8 自定义状态字段 ===== 在复杂 Agent 或 LangGraph 中,记忆往往不只是消息,还包括结构化状态字段。 ==== 10.8.1 一个自定义状态示意 ==== <code python> from typing import TypedDict, NotRequired class CustomState(TypedDict): messages: list user_profile: NotRequired[dict] current_order_id: NotRequired[str] last_summary: NotRequired[str] preferred_language: NotRequired[str] </code> 这种方式特别适合: * 将短期状态显式字段化; * 减少模型在历史中反复“找信息”; * 支持图节点之间共享状态。 ===== 10.9 记忆更新策略 ===== 不是每条信息都值得写进长期记忆。建议使用规则: * 用户明确确认过; * 对未来决策有持续价值; * 可结构化表达; * 不是高风险敏感原文; * 有失效规则。 ==== 10.9.1 一个简单的“只保存确认事实”示例 ==== <code python> def should_store_as_memory(text: str) -> bool: keywords = ["我是", "我负责", "以后都", "偏好", "请默认"] return any(k in text for k in keywords) </code> 真实项目中通常会用: * 规则; * 分类器; * 结构化提取; * 人工确认; 组合来决定是否写入长期记忆。 ===== 10.10 记忆失效机制 ===== 如果没有失效机制,系统会把过期事实当真理。例如: * 用户已调岗,但系统还记着旧部门; * 审批规则已更新,但记忆还是旧版本; * 用户偏好曾经有效,但现在已变。 建议你为记忆设计: * TTL(过期时间); * 版本号; * 最近确认时间; * 明确覆盖规则。 <code python> memory_record = { "fact": "用户偏好英文输出", "confirmed_at": "2026-04-03T10:00:00", "expires_at": "2026-07-03T10:00:00", "source": "user_confirmed" } </code> ===== 10.11 何时不该使用 Memory ===== 以下场景要谨慎: * 法务、财务等高敏感场景,除非你有明确合规方案; * 单轮问答; * 信息必须每次实时核验的场景,如价格、库存、政策即时状态; * 团队尚未建立基础日志与权限控制时。 ===== 10.12 一个“近期原文 + 历史摘要 + 档案字段”的组合案例 ===== <code python> conversation_state = { "profile": { "department": "finance", "language": "zh-CN", "style": "concise" }, "summary": "用户正在咨询差旅报销流程,已确认出差日期和目的地。", "recent_messages": [ {"role": "user", "content": "报销单需要先给主管审批吗?"}, {"role": "assistant", "content": "是的,通常需要主管审批后再提交财务。"} ] } </code> 然后你可以在主 Prompt 中分层使用: * profile → 用于风格和权限控制; * summary → 用于压缩长期上下文; * recent_messages → 用于保留最近语义连续性。 ===== 10.13 本章小结 ===== * Memory 本质上是状态管理问题,而不只是聊天记录缓存; * 短期、长期、摘要、档案记忆应区别设计; * `checkpointer + thread_id` 是现代 Agent 的常见短期记忆方式; * 结构化状态字段比无脑堆消息更稳定; * 必须为记忆设计写入规则、更新策略和失效机制。 ===== 练习 ===== 1. 为一个企业办公助手设计短期记忆、长期记忆和档案记忆三层结构。 2. 使用 `InMemorySaver` 搭建一个支持 thread_id 的简单多轮 Agent。 3. 设计一个“只保存已确认事实”的记忆筛选器。 4. 为你的系统写一份记忆失效规则,说明哪些信息多久失效、谁可以覆盖它。 5. 将某个多轮问答场景改造成“近期原文 + 历史摘要 + 档案字段”的结构。 ===== 参考资源 ===== * [[https://docs.langchain.com/oss/python/langchain/short-term-memory|LangChain 官方文档:Short-term memory]] ===== 10.14 补充案例:从聊天记录提取长期记忆 ===== 下面给出一个更具体的思路:让系统从长对话里自动提取“值得长期保存”的事实。 <code python> from pydantic import BaseModel, Field from typing import Literal from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate class MemoryCandidate(BaseModel): fact: str = Field(description="可被长期保存的事实") category: Literal["profile", "preference", "task", "other"] should_store: bool = Field(description="是否建议存入长期记忆") reason: str = Field(description="为什么值得存或不值得存") extract_prompt = ChatPromptTemplate.from_template(""" 请从下面对话中识别是否存在值得长期保存的信息。 仅保留以下类型: 1. 用户稳定身份信息 2. 用户明确确认的偏好 3. 会跨轮使用的重要任务事实 对话: {conversation} """) extractor = extract_prompt | ChatOpenAI(model="gpt-4o-mini", temperature=0).with_structured_output(MemoryCandidate) candidate = extractor.invoke({ "conversation": "用户说:我是财务部员工,以后默认用简洁中文回答我。" }) print(candidate) </code> 这个模式的价值在于: * 不是无脑存所有聊天; * 让“记忆写入”本身也可解释; * 便于在写入前做人工复核或规则过滤。 ===== 10.15 补充案例:把会话摘要与用户档案一起注入 Prompt ===== <code python> from langchain_core.prompts import ChatPromptTemplate assistant_prompt = ChatPromptTemplate.from_template(""" 你是企业办公助手。 用户档案: {profile} 历史摘要: {summary} 当前问题: {question} """) result = assistant_prompt.invoke({ "profile": {"department": "finance", "style": "concise"}, "summary": "用户正在咨询 4 月初的差旅报销流程,已确认出差城市为上海。", "question": "住宿发票忘开发票抬头怎么办?" }) print(result) </code> 这类设计很适合: * 对话很长但仍需保留连续性; * 有一些稳定档案字段需要持续生效; * 希望把“聊天历史”和“用户画像”分开管理。 登录 Detach Close 该主题尚不存在 您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。 langchain二次开发/核心能力详解_memory.txt 最后更改: 2026/04/03 17:43由 张叶安 登录