目录

第十章:核心能力详解 - Memory

“记忆”是很多 LLM 应用最容易被误用的能力之一。很多初学者会把 Memory 理解成“把历史聊天全塞进去”,但工程上真正的问题不是“有没有保存记录”,而是:系统应该记住什么、保存多久、以什么形式保存、在何时取回使用。

现代 LangChain 和 LangGraph 语境下,Memory 与其说是一个“会话缓存功能”,不如说是 状态管理 + 上下文压缩 + 长期档案设计 的组合。本章将系统讲解短期记忆、长期记忆、摘要记忆、档案记忆、线程状态、检查点,以及如何把这些概念落到具体代码里。

10.1 为什么需要 Memory

在一次性问答里,Memory 的收益很小;但在以下场景中,记忆会显著提升体验:

没有记忆,系统每轮都像“第一次见你”;记忆用错,则系统会变得啰嗦、昂贵、容易泄露信息,还可能记住过期事实。

10.2 记忆不是单一概念

10.2.1 短期记忆

短期记忆解决的是当前会话的连续性,例如:

10.2.2 长期记忆

长期记忆保存跨会话依然有价值的信息,例如:

10.2.3 摘要记忆

不是保存原文,而是压缩成关键结论,例如:

10.2.4 档案记忆

档案记忆更像结构化档案或用户画像,适合存:

10.3 记忆究竟应该存什么

一个实用原则是:只存对未来决策仍有帮助的信息。

适合保存:

不适合保存:

10.4 会话历史缓冲:最简单的短期记忆

10.4.1 直接保留最近几轮消息

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
 
history = [
    SystemMessage(content="你是企业 IT 助手。"),
    HumanMessage(content="VPN 连不上怎么办?"),
    AIMessage(content="先检查网络和账号状态,如果仍失败联系 IT 服务台。"),
    HumanMessage(content="那我昨天已经重置过密码了。")
]

这种方式最适合:

10.4.2 简单裁剪函数

def trim_history(messages, keep_last=8):
    system_messages = messages[:1]
    others = messages[1:]
    return system_messages + others[-keep_last:]

但要注意,这种方式很快会遇到两个问题:

10.5 用摘要记忆压缩长对话

对于较长会话,更好的方式通常是“近期原文 + 历史摘要”。

10.5.1 一个简单摘要函数示意

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)

10.5.2 摘要如何接回主链路

summary_message = SystemMessage(
    content="会话摘要:用户为财务部员工,正在咨询差旅报销规则,已确认出差日期为 4 月 1 日至 4 月 3 日。"
)

之后把 `summary_message` 插入消息列表,再拼接最近几轮原文,就能在有限 token 预算里保持连续性。

10.6 长期记忆与用户档案

长期记忆更适合存成结构化字段,而不是大量历史原文。

10.6.1 一个用户档案示例

user_profile = {
    "user_id": "u_001",
    "department": "finance",
    "language": "zh-CN",
    "preferred_response_style": "concise",
    "frequent_topics": ["报销", "预算", "采购流程"]
}

10.6.2 在 Prompt 中使用档案信息

from langchain_core.prompts import ChatPromptTemplate
 
prompt = ChatPromptTemplate.from_template("""
你是企业办公助手。
用户档案:{profile}
请根据用户档案和问题给出回答。
 
问题:{question}
""")

这类设计的优点是:

10.7 在 Agent 中使用短期记忆

根据 LangChain 官方短期记忆文档,现代 Agent 往往借助 checkpointer + thread_id 来实现线程级状态保存。

10.7.1 一个带 InMemorySaver 的 Agent

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,
)

在这个例子里:

10.8 自定义状态字段

在复杂 Agent 或 LangGraph 中,记忆往往不只是消息,还包括结构化状态字段。

10.8.1 一个自定义状态示意

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]

这种方式特别适合:

10.9 记忆更新策略

不是每条信息都值得写进长期记忆。建议使用规则:

10.9.1 一个简单的“只保存确认事实”示例

def should_store_as_memory(text: str) -> bool:
    keywords = ["我是", "我负责", "以后都", "偏好", "请默认"]
    return any(k in text for k in keywords)

真实项目中通常会用:

组合来决定是否写入长期记忆。

10.10 记忆失效机制

如果没有失效机制,系统会把过期事实当真理。例如:

建议你为记忆设计:

memory_record = {
    "fact": "用户偏好英文输出",
    "confirmed_at": "2026-04-03T10:00:00",
    "expires_at": "2026-07-03T10:00:00",
    "source": "user_confirmed"
}

10.11 何时不该使用 Memory

以下场景要谨慎:

10.12 一个“近期原文 + 历史摘要 + 档案字段”的组合案例

conversation_state = {
    "profile": {
        "department": "finance",
        "language": "zh-CN",
        "style": "concise"
    },
    "summary": "用户正在咨询差旅报销流程,已确认出差日期和目的地。",
    "recent_messages": [
        {"role": "user", "content": "报销单需要先给主管审批吗?"},
        {"role": "assistant", "content": "是的,通常需要主管审批后再提交财务。"}
    ]
}

然后你可以在主 Prompt 中分层使用:

10.13 本章小结

练习

1. 为一个企业办公助手设计短期记忆、长期记忆和档案记忆三层结构。

2. 使用 `InMemorySaver` 搭建一个支持 thread_id 的简单多轮 Agent。

3. 设计一个“只保存已确认事实”的记忆筛选器。

4. 为你的系统写一份记忆失效规则,说明哪些信息多久失效、谁可以覆盖它。

5. 将某个多轮问答场景改造成“近期原文 + 历史摘要 + 档案字段”的结构。

参考资源

10.14 补充案例:从聊天记录提取长期记忆

下面给出一个更具体的思路:让系统从长对话里自动提取“值得长期保存”的事实。

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)

这个模式的价值在于:

10.15 补充案例:把会话摘要与用户档案一起注入 Prompt

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)

这类设计很适合: