资讯动态

基于MCP与Docker构建LLM Agent长期记忆系统:从工作记忆到长期记忆的工程实践

发布时间:2026/10/3 9:36:08 来源:尧图企业网站定制
1. 从“hindsight”说起为什么我们需要给Agent装上一双“后视之眼”“hindsight”这个词直译过来就是“后见之明”。放在人类身上它描述的是一种再普通不过的能力你做完一件事回头复盘突然意识到“当时要是那样做就好了”。这种能力看似廉价甚至常常伴随着懊悔但它恰恰是智能行为中极其关键的一环——没有它一个系统就只能在同一个坑里反复跌倒。把这个概念搬到AI Agent身上事情就变得非常有意思了。当前绝大多数基于LLM的Agent本质上都是“失忆”的。你跟它聊了二十轮它可能还记得上下文窗口里的内容但一旦会话结束或者上下文被截断它就彻底忘了你是谁、你之前交代过什么偏好、上次那个任务为什么失败。它永远活在“当下”没有过去也就谈不上真正的成长。“hindsight”这个项目标题指向的正是这个痛点给Agent构建一套事后记忆机制。它不是简单地把聊天记录存进数据库而是要让Agent能够从过去的交互中提取经验、形成可复用的记忆并在未来的决策中主动调用这些记忆。换句话说它试图回答一个核心问题Agent如何像人一样从“事后诸葛亮”变成“事前有准备”这个方向之所以在当下特别值得聊是因为整个行业正在从“单次对话”向“长期陪伴型Agent”演进。无论是个人助理、客服机器人还是自动化工作流中的决策节点大家都不再满足于“一问一答”而是希望Agent能记住用户的习惯、项目的上下文、历史任务的成败教训。而支撑这一切的技术底座恰好就是热词里反复出现的几个关键词agent memory、LLM、MCP、Docker。这篇文章适合谁看如果你正在做Agent相关的产品开发或者对LLM应用架构感兴趣又或者你只是好奇“为什么我的Agent总是记不住事”那接下来的内容应该能给你一些可以直接抄作业的思路。我会从整体设计、核心细节、实操落地、问题排查四个层面把“hindsight”这类Agent记忆系统的构建逻辑拆开来讲尽量做到既有原理也有能跑起来的代码和配置。2. 整体设计与思路拆解Agent记忆系统到底该怎么搭2.1 为什么“存聊天记录”远远不够很多人第一次做Agent记忆第一反应就是把对话历史存进数据库下次对话时捞出来塞进prompt。这个做法在Demo阶段没问题但一旦对话轮次上去就会立刻撞墙。原因很简单上下文窗口是有限的而记忆是无限的。你不可能把过去一百次对话全部塞进prompt那样token成本会爆炸而且模型对超长上下文的注意力也会稀释关键信息反而被淹没。所以“hindsight”这类系统的第一个设计决策就是把记忆分成不同的层次。我在实际项目中通常会分成三层工作记忆Working Memory当前会话的上下文存在内存或Redis里生命周期就是这次会话。它负责维持对话的连贯性让Agent知道“刚才聊到哪了”。短期记忆Short-term Memory最近几次会话的摘要或关键事件存在关系型数据库或文档数据库里生命周期可能是几天到几周。它负责让Agent知道“最近发生了什么”。长期记忆Long-term Memory从大量历史交互中提炼出来的经验、偏好、事实性知识存在向量数据库里生命周期是永久的。它负责让Agent知道“用户是谁、什么做法有效、什么坑不能踩”。这个分层不是拍脑袋定的它对应的是人类记忆的认知模型。工作记忆就像你脑子里正在想的事短期记忆像你记得昨天吃了什么长期记忆像你记得自己的名字和专业技能。Agent要表现得“聪明”这三层缺一不可。2.2 为什么选MCP作为记忆的接入协议热词里反复出现“MCP”而且有人问“MCP是软件协议还是硬件协议那个概念”。这里先澄清一下MCPModel Context Protocol是一个软件协议它定义的是LLM应用如何与外部工具、数据源进行标准化交互。你可以把它理解成“AI世界的USB接口”——不管你是数据库、文件系统、还是某个API只要实现了MCPLLM就能用统一的方式去调用。在“hindsight”这个场景里MCP的价值在于解耦。记忆的存储、检索、更新逻辑可以封装成一个独立的MCP Server而Agent本身只需要通过MCP协议去调用它。这样做的好处是Agent的代码不需要关心记忆存在哪、怎么查它只管“我要回忆一下上次类似任务是怎么做的”MCP Server负责返回结果。记忆系统可以独立升级、独立扩展不会因为换了向量数据库就把Agent代码重写一遍。多个Agent可以共享同一个记忆MCP Server实现“团队记忆”。我试过把记忆逻辑直接写在Agent里也试过用MCP拆出去实测下来后者的维护成本低很多。尤其是当你的Agent需要接入多个工具时MCP的统一接口能让整个架构清爽不少。2.3 Docker在其中的角色为什么不是“可选项”而是“必选项”热词里“docker安装”“docker compose”“docker desktop”出现频率极高说明很多人正在被环境问题折磨。在Agent记忆系统里Docker不是锦上添花而是基础设施。原因有三个第一依赖隔离。向量数据库比如Milvus、Qdrant、关系型数据库比如MySQL、PostgreSQL、缓存Redis这些东西如果直接装在宿主机上版本冲突能让人崩溃。Docker Compose一拉各服务互不干扰。第二一键复现。你在这台机器上跑通的配置换台机器只要docker compose up就能复现。这对于团队协作和部署来说省掉的是无数个“在我机器上是好的”的扯皮时间。第三资源控制。记忆系统里的向量检索是吃内存的Docker可以限制每个容器的资源上限避免一个服务把整台机器拖垮。所以我的建议很直接先把Docker环境搞稳再谈Agent记忆。后面我会给出具体的Compose配置。3. 核心细节解析与实操要点记忆的写入、检索与遗忘3.1 记忆写入不是所有对话都值得记住一个常见的误区是“把所有东西都存下来”。我踩过这个坑早期版本把每一轮对话都写进向量库结果检索出来的全是“好的”“谢谢”“明白了”这种废话真正有用的信息被淹没在噪声里。“hindsight”的核心思路是选择性写入。具体来说我会在Agent的对话流程里加一个“记忆提取”步骤用LLM来判断当前这轮交互是否包含值得长期记住的信息。判断标准可以归纳成三个问题这轮对话里有没有出现新的事实比如用户说“我对花生过敏”有没有出现明确的偏好比如用户说“以后回答尽量简短”有没有出现任务成败的关键教训比如某个API调用方式导致失败换了一种方式成功如果三个都没有那就只留在工作记忆里不往长期记忆写。这个判断本身可以用一个轻量级的LLM调用完成成本很低但能大幅提升记忆库的信噪比。代码层面这个提取步骤大概长这样def extract_memory(conversation_turn: str) - dict: prompt f 分析以下对话片段判断是否包含值得长期记忆的信息。 如果有提取成JSON格式{{type: fact|preference|lesson, content: ...}} 如果没有返回 {{type: none}}。 对话{conversation_turn} response llm_client.chat(prompt) return parse_json(response)注意这个提取步骤的prompt要写得足够具体否则LLM会把“今天天气不错”也当成事实存下来。我一般会在prompt里加几个反例效果会好很多。3.2 记忆检索Token的三个点——Key、Query、Value热词里有一条很有意思“llm的token三个点key我是谁、query我在找什么、value我能提供什么”。这其实是在用通俗的方式解释注意力机制但放在记忆检索里同样适用。你可以把记忆库想象成一个巨大的键值对集合Key这条记忆是关于什么的比如“用户饮食偏好”“项目部署环境”“上次任务失败原因”。Query当前情境下Agent需要什么信息比如用户问“今晚吃什么”Query就是“饮食相关记忆”。Value这条记忆的具体内容。比如“用户对花生过敏偏好清淡口味”。检索的过程就是用Query去匹配Key然后取出Value。在向量数据库里Key和Query都被编码成向量通过余弦相似度来匹配。但纯向量检索有个问题它有时候会“语义漂移”。比如你查“部署问题”它可能返回“部署成功”的记忆因为语义太接近了。我的做法是混合检索向量相似度占70%关键词匹配占30%。关键词匹配用BM25或者简单的全文索引就行。这样既能抓住语义又能保证关键实体比如具体的错误码、人名、项目名不被漏掉。3.3 记忆遗忘为什么“忘掉”和“记住”一样重要这一点很少有人提但极其关键。记忆库如果只增不减几个月后就会变成一个臃肿的垃圾场。检索延迟上升噪声比例增加最终拖垮整个Agent的响应质量。“hindsight”里我设计了一个衰减机制每条记忆有一个“最后访问时间”和“访问次数”。每次被检索到并实际用于生成回答访问次数加一最后访问时间更新。定期比如每天凌晨跑一个清理任务把超过30天未被访问且访问次数低于3次的记忆标记为“冷记忆”。冷记忆不直接删除而是压缩成更抽象的摘要或者转移到更便宜的存储层。这个机制的逻辑是频繁被用到的记忆说明它有价值长期不用的记忆要么过时了要么本来就不重要。这跟人脑的记忆巩固机制很像——反复强化的记忆会变成长期记忆不用的就慢慢淡忘。4. 实操过程与核心环节实现从零搭一套可跑的记忆系统4.1 环境准备Docker Compose一把梭先把基础设施搭起来。下面这个docker-compose.yml是我在多个项目里验证过的配置包含向量数据库Qdrant、关系型数据库PostgreSQL、缓存Redis和MCP Serverversion: 3.8 services: qdrant: image: qdrant/qdrant:latest ports: - 6333:6333 volumes: - ./data/qdrant:/qdrant/storage deploy: resources: limits: memory: 2G postgres: image: postgres:16 environment: POSTGRES_USER: agent POSTGRES_PASSWORD: agent_pass POSTGRES_DB: memory ports: - 5432:5432 volumes: - ./data/postgres:/var/lib/postgresql/data redis: image: redis:7-alpine ports: - 6379:6379 volumes: - ./data/redis:/data memory-mcp: build: ./memory-mcp ports: - 8080:8080 depends_on: - qdrant - postgres - redis environment: QDRANT_HOST: qdrant POSTGRES_HOST: postgres REDIS_HOST: redis提示如果你在Windows上跑Docker Desktop的WSL2后端记得开启。热词里有人遇到“virtualization support not detected”那基本就是BIOS里的虚拟化没开或者Hyper-V冲突了。先去BIOS开VT-x/AMD-V再检查Windows功能里有没有勾选“虚拟机平台”。启动命令就一句docker compose up -d等所有容器healthy之后用docker compose ps确认一下状态。如果某个容器反复重启先看日志docker compose logs qdrant。4.2 记忆MCP Server的核心实现MCP Server的作用是暴露几个标准接口给Agent调用。我用Python写一个最小实现基于mcp库from mcp.server import Server from mcp.types import Tool, TextContent import qdrant_client import redis import psycopg2 app Server(memory-server) qdrant qdrant_client.QdrantClient(hostqdrant, port6333) redis_client redis.Redis(hostredis, port6379, decode_responsesTrue) app.tool() async def write_memory(type: str, content: str, metadata: dict) - str: 写入一条长期记忆 vector embed(content) # 调用embedding模型 qdrant.upsert( collection_namelong_term_memory, points[{ id: generate_id(), vector: vector, payload: {type: type, content: content, **metadata} }] ) return 记忆已写入 app.tool() async def search_memory(query: str, top_k: int 5) - list: 检索相关记忆 query_vector embed(query) results qdrant.search( collection_namelong_term_memory, query_vectorquery_vector, limittop_k ) return [{content: r.payload[content], score: r.score} for r in results]这个Server跑起来后Agent只需要通过MCP协议调用write_memory和search_memory两个工具就能完成记忆的读写。具体的MCP客户端接入方式取决于你用的Agent框架但核心逻辑是一样的。4.3 记忆写入的触发时机与参数选择写入时机我一般放在两个地方对话结束时整段会话结束后跑一次批量提取把值得记的挑出来。关键事件发生时比如用户明确说“记住这个”或者任务执行失败时立即触发写入。参数方面有几个经验值可以参考参数建议值说明embedding维度768或1024取决于模型768够用且省存储相似度阈值0.75低于这个值的检索结果直接丢弃top_k3-5太多会稀释prompt太少可能漏信息记忆过期天数30天冷记忆清理的默认阈值注意embedding模型的选择很关键。我试过用OpenAI的text-embedding-3-small效果稳定但成本不低也试过本地的bge-m3中文场景下表现很好而且免费。如果你的数据敏感建议用本地模型。4.4 检索结果如何注入Prompt检索出来的记忆不能直接一股脑塞进prompt那样会干扰模型的判断。我的做法是结构化注入[相关记忆] - 用户偏好回答尽量简短不要用列表来源2024-01-15 - 历史教训调用XX API时需要先获取token否则会返回401来源2024-01-20 - 事实用户的项目使用Python 3.11部署在K8s上来源2024-01-10然后在系统prompt里加一句“以上是历史记忆中与当前问题相关的信息请参考但不要盲从如果与当前对话冲突以当前对话为准。”这句话很重要它给了模型一个“优先级判断”的依据避免旧记忆覆盖新信息。5. 常见问题与排查技巧实录5.1 记忆检索不准先查Embedding再查分块检索不准是最常见的问题。排查顺序我一般是看Embedding质量把Query和几条已知相关的记忆拿出来手动算一下余弦相似度。如果相关记忆的相似度低于0.6那基本是Embedding模型的问题换模型或者微调。看分块策略如果一条记忆太长比如超过500字Embedding会丢失细节。我一般会把长记忆拆成200-300字的块每块单独存但保留同一个parent_id检索时可以把同一父块的记忆合并返回。看是否该用混合检索纯向量检索对实体名不敏感。如果用户经常问“XX项目怎么样了”而XX项目在记忆里是以“Project Alpha”存储的向量检索可能匹配不上。加关键词索引就能解决。5.2 Docker网络不通90%是端口和网段问题热词里“docker网络不通”出现多次我分享一下排查套路先docker compose ps看容器是否都在运行。再docker exec -it container ping other_container如果ping不通说明不在同一网络。Compose默认会创建一个bridge网络所有服务应该能互相访问。如果不行检查docker-compose.yml里有没有手动指定network_mode。如果容器间能通但宿主机访问不了检查端口映射。比如Qdrant的6333端口ports: 6333:6333前面是宿主机端口后面是容器端口。如果宿主机端口被占用docker compose up会报错。用netstat -ano | findstr 6333Windows或lsof -i:6333Mac/Linux查一下。提示Windows上Docker Desktop有时候会有端口转发延迟重启一下Docker Desktop通常能解决。5.3 记忆写入失败检查数据库连接和向量维度写入失败一般报错在日志里能看到。常见原因Qdrant collection不存在第一次跑的时候需要先创建collection指定向量维度和距离度量。我一般会在MCP Server启动时自动检查并创建。向量维度不匹配如果你换了Embedding模型维度从768变成1024但collection还是768就会报错。解决办法是删掉collection重建或者做一次全量重新embedding。PostgreSQL连接池耗尽如果写入频率很高连接池可能不够用。把max_connections调大或者用连接池中间件。5.4 常见问题速查表现象可能原因解决方向检索结果全是无关内容Embedding模型不适合当前语言/领域换模型或微调记忆库增长过快没有选择性写入加提取步骤过滤噪声检索延迟越来越高记忆库太大没有清理机制加衰减和冷记忆清理Docker容器反复重启内存不足或配置错误看日志调资源限制MCP工具调用超时Server处理太慢或网络问题加缓存优化检索逻辑记忆内容与当前对话冲突没有优先级提示在prompt里加冲突处理规则6. 一些踩坑之后的个人体会这套东西我在几个项目里反复迭代过最大的体会是Agent记忆的难点不在“存”而在“取”和“忘”。存数据谁都会但怎么在正确的时间取出正确的记忆怎么让不重要的记忆自然淡出这才是真正影响体验的地方。另一个体会是不要追求一步到位。我一开始就想做一个“全自动”的记忆系统结果复杂度失控调试成本极高。后来改成“半自动”写入时用规则LLM判断检索时用混合策略清理时用简单的衰减公式。每一步都留了人工干预的接口反而跑得更稳。最后分享一个小技巧在开发阶段我会给每条记忆加一个debug_info字段记录它是从哪次对话、哪个提取步骤来的。这样当检索结果不对劲时我能快速回溯到源头判断是提取错了还是检索错了。这个字段在生产环境可以关掉但开发阶段能省很多时间。如果你也在做类似的事情建议先从工作记忆和短期记忆做起把长期记忆的写入频率降下来观察一段时间再逐步放开。记忆系统跟人一样先学会记眼前的事再学会记一辈子的事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑