☰
从上下文窗口到MCP:Agent记忆分层设计与工具协同实战
2026/10/8 4:26:03 网站建设 项目流程

从上下文窗口到 MCP,Agent 的记忆到底该怎么设计?这个话题我断断续续研究了大半年,期间把能踩的坑基本踩了个遍。先说结论:Agent 真正要解决的从来不是"记住",而是"该记什么、忘什么、从哪儿调取"。这也是为什么单靠加大上下文窗口永远解决不了 Agent 的记忆问题——窗口再大,也只是把垃圾堆得更高而已。

这篇文章我会从上下文窗口的本质讲起,拆解记忆系统的分层设计,再对比"窗口用完怎么办"的几种常见方案,最后落到 MCP(Model Context Protocol)如何把记忆和工具真正串起来。文章里所有结论都来自我实际跑过的项目和实验,不是纯理论推演,适合正在做 Agent 开发、或者准备给自己的应用接入记忆与工具能力的同学参考。

1. 上下文窗口不是"显存",而是"草稿纸"——问题到底出在哪

很多第一次做 Agent 的朋友,最容易陷入一个误区:上下文窗口不够用,那就换更大的模型,比如从 128K 换到 200K,再不行换 1M。我一开始也是这么想的,直到我把同样一段对话历史塞进不同长度的窗口里跑对比,才意识到问题根本不是"放不放得下",而是**"放进去之后还能不能有效工作"**。

1.1 Token 计量背后的隐性成本

大模型的上下文窗口,单位是 token。你可能会觉得 token 嘛,不就是字数乘以个系数。但实际上,一段对话历史的 token 消耗和你想象中完全不一样。我实测过一个例子:一段 2000 字的用户问题,加上 5000 字的历史对话,再加上工具调用返回的 JSON 结果,累加起来轻松超过 15000 token。而当你用工具时,Agent 框架通常还会把工具的 schema 定义、系统提示词、few-shot 示例全部塞进上下文,这些"结构性开销"往往占到总 token 的 30% 甚至更多。

更隐蔽的是,上下文越长,模型对中间部分的注意力就越稀薄。Google 那篇著名的 "Lost in the Middle" 论文早就揭示过这个现象:模型对开头和结尾的内容感知最强,中间部分的信息最容易丢失。你辛辛苦苦把 50 轮对话塞进窗口,模型真正"看见"的可能只有头尾几轮。所以上下文窗口这件事,物理上限是一回事,有效工作区间是另一回事,两者之间的距离比你想象中大得多。

1.2 "记忆"不等于"对话历史"

这是我最想强调的一点。很多 Agent 框架把 memory 简单地实现为"把历史消息丢回给模型",这本质上只是上下文拼接,不是记忆。真正的记忆应该具备三个能力:

  • 选择性:不是所有对话都值得记,而是要区分哪些是用户偏好、哪些是任务过程、哪些是临时状态。
  • 结构化:记忆不是流水账,而是可以按实体、事件、关系组织起来的事实。
  • 可检索性:需要的时候能快速找到,而不是每次把全部内容重新读一遍。

打个比方,上下文窗口像一张草稿纸,你写满了就得擦掉重来;而记忆应该像笔记本加索引系统,需要用哪页翻哪页。没有索引的笔记本,跟草稿纸没有本质区别。这是理解后面所有方案设计的前提。

2. 记忆分层的核心设计:短期工作台 + 长期档案库 + 结构化知识库

市面上关于 Agent 记忆的讨论很多,热词里也有"双网络记忆模型""长短期记忆网络"这些说法。抛开学术名词,落到实际工程里,我把记忆系统拆成三层,每一层的生命周期、存储方式和调用方式都不一样。

2.1 短期工作台:对话进程内的"工作记忆"

短期记忆对应的是当前会话进行中的状态。比如:用户正在填写一个表单,填到第几步了;用户刚才提到要比较三款产品的价格,这三款分别是什么;Agent 正在执行一个多步骤任务,当前执行到哪个步骤了。

这一层的特点是生命周期短、变化频繁、必须实时更新。我的做法是在内存里维护一个状态对象,每轮对话后更新,并且定期把重要的状态"固化"到长期记忆库。但要注意一个问题:短期记忆不应该完全托管给模型自己"理解",最好用结构化的形式(比如 JSON 状态字段)单独维护,否则对话一长,模型自己都搞不清楚当前处于什么状态了。

我曾经做过一个失败的实验:完全靠模型从对话历史里"悟"出当前状态,结果在 10 轮以内的短会话里表现还行,一旦超过 20 轮,模型就开始频繁翻旧账,甚至把已经完成步骤重新执行一遍。后来改成外部状态管理,每轮结束时由 Agent 框架主动更新一个 state 变量,准确率一下就上来了。

2.2 长期档案库:跨会话的"用户画像"与"项目档案"

长期记忆解决的是"下次对话还能记住这次聊了什么"的问题。这里有个关键设计决策:存原文还是存摘要,还是两者都存?我的实战结论是:都要,但用途不同。

  • 原文(或原文的切片)存入向量数据库,用于语义检索。
  • 摘要(按主题、按对话批次生成)存入结构化存储,用于快速概览和实体关联。

举个例子,用户连续三天都在跟你讨论一个跨境独立站的选品。第一天聊了品类方向,第二天聊了供应商渠道,第三天聊了定价策略。如果你只存摘要,后续检索时可能会丢失具体的数字细节;如果你只存原文,检索时又会被大量无关对话干扰。我的方案是:每次对话结束后,触发一次"记忆沉淀"流程——把对话按主题切块,每块生成 100~200 字的摘要,摘要和原文块同时入库,两者通过对话 ID 关联。检索优先级:先精确匹配摘要,再向量召回原文块,两层互补。

2.3 结构化知识库:实体、关系、偏好与技能

第三层跟前两层不一样,它存的不是"发生过什么",而是"事实是什么"。比如用户的办公地点、常用的设计风格、偏好哪种沟通语气、项目里用到的技术栈、以及用户明确说过"下次不要再问我要这个信息"之类的规则性内容。

这一层最适合用图结构或者至少是带标签的 KV 结构来存。我最初用的是向量数据库,后来发现向量检索对"精确事实"的召回并不可靠——你搜"用户的公司名"这种明确实体,向量相似度反而容易找来一堆近似但不精确的结果。后来我改成混合方案:实体用键值对或图数据库存,描述性内容用向量存。直到今天这个方案依然是我所有 Agent 项目里最稳定的记忆底座。

三层记忆的关系可以简单概括为:短期记忆负责"正在做什么",长期档案负责"之前发生过什么",结构化知识负责"事实是什么"。三者各司其职,才不会出现一边拼命塞历史、一边却连用户名字都记不住的尴尬。

3. 当窗口用完时:五种"外部记忆"方案的取舍实录

热词里有个问题很直白:"大模型上下文窗口用完了怎么办"。我在项目里把主流方案几乎都试了一遍,这五种方案各有各的适用场景,这里用一张表先把结论摆出来,再逐个展开。

方案记忆完整性检索准确性实现成本适用场景
直接截断旧消息低中极低调试、一次性对话
摘要压缩中中低长对话但主题单一
向量检索增强(RAG)高中高中跨会话记忆、知识库问答
结构化数据库读写高高中高用户画像、项目档案、精确事实
混合分层方案高高高生产级 Agent

3.1 截断和摘要压缩:最省事,但最危险

截断就是"窗口满了把最早的对话扔掉"。这在 demo 里可以,生产环境里几乎必然出事。我在一个客服 Agent 项目里试过,前 20 轮用户提到的产品偏好,到了第 30 轮就因为截断丢失了,结果用户问"我刚才说的那个需求你怎么又问我一遍",体验直接崩塌。

摘要压缩比截断高级一点,但也有限。具体做法是:对话长度超过阈值时,把中间的若干轮对话交给模型生成一段摘要,用摘要替换原文。问题是,摘要本身就是有损压缩。模型在生成摘要时,可能觉得某个细节不重要就忽略了,但对后续任务来说那恰恰是关键信息。我曾经在调试时亲眼见过:摘要把用户反复强调的"预算上限 2000 元"弄丢了,Agent 推荐了 3000 元的产品,用户直接终止会话。

3.2 向量检索:RAG 不是银弹

RAG 的思路是:把历史内容切片并向量化,每次请求时根据当前问题做相似度检索,把最相关的几段塞进上下文。这个思路本身没问题,但我在实测中发现几个容易翻车的点。

第一,切片粒度不好把握。切太细,语义被截断,检索到的片段缺乏上下文;切太粗,向量之间的区分度下降,召回一堆弱相关的内容。我试过 200 字、500 字、1000 字三种粒度,最后在综合准确率和召回率后选了 500 左右。第二,向量模型和主模型的语义对齐问题。如果你的向量模型弱,它认为"相关"的内容,主模型可能觉得完全不相关,这一块必须实际测试,不能凭感觉选 embedding 模型。第三,检索结果的排序和去重,如果不做 rerank,前几轮检索的结果混着重复信息,反而给主模型添乱。

但向量检索在跨会话记忆这件事上,仍然是性价比最高的方案。我自己在 Obsidian 知识库 + Agent 的场景里,用向量检索做"历史对话回忆",效果基本够用——前提是你愿意花时间调切片大小和检索 TopK。

3.3 结构化数据库:精确事实的最后防线

如果你需要 Agent 记住的是"用户公司的官网是 example.com"这种精确信息,向量检索是不靠谱的,必须落到数据库里。我的常用做法是:通过一段"记忆提取"提示词,让模型从每轮对话中抽取出结构化的记忆条目(实体、属性、关系),写入数据库。

这套方案跑通之后,Agent 在"记得用户偏好"这个问题上的准确率能到 95% 以上。但它也有代价:需要维护一整套记忆 schema,而且要设计好记忆的冲突处理——用户今天说不喜欢甜口,明天说想吃甜的,到底以哪次为准?我目前的策略是写入时间戳,检索时默认取最新,但如果用户主动回溯到旧记录,也能翻出来。结构化记忆面对的最大问题反而是"该不该记":如果什么信息都入库,库会变成一个巨大的信息垃圾场,检索有效信息的难度堪比从地下室里找一枚硬币。

3.4 混合方案的架构参考

生产环境我最终采用的是三层混合:一个 Redis 服务短期状态,一个向量库存对话切片和摘要,一个关系型数据库存结构化记忆。每次请求的流程大致是:

  1. 先把短期状态里的关键信息写进提示词,保证 Agent 知道"当前任务进行到哪"。
  2. 对用户输入做意图和实体提取,决定要检索长期档案还是查询结构化知识。
  3. 检索结果经过重排和剪裁,拼接到上下文窗口,控制总长度在上限的 60% 以内,给工具调用和生成留出余量。

这样做之后,Agent 的有效工作长度从"聊 20 轮就开始失忆"提升到了"聊 200 轮依然能准确引用 3 天前的一个具体数字"。代价是架构复杂度上了一个量级,但换来的是可用的产品,这笔账值得。

4. MCP 不是"给 Agent 加功能",而是给 Agent 接上了手脚

如果说记忆解决的是"Agent 能记住什么",那工具解决的就是"Agent 能做什么"。工具这个话题绕不开 MCP。我看到热词里大量人在搜"mcp 是什么""ida mcp""dify 浏览器 mcp""codex 接入 figma mcp",看得出这个概念的关注度确实起来了。

4.1 MCP 的协议本质:把"工具调用"标准化

MCP 全称 Model Context Protocol,中文可以叫模型上下文协议。它的核心思路并不复杂:把 AI 应用访问外部工具和数据的方式,从"每家各写各的接口"变成"一套统一的协议"。

传统做法里,如果你要让一个 Agent 读取数据库、发邮件、操作浏览器,你得为每个工具写一套调用代码,每个工具的参数格式和返回格式都不一样。Agent 的提示词里得塞大量工具说明。MCP 出现后,工具提供方只需要按 MCP 规范实现一个 Server,Agent 侧通过 MCP Client 连接,工具列表、参数 schema、调用方式就都标准化了。这就像 USB-C 统一了充电口——之前的乱象是每个设备一个充电口,MCP 之后至少在这个生态内,插上就能用。

MCP 定义了三类核心能力,跟我们的主题关系都很紧密:

  • Tools:Agent 可以调用的函数,比如"查询天气""创建文件""运行 SQL"。
  • Resources:Agent 可以读取的结构化资源,比如某个项目文件、某个数据库表。
  • Prompts:预置的提示词模板,方便复用常见的操作流程。

这三者共同构成了 Agent 与外界交互的"标准母语"。

4.2 从 Memory 到 MCP:工具链也值得"记忆化"

我在前文说记忆要分层,工具的使用其实也需要"记忆"。一个成熟的人类助手,不仅知道你上次让他干什么,还知道"上次你说过这个客户喜欢邮件沟通,不要用微信"。Agent 也应该记住类似的信息。我的做法是把 MCP 工具的使用经验和偏好也写入结构化记忆库,比如:

  • 用户每次让 Agent 生成图片时,都会补充要求"不要带文字"。Agent 下次调用画图工具时,应自动带上这个约束。
  • 用户倾向于用某个特定格式接收数据,比如表格而非段落。

这个"工具使用偏好记忆"听起来简单,实际价值巨大。我在接入了 figma 相关工具和设计系统相关工具之后,靠这个功能把"生成设计稿初稿"的返工率降低了至少 30%。为什么?因为 Agent 记住了用户喜欢什么风格、什么布局,初次生成就更接近目标,而不是每次从零猜。

4.3 实操案例:让 Agent 通过 MCP 读写外部记忆库

接下来给一个可直接参考的例子。假设你的 Agent 框架支持 MCP Client,你想让 Agent 能把自己的长期记忆存到外部数据库里,而不是每次都被上下文窗口卡住。可以这么做:

第一步,准备一个 MCP Server,暴露两个工具:memory_save和memory_search。

{ "tools": [ { "name": "memory_save", "description": "将一条记忆写入长期存储,建议包含实体、属性、值和时间戳", "inputSchema": { "type": "object", "properties": { "entity": { "type": "string" }, "attribute": { "type": "string" }, "value": { "type": "string" }, "timestamp": { "type": "string" } }, "required": ["entity", "attribute", "value", "timestamp"] } }, { "name": "memory_search", "description": "根据实体和属性检索记忆,返回最新记录", "inputSchema": { "type": "object", "properties": { "entity": { "type": "string" }, "attribute": { "type": "string" } }, "required": ["entity"] } } ] }

第二步,在 Agent 的系统提示词里告诉它:"当对话中出现需要长期记忆的用户偏好时(比如用户明确表示喜欢/不喜欢某事物),调用 memory_save 保存;当用户询问之前的信息时,优先调用 memory_search 检索,而不是从对话历史里猜测。"

第三步,把 MCP Server 接到你的 Agent 框架。现在我主要用的是支持 MCP Client 的框架,配置方式通常是声明一个 MCP 服务地址和工具白名单。跑通后,Agent 就能越过上下文窗口的物理限制,按需读写自己的"外部大脑"。

这里有一个关键的架构收益:记忆不再全部塞进上下文,而是变成了 Agent 的一个工具。当记忆是工具时,它天然就是可检索、可更新、可版本化的,这比在提示词里堆 JSON 要可靠得多。热词里"mcp resource 实战"这个方向,我最近正在尝试把整个知识库暴露为 MCP Resource,让 Agent 能像浏览文件系统一样浏览知识库结构,效果比单纯向量检索更可控。

5. 记忆与工具如何协作:技能注册、工具选择与安全边界

把记忆和工具单独讲完之后,真正难的部分来了:两者怎么配合。我见过很多 Agent 项目,记忆系统做得不错,工具也接了一大堆,但放到一起,Agent 反而变得越来越笨。原因很简单:工具越多,模型越不知道在什么时机用哪个工具;记忆越多,模型越不知道怎么挑选和信任何种记忆。

5.1 工具不是越多越好:技能注册与裁剪

MCP 生态越来越丰富,今天接一个浏览器控制、明天接一个数据库查询、后天再接一个流程编排。接多了之后你会发现一个问题:模型每次都要在一大堆工具 schema 里做选择,而工具描述写得不够清晰时,模型会频繁选错。

我的建议是:给 Agent 按"技能"维度组织工具,而不是按工具来源组织。比如"用户画像管理"是一个技能,它内部包含读记忆、写记忆、更新偏好三个工具调用流程;"网页信息采集"是另一个技能,包含打开网页、提取正文、保存摘要三个步骤。每一个技能可以是一段提示词模板加一组工具的组合,甚至可以做成可插拔的 skill 模块。

实际跑下来的效果很明显:工具从 20 多个杂乱 API 变成 5~6 个清晰技能之后,Agent 的工具选择准确率从 70% 左右提升到了接近 90%。而且每个技能自带"什么时候用""什么时候不用"的说明,模型识别起来容易得多。

5.2 记忆驱动的工具选择:让 Agent 记住"上次怎么做的"

这算是我最近比较得意的实践。具体思路是:当 Agent 完成一项任务后,把"执行路径"记录下来——任务类型、用了哪些工具、调用顺序、关键参数、结果如何。下次遇到同类任务时,先检索这些历史执行路径,作为工具调用的参考。

比如用户每周要 Agent 生成一份周报。第一次跑的时候,Agent 可能先取了项目数据,再取了团队成员的更新,然后调用文档生成工具,最后发给用户。这个过程被记录后,第二次用户再说"生成周报",Agent 会先检索到上次的执行路径,直接按同样顺序执行,效率会高很多,也更稳定。

这里最需要注意的是执行路径不能是死板的。上次的数据源如果失效了,Agent 应该能判断出来并切换备选方案,而不是死按旧路径执行。我的做法是在执行路径里给每个步骤加上"期望结果"标注,模型在执行时可以实时校验"这一步的结果是否符合预期",不符合就动态调整。

5.3 安全边界:Agent 能记什么、能调什么,必须有默认禁区

热词里有"agent 安全",这个词被讨论得多,但落到记忆和工具这个具体场景,我觉得有三条安全红线必须划清楚:

  • 记忆安全:用户的隐私数据(身份证号、银行卡、健康信息等)默认不允许进入长期记忆库;如果必须记录,要脱敏处理。我踩过一次坑:测试 Agent 时它把用户的完整地址写入了记忆库,后来被同事发现,直接触发了一轮安全整改。
  • 工具权限安全:每个 MCP 工具必须声明自己需要的权限范围。比如"读取浏览器书签"和"修改浏览器设置"是两回事,前者可以给 Agent 用,后者默认不给。实际项目里我习惯用一个中间层做权限代理,Agent 调用工具前先过一道白名单检查。
  • 记忆写入的共识机制:不是模型说什么记忆就记什么,而是要设置记忆写入的校验逻辑。比如实体必须明确、值不能为空、冲突时需要覆盖旧值时必须有时间戳佐证。这能防止模型在幻觉状态下把不存在的"事实"写入长期库,污染后续所有会话。

信息安全这件事在 Agent 里远比传统软件难做,因为 Agent 的行为不是完全可预测的,只能靠边界兜底。我在所有项目里都会加一句系统提示词:"当你不确定某项操作是否被允许时,停止执行并向用户确认。"这句话帮我挡掉了至少 80% 的潜在风险。

6. 实测体验与避坑清单:从单机 Demo 到生产可用的关键几步

最后这部分,我想用自己在两个真实项目里的踩坑记录来收尾。一个是从零搭建的销售线索管理 Agent,另一个是接入 MCP 工具集的代码审查助手。这两个项目跨度挺大,但暴露出来的问题高度一致,我觉得很有代表性。

6.1 坑一:上下文漂移导致 Agent "精分"

现象:Agent 在长时间会话中,前后回答的口径不一致。比如前面说"预算应该控制在 2000 以内",到后面又推荐了 3000 的产品;前面已经确认过的信息,后面又开始反复追问。

根因:短期记忆和长期记忆的切换没有做好。对话前期用户提到的约束只在短期状态里,状态被后续内容冲刷后,模型就"忘了"。修复方式就是我前面说的:在关键信息出现时,立刻调用 memory_save 写入长期记忆,并在每次会话开始时把检索到的长期记忆注入提示词。此后"口径不一致"的问题基本绝迹。

6.2 坑二:MCP 工具的"幽灵调用"

现象:Agent 时不时会调用一个并不存在的工具,或者调用了某个工具但参数明显不合理,导致下游系统报错。

根因:工具列表太长、描述不清晰时,模型产生了幻觉调用。尤其是当工具名字相似时,比如"get_user_profile"和"update_user_profile",模型可能在处理查询任务时误调用了 update 方法。修复方案:一是精简工具列表,二是每个工具的 description 里明确写明"该工具会修改数据,查询场景禁用",三是增加工具调用的前置条件校验,不满足条件直接拦截。

6.3 可复用的落地清单

如果你要把这套方案搬到自己的项目里,我建议按下面的顺序推进:

  1. 先定义记忆 schema,想清楚哪些信息必须精确存储、哪些信息可以模糊检索。这是地基,地基不牢,后面全部返工。
  2. 从单层记忆起步,先用向量检索跑通跨会话回忆,再逐步引入结构化存储。不要一上来就上三层架构,复杂度会淹没你。
  3. 工具接入按技能分组,不要每接一个新 MCP 就直接丢给 Agent,而是先想清楚这个工具属于哪个技能、技能描述怎么写。
  4. 加一层记忆和工具的日志,每次 Agent 读写记忆、调用工具都留痕。排查问题的时候,这些日志能帮你快速定位是记忆检索错了,还是工具调用错了。
  5. 持续用真实用户对话做回归测试,每调整一次 prompt 或工具配置,都要跑一遍历史测试集,防止修了一个问题带出三个新问题。

我自己在开发中最后保留的一个习惯是:每次版本发布前,用一批固定的"记忆场景测试"(比如让 Agent 记住一个虚构用户的三个偏好,隔一天后再问它)来验证稳定性。这比任何语言层面的优化都实在。

从上下文窗口到 MCP,Agent 的记忆与工具本质上是在解决同一个问题:让模型不再被固定长度的对话历史锁死,而是像一个真正有工作经验的助手那样,按需调用记忆、按需调用工具。这条路没有终点,但每一个跑通的环节,都会让你的 Agent 离"真正可用"更近一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询