Agent 上下文记忆原理

本文从「Agent 为什么需要记忆」出发,讲清上下文记忆的底层原理、三层记忆结构、溢出处理策略、主流框架落地方式,以及工程上的权衡与常见坑。读完能理解:Agent 的"记忆"本质上不是模型记住了东西,而是一段编排逻辑决定"把什么塞进有限的上下文窗口"。


1. 一句话核心

大模型(LLM)是无状态的,它唯一能感知到的就是每次请求时喂给它的全部文本------即"上下文"。所谓 Agent 记忆,就是一段代码在每轮对话时,决定哪些历史进入这个容量有限的上下文窗口、进入多少、以什么形式进入。


2. 为什么需要"记忆"------无状态性

每次推理,LLM 都是从零开始读入输入,它不会自动记住上一轮说了什么,也不存在跨请求的内部状态。它能"看到"的只有:

  • 系统提示(system prompt)
  • 历史消息列表
  • 当前用户输入
  • 工具调用与结果(function/tool call)

这些文本的总 token 数受到上下文窗口上限约束(例如 8k / 32k / 128k)。因此:

放进去的,模型就"记得";被压缩、丢弃或没检索回来的,模型就"忘记"。

理解了这一点,后面所有记忆方案都只是在回答一个问题:大量历史,往哪放、怎么取舍、什么时候取回。


3. 记忆的三个层次

主流系统通常把记忆拆成三层:

层次 载体 特点 类比
工作记忆(短期) 上下文窗口内的原始对话 + 工具结果 精确、完整,但最贵、最有限 人脑的"当下注意"
长期记忆 向量库 / 数据库 / 文件等外部存储 近乎无限,靠检索召回,可能漏或错 人脑的"知识沉淀"
反思 / 元记忆 总结、教训、偏好、目标等提炼信息 信息密度高,以摘要或结构化字段保存 人脑的"经验与自我认知"
  • 工作记忆:Agent 的"当下"全靠它,是最真实的状态。
  • 长期记忆 :历史超出窗口后持久化到外部,需要时检索相关片段塞回窗口------这就是 RAG 在记忆场景的应用。
  • 反思记忆:不是原始对话,而是 Agent 对过往的总结与偏好("用户不喜欢表格""上次这个方案跑挂了")。MemGPT 的 core memory、Reflexion 的反思缓存都属于此类。

4. 上下文溢出后的三种处理策略

对话越长,历史迟早超过窗口上限。此时有三类策略,通常组合使用

4.1 滑动窗口(Sliding Window)

暴力截断,只保留最近 N 条,最旧的直接丢掉。

  • 优点:实现最简单。
  • 缺点:早期上下文被彻底遗忘。

4.2 摘要压缩(Summarization)

把较老的一段对话用 LLM 压缩成一小段摘要,用"摘要"替代"原文"继续留在窗口。

  • 优点:保大意、省 token。
  • 缺点:细节丢失,且摘要需要额外一次 LLM 调用。

4.3 长期存储 + 检索(Store & Retrieve)

把完整历史写入向量库,每轮根据当前问题做相似度检索,只取回最相关的几条。

  • 优点:容积极大、扩展性最好,是目前主流方案。
  • 缺点:依赖检索质量,可能召错或召漏。

循环总结:新信息进来 → 窗口不够 → 挤出去的历史或被丢弃、或被压成摘要、或被存档 → 需要时再检索回流。


5. 一次完整对话的记忆读写流程

markdown 复制代码
用户输入
  │
  ├─ 1) 组装候选记忆:当前输入 + 常驻 prompt(人设/指令)
  │         + 短期原始对话(滑动窗口内)+ 检索回的长期相关片段 + 反思摘要
  │
  ├─ 2) 拼成完整 prompt,调用 LLM
  │
  ├─ 3) LLM 输出 + 可能发起工具调用,工具结果再回流传给 LLM
  │
  └─ 4) 收尾:本轮内容落入短期记忆
           │  超窗 → 触发滑动窗口丢弃 / 摘要压缩 / 写入长期存储(向量化)
           └  可选 → 提炼反思/偏好,更新元记忆

关键点:记忆的读(检索组装)发生在调用 LLM 之前,写(落库/压缩/丢弃)发生在调用之后。


6. 主流框架 / 项目落地对照

项目 / 框架 记忆实现方式
LangChain / LlamaIndex 提供可插拔 Memory 组件:ConversationBufferMemory(全量)、ConversationBufferWindowMemory(滑窗)、ConversationSummaryMemory(摘要)、VectorStoreRetrieverMemory(向量检索)
MemGPT / Letta 把 LLM 比作操作系统:主上下文=内存,外部存储=磁盘,实现"换页 / 缺页调入",并显式写入反思类 core memory
AutoGPT / BabyAGI 早期做法:把目标与中间结果写文件 / 向量库,下次读回,是显式外存化雏形
现代对话 / 多 Agent 产品 通常是「滑动窗口 + 摘要 + 向量检索」三者混合,人设/system prompt 常驻窗口最前

7. 记忆的分级(按作用范围)

除了按"时限"分层,工程上还常按作用范围分级,避免"一个向量库装所有东西":

  1. 会话级记忆:仅本次会话有效,随会话结束清空------即短期记忆。
  2. 用户级记忆(profile):跨会话持久的用户画像、偏好、习惯("记住我的时区""我是开发者")。
  3. 项目级 / 知识级记忆:某个项目、主题的长期知识库,供多会话复用。
  4. 全局 / 组织级:团队规范、产品文档等公共知识。

分层的好处:不同范围用不同存储与生命周期,检索时也按范围隔离,减少噪声。


8. 一个最小实现示例

以下是一个极简的「滑动窗口 + 摘要 + 向量检索」记忆循环骨架(TypeScript 示例,便于理解原理):

typescript 复制代码
interface Turn {
  user: string;
  content: string;
}

interface Prompt {
  system: string;
  recent: Turn[];
  retrieved: string[];
  user: string;
}

interface Model {
  generate(prompt: Prompt): Promise<string>;
  summarize(text: string): Promise<string>;
}

interface VectorStore {
  add(content: string): Promise<void>;
  search(query: string, topK?: number): Promise<string[]>;
}

class MinimalAgent {
  private shortTerm: Turn[] = [];       // 近期原始对话(滑动窗口)
  private summary = '';                 // 早期对话的摘要
  private readonly maxTurns: number;

  constructor(
    private readonly model: Model,
    private readonly vectorStore: VectorStore,
    maxTurns = 8,
  ) {
    this.maxTurns = maxTurns;
  }

  async remember(turn: Turn): Promise<void> {  // 写:新内容落入短期记忆
    this.shortTerm.push(turn);
    if (this.shortTerm.length > this.maxTurns) {
      const expired = this.shortTerm.shift()!;        // 滑动窗口:丢最旧
      this.summary = await this.model.summarize(      // 摘要压缩
        `${this.summary} ${expired.content}`,
      );
      await this.vectorStore.add(expired.content);    // 长期存储:向量化存档
    }
  }

  async buildPrompt(userInput: string): Promise<Prompt> {  // 读:组装候选记忆
    const retrieved = await this.vectorStore.search(userInput, 5);
    return {
      system: `你是助手。历史摘要:${this.summary}`,
      recent: this.shortTerm,
      retrieved,
      user: userInput,
    };
  }

  async run(userInput: string): Promise<string> {
    const prompt = await this.buildPrompt(userInput);
    const answer = await this.model.generate(prompt);
    await this.remember({ user: userInput, content: answer });
    return answer;
  }
}

对应关系:short_termshortTerm)= 工作记忆,summary = 反思/摘要记忆,vector_storevectorStore)= 长期记忆。三者以不同形式一起进入 prompt。


9. 关键权衡与常见坑

9.1 三个核心权衡

  1. 成本 vs 记忆:塞越多记越多,但注意力代价随长度(近似平方)增长,token 成本也线性上升,必须剪裁。
  2. 保真 vs 紧凑:原文最真但最贵,摘要最省但失真,检索最灵活但可能召错,工程上要按场景配比。
  3. 位置敏感 :模型对窗口开头和结尾记得最牢,中间容易被忽略("Lost in the Middle" 现象),因此常把关键指令/人设放最前、最新对话放最后。

9.2 常见坑

  • 上下文污染:检索回的无关片段会干扰模型,甚至劫持指令(prompt injection),需要过滤与去重。
  • 检索噪声:embedding 相似度不等于"有用",召回的片段需重排(rerank)或加阈值。
  • 摘要滚雪球:摘要逐轮叠加会累积误差,越到后面越失真,可定期"重建摘要"。
  • 记忆漂移/冲突:长期记忆里新旧信息矛盾时(如用户改了偏好),需要覆盖或冲突消解策略。
  • 重复记忆:同一信息多次写入造成冗余,需去重。
  • 遗忘策略缺失:只加不减的长期记忆会越来越大、越来越慢,需要定期清理或过期机制。

10. 总结

  • LLM 无状态,上下文窗口=它全部可感知的输入,记忆=对窗口内容的编排。
  • 记忆分工作 / 长期 / 反思三层,分别对应"当下 / 存档检索 / 提炼经验"。
  • 窗口溢出后的三大策略:滑动窗口(丢弃)、摘要压缩(压原文)、长期存储+检索(外存回流),实际工程混合使用。
  • 主流框架(LangChain、MemGPT/Letta、AutoGPT)只是把上述策略做成了不同粒度的组件。
  • 落地时要兼顾 成本、保真、位置敏感,并防住污染、噪声、冲突、滚雪球等坑。

一句话收束:一个好的 Agent 记忆系统,本质是在"记住该记的"和"放得下、取得回"之间做动态平衡。

相关推荐
刘立军1 小时前
中心化配置与 I18n:严格禁止 AI 魔法值与硬编码参数
人工智能·后端·架构
两万五千个小时1 小时前
DeepSeek Harness 从 0 开始:18 todo 任务记忆
人工智能·程序员·架构
两万五千个小时1 小时前
DeepSeek Harness 从 0 开始:17 plan(计划模式)
人工智能·程序员·架构
两万五千个小时2 小时前
DeepSeek Harness 从 0 开始:16 scope 域(作用域隔离)
人工智能·程序员·架构
江洋之道2 小时前
嵌入式LVGL UI架构实践:MVP分层 + 双链表路由,构建低耦合、易维护的嵌入式界面
c语言·架构·嵌入式·lvgl
Java后端的Ai之路2 小时前
02、Python普通工厂模式
开发语言·人工智能·python·设计模式·普通工厂模式
HZZD_HZZD2 小时前
3T-UEM 架构实战:端-接-算-用四层栈如何承载三级能源计量
struts·架构·能源
mldong3 小时前
会签三兄弟:并行/串行/按比例的实现与取舍
java·架构
Java后端的Ai之路3 小时前
01、Python - 设计模式介绍
java·人工智能·python·设计模式·通用