
1. 为什么“记忆”和“工具”是 Agent 落地的两道坎做 Agent 开发的人迟早会撞上同一堵墙模型本身很聪明但一放到真实任务里就变得又健忘又笨拙。健忘是因为上下文窗口有限聊到第十轮前三轮的关键信息已经被挤出去了笨拙是因为它只能“说”不能“做”没法查数据库、调接口、读文件。这两个问题不解决Agent 永远只能停留在演示视频里。我最初做 Agent 的时候天真地以为把对话历史全部塞进 prompt 就行了。结果 token 消耗飞涨响应越来越慢而且模型在超长上下文里反而抓不住重点开始胡编。后来才明白记忆不是把什么都记住而是知道该记什么、该忘什么、什么时候把什么捞回来。这跟人脑的工作方式其实很像——你不会记得今天早上刷牙用了几毫升水但你会记得下午三点要开会。工具这一侧同样有坑。早期大家各写各的 function calling格式五花八门换个模型就得重写一遍。直到 MCPModel Context Protocol这类协议出现才把“工具怎么描述、怎么调用、怎么返回”这件事标准化了。它解决的核心问题是让 Agent 和外部能力之间的接口从“私有方言”变成“普通话”。这篇文章我想把这两条线捋清楚一条是记忆的演进路径从最朴素的上下文窗口到外部存储再到检索增强另一条是工具的演进路径从硬编码 function calling 到 MCP 协议。两条线最终会交汇——因为记忆本身也可以被抽象成一种工具而工具调用的结果又需要被记忆管理。理解这个交汇点才算真正摸到了 Agent 工程的门道。适合读这篇的人正在做 Agent 应用、被上下文长度折磨过、或者对 MCP 好奇但还没搞明白它到底解决什么问题的开发者。我会尽量少讲空话多讲我实际踩过的坑和验证过的做法。2. 上下文窗口Agent 记忆的起点也是第一个瓶颈2.1 上下文窗口到底装了什么先把概念对齐。上下文窗口就是模型一次能“看到”的 token 总量包括系统提示、对话历史、工具定义、工具返回结果、当前用户输入全部算在内。比如一个 128K 窗口的模型理论上能装下大约十万个汉字量级的内容听起来很多但实际用起来消耗极快。我做过一个粗略的统计一个中等复杂度的 Agent 系统提示加工具定义轻松吃掉 3000 到 8000 token每轮对话加上工具返回平均 1000 到 3000 token。也就是说128K 窗口在真实任务里可能撑不过三四十轮。如果任务涉及读长文档、查大量数据几轮就爆了。更麻烦的是窗口没满不代表没问题。业界有个说法叫“lost in the middle”——模型对上下文中间部分的信息召回率明显低于开头和结尾。你把关键信息塞在第 50K token 的位置模型很可能视而不见。这不是玄学是注意力机制在长序列上的固有特性。2.2 全量塞入为什么行不通新手最容易犯的错就是“全量塞入”。逻辑很直觉信息越多模型判断越准。但实测下来有三个致命问题。第一是成本。token 是按量计费的全量塞入意味着每一轮都要为所有历史付费。一个跑几十轮的任务成本可能是精简方案的十倍以上。第二是延迟。上下文越长首 token 延迟越高。用户等三秒和等十秒体验是天壤之别。第三是质量下降。这点最反直觉。信息过载会让模型分心它可能抓住一个无关的细节大做文章反而忽略了真正的任务目标。我遇到过模型因为历史里提过一句“用户喜欢蓝色”就在生成报告时硬塞蓝色主题完全跑偏。所以正确的思路不是“塞更多”而是“管得更聪明”。这就引出了记忆管理的核心问题什么信息值得留在窗口里什么应该挪到外部什么时候再捞回来。2.3 滑动窗口与摘要压缩的取舍最朴素的记忆管理是滑动窗口只保留最近 N 轮对话更早的直接丢弃。实现简单成本可控但代价是丢失长期信息。用户第一轮说的偏好到第二十轮就没了。稍微进阶一点的是摘要压缩把早期对话用模型总结成一段简短摘要替换掉原始内容。这样能保留要点又省 token。但摘要本身有信息损失而且摘要质量依赖模型能力总结得不好反而误导后续推理。我的经验是滑动窗口加摘要的组合拳在多数场景够用最近 5 到 10 轮保留原文更早的压缩成结构化摘要比如按“用户偏好”“已确认事实”“待办事项”分类。关键是摘要要结构化不能是一段流水账否则模型还是抓不住重点。但这两招都治标不治本。真正的长期记忆得靠外部存储。3. 从窗口到外部存储Agent 长期记忆的几种实现路径3.1 向量数据库语义检索的主力外部记忆最主流的方案是向量数据库。原理不复杂把每段记忆转成 embedding 向量存起来需要的时候用当前查询去检索最相似的若干条塞回上下文。这样记忆容量理论上无限检索也快。我实际用下来向量检索的效果高度依赖几个细节。分块策略是第一道关。把一段对话按固定长度切还是按语义边界切结果差别很大。我倾向于按“一个完整的事件或结论”切块而不是机械按字数。比如“用户确认预算上限是五万”这种完整信息不该被切成两半。embedding 模型的选择也很关键。通用 embedding 在专业领域比如医疗、法律、代码表现会打折有条件的话用领域数据微调一下召回率能提升不少。还有个容易被忽略的点元数据过滤。光靠语义相似度检索经常捞回一堆“看起来像但实际无关”的记忆。加上时间、类型、来源等元数据过滤精度会好很多。比如检索“用户上次提到的预算”可以限定只搜“用户偏好”类型的记忆而不是全库乱搜。3.2 结构化记忆把关键事实单独存向量检索擅长模糊匹配但对精确事实的存储和更新不友好。用户说“我预算五万”后来又改成“八万”向量库里两条都存着检索时可能捞回旧的那条。所以我会把关键事实单独抽出来存成结构化的键值对或小表。比如用户画像、任务状态、已确认约束这些用结构化存储更新时直接覆盖检索时精确命中。向量库负责“模糊的、叙述性的”记忆结构化存储负责“精确的、状态性的”记忆两者分工明确。这个思路其实借鉴了传统软件工程里“缓存加数据库”的分层思想。热数据、精确数据放结构化层冷数据、叙述数据放向量层。3.3 记忆的写入、检索与遗忘策略记忆管理最难的不是存而是决定什么时候写、什么时候读、什么时候删。写入时机上我一般设几个触发点用户明确表达偏好或约束时、任务状态发生变更时、一轮子任务完成时。不是每句话都值得记记太多反而稀释了有效信息。检索时机上不是每轮都检索。我的做法是在任务开始、话题切换、或者模型主动表示“需要回忆”时才触发检索。频繁检索既费钱又容易引入噪声。遗忘策略最容易被忽视。记忆库无限膨胀检索精度会下降成本会上升。我会给记忆设过期时间或者按访问频率做衰减——长期没被检索到的记忆降低权重甚至归档。这跟人脑的记忆衰减机制异曲同工。提示记忆系统的评估不能只看“检索准不准”还要看“该记的有没有记、该忘的有没有忘”。我建议单独建一个测试集模拟多轮任务检查关键信息在长对话后是否还能被正确召回。4. 工具调用Agent 从“会说”到“会做”的关键一跃4.1 Function Calling 的工作机制工具调用的本质是让模型输出一个结构化的“调用意图”由外部代码执行再把结果喂回模型。早期各家实现不同但核心流程一致在 prompt 里描述有哪些工具、每个工具接受什么参数模型判断需要调用时输出一个 JSON 描述调用哪个工具、传什么参数。这里有个关键认知模型本身不执行任何工具它只是生成调用参数。真正执行的是你的代码。理解这一点就不会对“模型怎么知道数据库密码”这类问题感到困惑——密码在你的执行代码里模型根本接触不到。参数生成的准确性是第一个难点。模型可能传错类型、漏传必填项、或者编造不存在的参数值。我的做法是在工具描述里把参数约束写清楚包括类型、范围、示例并且在执行层做严格校验校验失败就把错误信息返回给模型让它重试。4.2 工具描述写得好调用成功率翻倍我踩过最大的坑就是工具描述写得太随意。一开始我觉得描述就是给人看的注释随便写写。结果模型频繁误用工具或者该调用时不调用。后来我总结了几条经验。描述要写“什么时候用”而不只是“这是什么”。比如一个查询订单的工具光写“查询订单信息”不够要写“当用户询问订单状态、物流进度、或需要根据订单号获取详情时使用”。这样模型才知道触发条件。参数描述要给例子。模型对抽象描述的理解不如对具体例子敏感。与其写“日期参数”不如写“日期参数格式 YYYY-MM-DD例如 2024-01-15”。工具数量要克制。我见过有人一口气塞几十个工具结果模型选择困难调用准确率暴跌。经验值是单次暴露给模型的工具不超过 10 到 15 个多了就分组或者按场景动态加载。4.3 多工具编排与错误处理真实任务往往需要多个工具配合。比如“帮我查一下上周的销售数据并生成报告”可能涉及查数据库、调分析接口、生成文档三个工具。模型需要自己规划调用顺序处理中间结果。这里最容易出问题的是错误处理。工具调用失败时如果直接把原始错误抛给模型它可能一脸懵。更好的做法是把错误转成模型能理解的描述比如“查询失败原因是订单号格式不正确请确认后重试”。模型看到这种信息才知道怎么调整。还有一个坑是循环调用。模型可能陷入“调用工具、结果不满意、再调用、还不满意”的死循环。我会设一个最大调用轮次超过就强制中断返回当前最优结果并说明情况。5. MCP 协议工具生态从“私有方言”走向“普通话”5.1 MCP 到底解决了什么问题在 MCP 出现之前每个模型厂商、每个 Agent 框架都有自己的工具定义格式。你为一个模型写的工具换个模型就得重写。这就像每个城市都有自己的电源插头标准出差得带一堆转换头。MCP 的思路是定义一套标准协议把“工具提供方”和“工具使用方”解耦。工具提供方按 MCP 规范暴露能力任何支持 MCP 的客户端都能调用不用关心底层是哪个模型。这跟 USB 接口的意义是一样的——统一标准即插即用。它的架构是客户端-服务端模式。MCP 服务端暴露三类能力工具可执行的操作、资源可读取的数据、提示预设的模板。客户端也就是 Agent 运行时连接服务端动态发现有哪些能力可用然后按需调用。5.2 MCP 的三类原语工具、资源与提示工具是最常用的对应可执行的操作比如查数据库、发请求、算数据。每个工具有名字、描述、参数 schema跟 function calling 的定义类似但格式标准化了。资源对应可读取的数据比如文件内容、数据库记录、API 返回。它和工具的区别在于工具是“做一件事”资源是“读一份数据”。这个区分让 Agent 能更清晰地表达意图。提示是预设的模板服务端可以提供一些常用的提示词模板客户端直接调用。这个原语用得相对少但在标准化场景下很有价值。我实际用下来MCP 最大的好处是工具可以跨项目复用。以前每个项目都要重新写一遍数据库查询工具现在写一个 MCP 服务端所有项目都能连。维护成本大幅下降。5.3 自己动手写一个 MCP 服务端的要点写 MCP 服务端不复杂但有几个细节容易踩坑。第一是能力声明的粒度。不要把一个大功能拆成几十个细碎工具也不要一个工具包揽所有事。我的经验是按“用户意图”划分一个工具对应一类明确的操作。第二是错误返回的规范性。MCP 对错误有标准格式要按规范返回客户端才能正确处理。我见过有人直接抛异常导致客户端解析失败。第三是鉴权和权限。MCP 服务端可能暴露敏感操作必须做好鉴权。别以为本地运行就安全Agent 可能被诱导调用不该调用的工具。第四是性能。MCP 服务端如果响应慢整个 Agent 链路都会卡。我一般会给耗时操作加超时和缓存。注意MCP 服务端的工具描述同样要遵循“什么时候用”的原则。协议标准化了格式但描述质量还是靠人来保证。6. 记忆与工具的交汇把记忆本身抽象成一种能力6.1 记忆即工具的设计思路想通一件事之后整个架构会清爽很多记忆操作本身就可以抽象成工具。存记忆是一个工具检索记忆是一个工具遗忘也是一个工具。这样 Agent 就能像调用其他工具一样管理自己的记忆而不是把记忆逻辑硬编码在框架里。这个思路的好处是灵活。Agent 可以根据任务需要主动决定“这条信息值得记”“我需要回忆一下之前的约定”。它把记忆的主动权交给了模型而不是由外部规则强制管理。当然这也有风险。模型可能滥用记忆工具什么都记或者频繁检索。所以我会在工具描述里加约束比如“只在信息具有长期价值时调用存储”“检索前先判断是否真的需要”。6.2 用 MCP 统一记忆服务的接口既然记忆是工具那它天然适合用 MCP 来标准化。我可以写一个 MCP 记忆服务端暴露 store、retrieve、forget 三个工具任何支持 MCP 的 Agent 都能连上使用。这样做的好处是记忆服务独立于 Agent 框架。换框架、换模型记忆服务不用动。而且多个 Agent 可以共享同一个记忆服务实现跨会话、跨应用的记忆互通。我实际搭过一套结构大概是底层是向量库加结构化存储中间是记忆管理逻辑分块、去重、衰减上层是 MCP 接口。Agent 通过 MCP 调用完全不用关心底层实现。6.3 一个可落地的组合架构把前面的东西串起来我常用的架构是这样的上下文层滑动窗口加结构化摘要管理当前会话的短期记忆。记忆服务层MCP 服务端提供 store、retrieve、forget 工具底层接向量库和结构化存储。工具层业务工具通过 MCP 暴露和记忆服务并列。编排层Agent 运行时负责规划、调用工具、管理上下文。这个架构的关键是分层清晰、接口标准。每层只关心自己的事通过标准协议通信。这样任何一层都可以独立替换或升级不会牵一发动全身。我踩过的坑是早期把记忆逻辑和业务逻辑混在一起代码越写越乱改一处崩三处。分层之后维护成本直线下降。7. 实测中的几个坑与应对经验7.1 记忆检索的“相似但不相关”问题向量检索最烦人的是捞回一堆语义相似但实际无关的记忆。比如用户问“上次那个方案”检索可能捞回所有带“方案”二字的记忆但用户指的是哪一个模型得自己猜。我的应对是加时间衰减和类型过滤。近期记忆权重更高同类型的记忆优先。另外在检索结果里带上时间戳和来源让模型有更多线索判断。还有一个技巧是让模型先澄清再检索。如果查询太模糊与其瞎猜不如让模型先问用户“您指的是哪个方案”。这比检索一堆无关记忆再让模型纠结要好。7.2 工具调用的参数幻觉模型编造参数值是常见问题。比如调用查询工具时编一个不存在的订单号。这在演示时看不出来上线后就是灾难。我的做法是执行层严格校验加友好报错。参数格式不对、值不在范围内、必填项缺失都在执行层拦住返回明确的错误描述。模型看到具体错误通常能自我修正。另外关键参数不要让模型自由生成。比如用户 ID、订单号这类能从上下文或用户输入里提取的就提取提取不到就让模型问用户而不是让它编。7.3 长任务中的记忆一致性长任务里记忆可能前后矛盾。用户早期说预算五万后期说八万如果两条都存着模型可能用错。我的方案是结构化记忆做版本管理。同一类事实只保留最新版本旧版本归档。检索时默认只取最新需要历史对比时才取旧版本。这样保证模型看到的是当前有效的信息。还有个细节是记忆的时效标注。每条记忆带上“有效期”或“最后确认时间”模型可以据此判断信息是否还可靠。8. 我个人的一些实践体会做了这么多 Agent 项目我最大的体会是记忆和工具不是两个独立模块而是一体两面。记忆是 Agent 对内的能力工具是 Agent 对外的能力两者都需要标准化的接口和清晰的分层。另一个体会是不要过度设计。我见过有人一上来就搞复杂的记忆图谱、多级缓存结果维护成本高得吓人效果还不如简单的滑动窗口加向量检索。先从简单方案做起遇到瓶颈再升级这是更务实的路径。MCP 这类协议的价值会越来越明显。当工具生态标准化之后Agent 的能力边界就不再取决于你写了多少代码而取决于你能连上多少服务。这跟当年 App 生态的爆发是一个逻辑——基础设施标准化了上层应用才能百花齐放。最后分享一个小技巧给记忆和工具都加日志。每次检索、每次调用都记下来。出问题时日志是唯一的真相来源。我靠日志定位过好几次“模型为什么突然变傻”的问题最后发现都是记忆检索捞回了错误信息或者工具返回了意外格式。没有日志这些坑能查一整天。