
1. 从“hindsight”这个词说起为什么记忆是 Agent 最被低估的能力“hindsight”这个词本身很有意思字面意思是“事后的洞察力”也就是我们常说的“后见之明”。但把它放到 AI Agent 和 LLM 的语境里它指向的是一个非常具体、非常硬核的技术命题Agent 如何记住过去发生过的事情并在后续决策中真正用上这些记忆。我接触过不少 Agent 项目从简单的工具调用到复杂的多轮任务编排几乎所有人一开始都会把精力砸在“让 Agent 能做事”上——接 MCP、写 function call、调 LLM 接口。但跑一段时间后就会发现一个尴尬的现实Agent 每次对话都像失忆一样用户昨天说过的偏好、上周处理过的同类任务、上个月踩过的坑它一概不记得。你不得不把大量上下文反复塞进 prompttoken 烧得飞快效果还不稳定。这就是“hindsight”要解决的核心问题。它不是一个具体的开源库名字而是一类能力的统称让 Agent 具备对历史交互的持久化记忆并且能在正确的时机检索、注入、更新这些记忆。关键词里出现的 agent、memory、LLM、MCP基本勾勒出了这个方向的技术栈轮廓——Agent 是主体Memory 是能力LLM 是推理引擎MCP 是连接外部世界的协议层。这篇文章适合谁看如果你正在做 Agent 开发不管是基于 LangChain、AutoGPT 还是自己手搓的框架只要你遇到了“Agent 记不住东西”“上下文窗口不够用”“多轮任务状态丢失”这类问题那这篇内容就是写给你的。我会从记忆的本质讲起拆解 working memory、episodic memory、semantic memory 在 Agent 里的落地方式然后给出可操作的存储方案、检索策略和 MCP 集成思路最后分享几个我在实际项目中踩过的坑。2. Agent Memory 的本质不是存聊天记录而是存“可复用的决策依据”2.1 为什么把对话历史全塞进 context 是最蠢的做法很多人对 Agent 记忆的第一反应是把历史对话存下来下次拼到 prompt 里不就行了这个思路在 demo 阶段没问题但一上生产就崩。原因有三第一token 成本线性增长。假设每次对话平均 2000 token聊 50 轮就是 10 万 token就算用 128k 上下文的模型也撑不了多久。而且每次请求都要重新计算这些 token 的 attention延迟和费用都受不了。第二信噪比急剧下降。历史对话里 90% 是寒暄、确认、重复表述真正有价值的决策信息可能就一两句。全量塞进去LLM 反而容易被无关信息干扰出现“注意力涣散”。第三无法跨会话复用。用户今天在会话 A 里说了“我偏好用 Python 而不是 JavaScript”明天开了一个新会话 B如果记忆只存在会话 A 的 context 里B 完全不知道这个偏好。所以正确的做法是把记忆从 context 里剥离出来做成独立的存储层按需检索、按需注入。这就是 Agent Memory 系统的核心设计思想。2.2 Working Memory、Episodic Memory、Semantic Memory 的三层分工借鉴认知科学的分类Agent 记忆通常分三层记忆类型对应概念存储内容生命周期典型实现Working Memory工作记忆当前任务的临时状态、中间变量单次任务内存变量、RedisEpisodic Memory情景记忆具体交互事件、时间戳、上下文数天到数月向量数据库、时序数据库Semantic Memory语义记忆抽象知识、用户偏好、领域规则长期知识图谱、结构化存储Working Memory 最好理解就是 Agent 在执行一个任务时的“草稿纸”。比如用户让 Agent 订机票Working Memory 里存的就是出发地、目的地、日期、候选航班列表这些临时数据。任务结束这些数据就可以丢弃。Episodic Memory 是“我什么时候做了什么”。比如“2024-03-15 用户让我订了一张北京到上海的机票选择了国航 CA1501”。这种记忆带时间戳适合用向量数据库存储检索时按语义相似度加时间衰减来排序。Semantic Memory 是“我知道什么”。比如“这个用户偏好靠窗座位”“这个用户是某公司的员工出差标准是经济舱”。这种记忆是抽象的、去时间化的适合用结构化方式存储检索时直接按 key 查。注意三层记忆不是互斥的而是协同的。Working Memory 在任务结束后有价值的部分会沉淀到 Episodic MemoryEpisodic Memory 经过多次重复后可以抽象成 Semantic Memory。2.3 从“存什么”到“怎么取”检索策略决定记忆系统的成败存得好不如取得准。我见过太多项目记忆存了一大堆但检索出来的全是无关信息反而拖累了 Agent 表现。检索策略的核心是相关性排序通常综合考虑三个维度语义相似度用 embedding 计算 query 和记忆条目的余弦相似度这是基础。时间衰减越近的记忆权重越高。可以用指数衰减函数比如weight exp(-λ * Δt)λ 根据业务调整。重要性评分记忆条目本身有一个重要性分数可以由 LLM 在存储时打分也可以由用户显式标记。最终得分可以是三者的加权和。我在实际项目里常用的权重是语义相似度 0.6时间衰减 0.25重要性 0.15。这个比例不是固定的需要根据业务场景调。比如客服场景时间衰减要调高因为用户最近的问题更相关知识问答场景语义相似度要调高。3. 落地一套 Agent Memory 系统存储选型与数据结构设计3.1 向量数据库不是唯一选择但通常是最优解说到记忆存储很多人第一反应是上向量数据库。Pinecone、Weaviate、Qdrant、Milvus、Chroma选择很多。但我要说的是向量数据库不是必须的取决于你的记忆规模和检索需求。如果记忆条目少于 1 万条用 SQLite 本地 embedding 模型完全够用甚至可以直接用 numpy 做余弦相似度计算。我有个项目就是用一个 200 行的 Python 脚本把记忆存在 SQLite 里embedding 用 sentence-transformers 本地跑检索延迟在 50ms 以内成本为零。当记忆条目超过 10 万或者需要多租户隔离、高并发检索时才需要考虑专业向量数据库。选型时重点看三个指标召回率ANN 算法的召回率能不能到 95% 以上。过滤能力能不能在向量检索的同时做 metadata 过滤比如只检索某个用户的记忆。运维成本自托管还是云服务团队有没有能力维护。下面是一个典型的记忆表结构设计用 PostgreSQL pgvector 举例CREATE TABLE agent_memory ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), agent_id VARCHAR(64) NOT NULL, user_id VARCHAR(64), memory_type VARCHAR(32) NOT NULL, -- working / episodic / semantic content TEXT NOT NULL, embedding VECTOR(1536), importance FLOAT DEFAULT 0.5, access_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), last_accessed_at TIMESTAMP DEFAULT NOW(), metadata JSONB ); CREATE INDEX ON agent_memory USING ivfflat (embedding vector_cosine_ops); CREATE INDEX ON agent_memory (agent_id, user_id, memory_type);这个设计里几个关键点importance和access_count用于后续的检索排序和记忆淘汰last_accessed_at用于时间衰减计算metadata存灵活的业务字段比如任务 ID、会话 ID、标签等。3.2 记忆的写入时机不是每句话都值得记什么时候往记忆系统里写数据这个问题比存储本身更重要。我的经验是不要每轮对话都写而是按事件粒度写。具体来说以下几种情况触发写入任务完成时一个多轮任务结束后把任务目标、关键决策、最终结果压缩成一条 Episodic Memory。用户显式表达偏好时比如“我以后都用中文回复”“我不喜欢太长的答案”这种直接写入 Semantic Memory。Agent 做出重要决策时比如选择了某个工具、放弃了某个方案记录决策依据供后续复盘。错误发生时记录错误类型、上下文、修复方式避免重复踩坑。写入前最好用 LLM 做一次“记忆提炼”把冗长的对话压缩成简洁的陈述句。比如把“用户说他想订机票然后问有没有靠窗的我说有他说那订靠窗的吧”压缩成“用户偏好靠窗座位”。3.3 记忆的淘汰与遗忘不是所有记忆都值得留记忆系统不能只增不减否则检索质量会越来越差。淘汰策略通常有几种LRU最近最少使用淘汰最久没被访问的记忆。重要性阈值importance 低于某个值的记忆定期清理。时间窗口只保留最近 N 天的 Episodic MemorySemantic Memory 长期保留。容量上限每个用户最多保留 M 条记忆超出时按综合得分淘汰。我在项目里用的是组合策略Episodic Memory 保留 90 天Semantic Memory 永久保留但定期做去重合并Working Memory 任务结束即清理。每季度做一次全量记忆的“压缩”把多条相关记忆合并成一条更高层的抽象记忆。4. MCP 在记忆系统里的角色连接、暴露与标准化4.1 MCP 不是记忆存储而是记忆的“接口层”MCPModel Context Protocol最近很火但很多人对它的定位有误解。MCP 本身不存储记忆它是一套标准化的协议让 LLM 能够以统一的方式调用外部工具和数据源。在记忆系统里MCP 的价值在于把记忆的读写操作标准化成工具让任何支持 MCP 的 Agent 都能接入。举个例子你可以把记忆系统封装成几个 MCP toolmemory_write写入一条记忆memory_search按 query 检索记忆memory_update更新记忆的重要性或内容memory_forget删除指定记忆这样不管你的 Agent 是基于 Claude、GPT 还是本地模型只要它支持 MCP就能通过统一的接口访问记忆。这比每个 Agent 框架自己实现一套记忆接口要优雅得多。4.2 用 MCP 封装记忆服务的实操步骤下面是一个用 Python 实现 MCP 记忆服务的简化示例。假设你已经有了一个基于 pgvector 的记忆存储层from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationOptions import mcp.server.stdio import mcp.types as types server Server(memory-service) server.list_tools() async def handle_list_tools(): return [ types.Tool( namememory_write, description写入一条 Agent 记忆, inputSchema{ type: object, properties: { content: {type: string}, memory_type: {type: string, enum: [episodic, semantic]}, importance: {type: number, default: 0.5} }, required: [content, memory_type] } ), types.Tool( namememory_search, description检索相关记忆, inputSchema{ type: object, properties: { query: {type: string}, top_k: {type: integer, default: 5} }, required: [query] } ) ] server.call_tool() async def handle_call_tool(name: str, arguments: dict): if name memory_write: # 调用存储层写入 memory_id await store.write( contentarguments[content], memory_typearguments[memory_type], importancearguments.get(importance, 0.5) ) return [types.TextContent(typetext, textf记忆已写入ID: {memory_id})] elif name memory_search: results await store.search( queryarguments[query], top_karguments.get(top_k, 5) ) return [types.TextContent(typetext, textformat_results(results))]这个服务跑起来后Agent 端只需要配置 MCP server 地址就能自动发现并调用这些工具。实测下来从零到跑通大概半天时间主要成本在存储层的实现上。4.3 MCP 记忆服务的性能与安全考量MCP 走的是 stdio 或 SSE 传输本地 stdio 延迟极低但 SSE 走网络的话要考虑延迟。如果记忆检索频繁建议在 Agent 端做一层本地缓存比如用 LRU cache 缓存最近检索过的记忆。安全方面MCP 记忆服务必须做租户隔离。每个 Agent 或每个用户只能访问自己的记忆不能跨租户检索。实现上就是在存储层强制加agent_id和user_id过滤条件MCP tool 的入参里不要暴露这些字段而是从连接上下文里取。另外记忆内容可能包含敏感信息写入前要做脱敏处理。比如用户说“我的手机号是 138xxxx”写入记忆时应该存成“用户提供了手机号”而不是原样存储。5. 实测中的坑记忆系统上线后才会暴露的问题5.1 记忆污染错误记忆比没有记忆更可怕这是我最想强调的一个坑。记忆系统一旦写入错误信息后续所有基于这条记忆的决策都会跑偏。比如用户说“我下周三要去上海”Agent 记成了“用户要去上海”结果下周三用户其实是要去杭州Agent 就订错了票。防范记忆污染的关键是写入前校验。我的做法是所有写入记忆的内容先用 LLM 做一次“事实一致性检查”确认没有歧义、没有与已有记忆冲突。如果冲突要么拒绝写入要么标记为“待确认”。另外记忆条目要保留来源追溯。每条记忆都记录它是从哪次对话、哪个时间点提取的。当发现记忆错误时可以追溯到源头批量修正。5.2 检索延迟向量检索不是免费的向量检索看起来很快但当记忆条目到百万级ANN 索引的构建和查询都会有明显延迟。我遇到过检索 P99 延迟超过 500ms 的情况原因是索引没有调优nprobe参数设得太小导致要扫描大量聚类。调优经验pgvector 的ivfflat索引lists参数建议设为sqrt(行数)probes设为sqrt(lists)。比如 100 万条记忆lists1000probes32。这样能在召回率和延迟之间取得平衡。如果延迟还是高可以考虑两级检索先用关键词或 metadata 做粗筛再用向量做精排。或者用量化技术把 embedding 从 float32 压到 int8内存占用和计算量都能降一个数量级。5.3 记忆与 context 的边界什么时候该注入什么时候不该记忆检索出来后不是全部塞进 prompt 就好。注入太多记忆会挤占 context 空间还会干扰 LLM 判断。我的经验是每次注入的记忆不超过 5 条总 token 控制在 500 以内。注入时机也很关键。不是每轮对话都要检索记忆而是在以下时机触发用户提出新任务时Agent 需要做决策时用户提到“之前”“上次”“以前”等关键词时其他时候让 Agent 专注当前对话即可。这需要 Agent 框架支持“按需检索”而不是每轮都自动检索。6. 从 hindsight 到 foresight记忆系统的演进方向6.1 记忆的主动遗忘与隐私保护现在大家都在做“记住更多”但未来一定会转向“该忘就忘”。用户有权要求 Agent 忘记某些信息这不仅是隐私合规的要求也是提升记忆质量的手段。我预计未来记忆系统会内置遗忘策略引擎支持按时间、按类型、按用户指令自动清理。6.2 跨 Agent 的记忆共享与联邦现在每个 Agent 的记忆是孤立的但用户可能同时用多个 Agent 处理不同任务。如果这些 Agent 能共享记忆体验会好很多。比如一个 Agent 知道了用户的出差偏好另一个 Agent 在订酒店时就能直接用上。这需要一套联邦记忆协议在保护隐私的前提下实现记忆的安全共享。6.3 记忆的可解释性与审计当 Agent 基于某条记忆做出决策时用户应该能知道“它为什么这么做”。这要求记忆系统支持决策追溯从最终输出反查到用了哪些记忆这些记忆又是从哪来的。这在医疗、金融等强监管场景里是刚需。我在实际项目里的体会是记忆系统不是一蹴而就的而是随着 Agent 使用不断迭代的。一开始可以很简单甚至用 JSON 文件存都行关键是先把“写入-检索-注入”这个闭环跑通然后再逐步优化存储、检索和淘汰策略。踩过几次坑之后你会对“什么该记、什么该忘、什么时候用”有越来越清晰的直觉这种直觉比任何框架都值钱。