# 深度解析Memory本质：Agent和Chatbot的差异

> 作者：晨旭｜发布：2025-11-28｜系列：动手落地AI
> 来源：https://chenxu.xin/writing/agent-memory-essence

从代码底层看 Agent 记忆：所谓记忆不过是拼接和重传。拆解多轮对话、Function Call、Chatbot 的存储与遗忘策略，以及 Agent 与多 Agent 的记忆设计。

---

一直以来，我对 Agent 记忆这个概念都感觉像是雾里看花。

最近在学习底层实现后我才发现：相比于看技术文档或二手拆解文章，直接看代码带来的理解冲击力是完全不同的。

这篇文章，就是我从代码底层视角出发，对理解 Agent 记忆机制的一次复盘。（如果对多轮对话以及 Function Call 的基础实现已经很熟悉，可以直接跳到第二部分）

本文会依次讲四件事：记忆的本质、Chatbot 的长短期记忆机制、Agent 记忆的核心差异、多 Agent 记忆设计思路。

## 记忆的本质：拼接和重传

### 多轮对话

首先，为什么 ChatGPT 能接上话？

因为在这个对话系统的后台，有一个中间人（应用程序代码），它把你之前说过的所有话，打包成一个长长的列表，每一次都完整地塞回给模型。

这就像是你和一个失忆症患者聊天，每次你想问新问题，都必须把之前的聊天记录打印出来递给他，他看完这叠厚厚的纸，才能回答你的新问题。

```text
第一轮：用户问「北京天气怎么样？」
       -> 系统发送 [User: 北京天气] -> 模型回答。

第二轮：用户问「那上海呢？」
       -> 系统必须发送 [User: 北京天气, AI: ...（上一轮AI的回复）, User: 那上海呢?]。

第 N 轮：系统发送 [User: ..., AI: ..., (无数轮)..., User: 新问题]。
```

### 工具调用

上面是 LLM 基于自身的知识对话的情况。当涉及到工具调用的时候，这时的工作流程是怎么样的呢？是模型和人一样去根据 URL 访问网页，获得结果然后回复吗？

其实不是这样的。（在我深入学习代码之前，我就是这么觉得的——或者说，我都没有仔细去想过这中间的过程。）

#### 第一回合：LLM 的决策时刻

**（1）定义工具**

在代码里，需要定义一个 `tools` 列表（JSON Schema），告诉 LLM：「有一个叫 `get_weather` 的工具，需要 `latitude` 和 `longitude` 两个参数」。

注意：这时候什么都没发生，只是定好了规则。

**（2）发起第一轮请求**

用户问：「What's the weather like in Paris today?」

程序把这句话封装进 `messages` 列表发给 LLM。关键点：在 `client.chat.completions.create` 中，同时传入了 `messages` 和 `tools`。

**（3）LLM 的思考与停顿**



对应流程图中的「需要调用函数？」菱形判断框。

LLM 收到请求后，发现自己不知道巴黎实时的天气（知识截止），但它发现手边有个 `get_weather` 的工具。这时 LLM 不会直接回答问题，而是返回一个特殊的指令。

**（4）第一轮输出（JSON Output）**

LLM 返回的内容不是自然语言，而是一段 JSON 数据，大概长这样：

```json
{
  "name": "get_weather",
  "arguments": "{ 'latitude': 48.85, 'longitude': 2.35 }"
}
```

这一步非常关键：LLM 到此为止就收工了。它没有去查天气，它只是生成了一个条子，上面会写着要调用什么函数，以及调用这个函数所需的信息参数。

#### 中场休息：Python 程序的跑腿时刻

这就到了程序执行环节。这里完全没有 LLM 的事，全是 Python 代码在干活：

- **解析字符串**：代码 `json.loads(...)` 把 LLM 给的字符串解析成参数。
- **执行函数**：代码执行 `get_weather(...)`。这一步就是根据模型提供的信息去调外部 API。
- **拿到结果**：程序拿到了 `"14.2°C"` 这个结果。

#### 第二回合：带上结果，重新排队

这一步就是把历史记录全部重新塞给 LLM。为了让 LLM 能够根据查询结果回答用户，必须构造一个新的上下文。

**（1）追加 LLM 提供的条子**

```python
messages.append(completion.choices[0].message)
```

必须把第一回合 LLM 说的「我要调用工具……」这句话记入历史，否则 LLM 待会会因为不知道自己发起了调用而感到莫名其妙。

**（2）追加 Tool 的结果**

```python
messages.append({ "role": "tool", "content": "14.2°C" })
```

这里把 Python 跑出来的 `14.2°C` 塞进了历史记录。

**（3）发起第二轮请求**

再次调用 `client.chat.completions.create`。此时发给 LLM 的是三条信息：

```text
User:      「巴黎天气咋样？」
Assistant: 「（发出指令）调用 get_weather 参数是巴黎...」
Tool:      「14.2°C」
```

LLM 看到这三句话，终于恍然大悟：「原来我查到了是 14.2 度。」

**（4）最终输出**

LLM 生成自然语言回复：「The current temperature in Paris is 14.2°C.」

总结一下，Function Call 的本质就是：**暂停 → 外部执行 → 拼接历史 → 重发**。

如果把这里的 `get_weather` 换成 `save_memory`（写入记忆）或 `search_memory`（读取记忆），原理是一模一样的：

- Agent Memory 不是大模型脑子里的东西。
- 它是大模型在第一回合发出 `search_memory` 的指令（JSON）。
- Python 程序去数据库里捞出数据。
- Python 程序把捞出的记忆伪装成 `tool` 的消息，追加到对话历史里。
- 大模型在第二回合看到这条历史，才想起了这件事。

在很多 Memory 设计里，`search_memory` 这一步就是一个小型的 RAG：用当前对话作为 query，在外部记忆库中做语义检索，再把结果拼回上下文。

## 普通 Chatbot 的记忆机制

所谓的拥有记忆，在代码层面，其实就是像上述那样一次次笨拙但有效的拼接和重传过程。记忆本质上就是一个特殊的检索源。要在工程上实现记忆需要解决四个问题：**存什么、存哪儿、什么时候读、什么时候忘**。

下面拿一个 RAG 架构的企业问答助手为例。

### 一、核心架构：双重检索机制

在引入记忆模块后，RAG 的工作流就不再是单线条的了，而是变成了一个闭环系统。最核心的变化在于引入了双重检索。

- **静态知识检索**：去向量数据库里查企业文档、Wiki 等固定知识。
- **动态记忆检索**：去记忆模块里查对话历史、用户偏好、中间推理结果等动态上下文。

此时工作流变成了这样：

```text
用户提问 -> 双重检索（知识库 + 记忆库） -> 信息融合 -> 模型生成 -> 记忆更新（写入新一轮对话）
```

这种架构让模型既有博学的一面（查资料），又有贴心的一面（查过往）。

### 二、存储层：记忆放在哪里？

这并不是一个简单的存数据库就能解决的，根据时效性，通常采用组合方案。

**（1）短期记忆：内存 / 键值存储（Redis）**

- 场景：就像人的工作记忆，只记最近几轮对话，随用随取。
- 技术：Redis 等内存数据库。
- 优点：速度极快，延迟低，适合高频读写。通常通过 Session ID 来存取。
- 缺点：容量有限，非持久化。一旦会话结束或超时，可能就清空了。而且只能按 Key 查，不能按语义查。

**（2）长期 / 语义记忆：向量数据库（Vector DB）**

- 场景：就像人的长期回忆，比如你问「我上次说的那个项目方案是什么？」，模型需要跨越几十轮对话去捞取信息。
- 技术：向量数据库，如 Pinecone、Weaviate。
- 优点：支持语义搜索。把记忆转化成向量，当用户说话时，系统计算语义相似度，去库里捞出语义相关的往事。
- 缺点：这种方式能实现长程回忆，但引入了额外的计算开销（Embedding + 搜索），且可能偶尔检索出语义相似但语境不相关的错误记忆。

**（3）用户画像：NoSQL 数据库**

- 场景：存用户画像、历史决策日志等结构化数据。
- 技术：MongoDB、DynamoDB。
- 作用：作为兜底的持久化存储，容量大且便宜，适合存那些不需要频繁语义检索的档案信息。比如构建用户画像来提炼用户的个性，让模型在任何时候都能调取背景偏好。

### 三、管理层：如何避免脑容量爆炸？

这是最需要关注的地方。如果把所有对话一股脑都塞给大模型，Token 费用会爆炸，模型也会因为上下文太长而变笨。所以需要记忆管理策略。

**（1）选择性遗忘**

人类的大脑会遗忘，AI 也要学。

- **滑动窗口**：最简单粗暴，只保留最近 N 轮，旧的直接扔掉。
- **艾宾浩斯遗忘曲线**：模拟人类记忆规律，对很久没被提及（检索）的记忆降低权重，慢慢淡忘；对经常被提起的记忆进行加固。

**（2）层次化摘要**

别让模型读几十页的聊天记录，给它看摘要。

- 操作：定期将详细的对话日志提炼成简短的要点。
- 效果：既保留了事情的梗概，又节省了 Token。新对话发生时，模型看的是「近期详细记录 + 远期摘要」。

**（3）用户画像持久化**

这是实现个性化的关键。

- 操作：从对话中提取事实。比如用户说「我海鲜过敏」，系统将其转化为结构化数据 `{Topic: Food, Preference: No Seafood}` 存入专门的静态记忆块。
- 好处：无论过了多久，只要涉及吃饭场景，这个画像就会被调出来注入 Prompt，无需重复检索海量历史。

## 单 Agent 的记忆机制：从说话到做事

前面讲述的普通 Chatbot 记忆（向量库 + Redis），解决的核心问题是「如何像人一样聊天」。但 Agent 和 Chatbot 有两个关键差异，决定了它不能只靠对话历史：

- 它需要多步推理。
- 它的一次任务可能跨越很多轮，涉及多次工具调用。

所以，Agent 的记忆设计，必须在对话内容之外，额外增加两类核心记忆。

### 1、行为记忆（工具调用日志）

普通 Chatbot 记的是「你说了什么，我说了什么」，但 Agent 必须记住「我做过什么」。

- **痛点**：如果 Agent 不记得自己调用过 `Google Search` 且失败了，它可能会在一个死循环里不断重试。
- **解决**：需要记录 `(tool_name, input, output, success_flag)`。
- **进阶**：在任务结束后，将踩坑经验写入长期向量库。这样下次遇到类似任务，Agent 不仅记得用户说过什么，还能记得「上次怎么做才成功」。

### 2、任务级临时记忆

这是 Agent 的草稿纸。

- **场景**：比如写代码，Agent 需要拆解步骤：Step 1 建库，Step 2 写接口……
- **区别**：这些中间思考过程对于任务执行至关重要，但对于用户来说是噪音。
- **策略**：为每个 `task_id` 维护独立的短期记忆区。任务进行中完整保留，确保逻辑连贯；任务结束后只提炼关键结论存入长期记忆，把草稿纸直接扔掉，防止 Token 爆炸。

基于上述差异，一个成熟的单 Agent 存储布局应该是以下的组合：

- **短期记忆（Redis）**：存当前任务的「思考链、工具调用结果」。
- **长期记忆（Vector DB）**：存「用户事实、反思、最佳实践」。
- **持久化（NoSQL）**：存结构化的「用户配置、工具日志」。

读写策略可以总结为：

**写入**

- 每轮对话：抽取新的事实，并实时更新用户画像。
- 任务结束：生成总结，丢弃过程草稿，沉淀经验。

**读取**

- 新任务启动：按「用户 ID + 任务类型」检索，加载背景偏好与历史同类任务摘要。
- 任务执行中：按「当前子任务语义」检索，动态查找历史错题集与最佳实践。

## 多 Agent 记忆机制

在搞懂了单 Agent 的拼接重传机制后，多 Agent 的记忆管理其实就很好理解了。

多 Agent 的记忆，本质上是在前面那套「用户、任务、Agent」记忆体系上，再加一维：角色（`agent_id`）+ 作用域（`scope`）。

可以拆成两个问题来想：

- 每个 Agent 自己的记忆如何隔离？
- 哪些东西要共享，怎么共享才不会乱？

所以，多 Agent 记忆设计的核心，本质上就是在单 Agent 的基础上，加了一个权限管理的维度。

### 1、记忆的作用域：给记忆打上标签

在代码层面，要在多 Agent 场景下复用我们之前的记忆系统，其实只需要在数据库里多加几个字段（Tag）。可以把记忆分成这四层：

- **Global（全局知识）**：公司的图书馆。产品文档、API 手册，所有人随时都能查，基本不怎么变。
- **User（用户画像）**：客户档案。「用户只用 Python」「用户是金融背景」，这些信息对于 Planner、Coder 都是通用的，谁接待用户谁就能看到。
- **Task（任务白板）**：会议室的白板。这是多 Agent 协作的灵魂。当大家为了同一个 `task_id` 努力时，Planner 写下的计划、Researcher 查到的关键摘要，都要写在这个 scope 里。所有参与这个任务的 Agent 都能读到白板上的内容，这样 Coder 才知道 Planner 到底规划了什么。
- **Agent（私有笔记）**：员工的私人笔记本。这是最容易被忽视的一点。Planner 可以在这里记下「最近这类的任务拆解容易出错」，Coder 可以记下「这个库的某个函数有问题」。关键点：Coder 的私人笔记，Planner 不需要看，也看不懂。隔离这些噪音，能极大节省 Token。

### 2、怎么存？其实就是多加一列 ID

不管是存 Redis 还是向量库，本质上就是在每一条记忆上打标签。

以前单 Agent 可能长这样：

```json
{ "content": "...", "time": "..." }
```

多 Agent 只需要扩展成：

```json
{
  "content": "...",
  "user_id": "u_001",      // 属于哪个用户
  "task_id": "t_999",      // 属于哪个任务（如果是跨任务记忆则为空）
  "agent_id": "coder_a",   // 谁写的
  "scope": "task"          // 作用域：user / task / agent
}
```

### 3、一个协作的例子

下面用一个简单形象的流程说明一下三个 Agent（Planner、Researcher、Coder）是怎么配合的。

**第一步：Planner 入场**

- 它向记忆模块发起查询：`scope=user` + `scope=agent(planner)`。
- 它看到了用户说「我要写 Python」，也看到了自己以前总结的「拆解报表任务的模版」。
- 它写下一份 Plan，标记为 `scope = task`。这意味着这份 Plan 被贴到了会议室白板上。

**第二步：Researcher 入场**

- 它不需要看 Planner 的私有经验，它只需要看白板（`scope = task`）。
- 它发现白板上写着：Step 1: 查一下某 API 文档。
- 于是它去执行，查完后，把结果摘要也贴回白板（`scope = task`）。

**第三步：Coder 入场**

- 轮到它干活时，它看一眼白板，上面已经有了 Plan 和 API 文档摘要。
- 它结合这些信息写出代码。如果报错了，它把这个坑记录在自己的 `scope = agent` 里，下次它再遇到类似问题就能避坑，但不会去打扰 Planner。

总结一下：多 Agent 记忆机制并不是什么玄学的群体意识，它在工程上就是一个带权限控制的读写系统。

- **隔离**（依靠 `agent_id`）是为了减少噪音，让每个 Agent 专注自己的专业领域。
- **共享**（依靠 `task_id` 和 `user_id`）是为了对齐目标，确保大家在一个频道上对话。

## 最后

理解了这些底层的拼接逻辑，我们在设计产品时才能更准确地预估 Token 成本、更合理地设计记忆策略。

记忆不是越长越好，而是越准越好。

希望我们也可以找准自己的定位，能够在曲折的路途中遇到任何问题都可以坚定自我地走下去。

## 核心结论

- 所谓「大模型有记忆」，在代码层面就是每一轮都把完整历史重新拼接、重新发送给模型。模型本身始终是失忆的，记忆只是一个挂在外面的特殊检索源。（把握较大）
  永久链接：https://chenxu.xin/writing/agent-memory-essence#memory-is-concatenation
- Function Call 的本质是四步：模型暂停并输出一张调用条子，程序在外部执行，把结果伪装成 tool 消息追加进历史，再重发一次。把 get_weather 换成 search_memory，原理一模一样。（把握较大）
  永久链接：https://chenxu.xin/writing/agent-memory-essence#function-call-is-pause-execute-append-resend
- Agent 和 Chatbot 的关键差异在于，Chatbot 只需记「谁说了什么」，Agent 还必须记「我做过什么」——否则会在失败的工具调用上陷入死循环。（判断）
  永久链接：https://chenxu.xin/writing/agent-memory-essence#agent-needs-behavior-memory
- 多 Agent 记忆不是什么群体意识，工程上就是给每条记忆打上 user / task / agent 作用域标签的读写权限系统。隔离为了降噪，共享为了对齐。（判断）
  永久链接：https://chenxu.xin/writing/agent-memory-essence#multi-agent-memory-is-permissions
- 记忆不是越长越好，而是越准越好。（判断 · 把握较大）
  永久链接：https://chenxu.xin/writing/agent-memory-essence#memory-accuracy-over-length

---

本文出自晨光里的AI（https://chenxu.xin），作者晨旭。
引用时请保留来源链接。文中标注「判断」「假设」的部分是作者的个人看法，不是事实。
