news 2026/10/3 3:36:27

Agent记忆系统实战:基于MCP与Docker构建可检索的长期记忆

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent记忆系统实战:基于MCP与Docker构建可检索的长期记忆

1. 从“hindsight”说起:为什么我们需要给Agent装上记忆

“hindsight”这个词本身很有意思,字面意思是“事后的洞察力”,也就是我们常说的“后见之明”。放在AI Agent的语境里,它指向一个非常具体且关键的问题:Agent能不能记住之前发生过什么,并在后续决策中真正用上这些信息?

我接触过不少做Agent项目的团队,大家一开始都把精力放在工具调用、提示词优化、模型选型上,但跑了一段时间之后,几乎所有人都会撞上同一堵墙——Agent没有记忆,或者说,它的记忆是“假”的。每次对话重新开始,它就像失忆一样,用户之前说过的偏好、上一次任务执行的结果、中间踩过的坑,统统不记得。你让它帮你订过一次咖啡,下次它还是问你“请问您想喝什么”。

这就是hindsight要解决的核心问题。它不是简单的“把聊天记录存下来”,而是要让Agent具备一种可检索、可推理、可更新的长期记忆能力。结合热搜词里反复出现的agent memory、working memory、MCP、Docker这些关键词,可以很清楚地看到,这个项目大概率是一个围绕Agent记忆系统展开的工程实践,涉及记忆的存储结构、检索机制、与LLM的交互方式,以及如何通过MCP协议和Docker容器化来落地。

这篇文章适合谁看?如果你正在做Agent相关的开发,或者你已经在用Dify、Coze这类平台搭建智能体,但发现记忆功能总是不够用,那这篇内容会对你有直接帮助。如果你只是对LLM应用感兴趣,想了解“Agent记忆”到底是怎么回事,我也会尽量用生活化的例子把它讲清楚。

提示:本文涉及的所有技术方案和参数选择,均基于我在实际项目中的经验总结,不同场景下需要根据具体需求调整。

2. 核心思路拆解:Agent记忆到底该怎么设计

2.1 为什么“把聊天记录塞进上下文”不是真正的记忆

很多人第一次做Agent记忆的时候,思路很直接:把历史对话全部拼接到prompt里,一起发给LLM。这个方法在对话轮次少的时候能用,但很快就会遇到三个问题。

第一个问题是上下文窗口有限。就算现在很多模型支持128K甚至更长的上下文,但你不可能把几个月的对话记录都塞进去。而且上下文越长,推理成本越高,延迟也越大。我实测过一个场景,当上下文超过30K token之后,模型对中间部分信息的注意力明显下降,这就是所谓的“lost in the middle”现象。

第二个问题是信息噪音。历史对话里大量内容是寒暄、确认、重复的指令,真正有价值的记忆可能只占5%。把这些噪音一起喂给模型,反而会干扰它的判断。

第三个问题是无法跨会话。用户今天和你聊完,明天再打开,如果系统没有持久化存储,一切归零。就算存了数据库,没有好的检索机制,也等于没存。

所以hindsight这类项目的核心思路,一定不是“存聊天记录”,而是构建一套记忆的抽象层:把原始对话经过提取、压缩、结构化之后,存成可检索的记忆单元,在需要的时候精准召回。

2.2 记忆的分层:working memory和long-term memory

热搜词里出现了“agent 存储 working memory”,这说明working memory是一个被明确讨论的概念。在认知科学里,working memory指的是人在当前任务中临时保持和操作信息的系统,容量有限但访问极快。对应到Agent,working memory就是当前会话中正在处理的上下文,而long-term memory则是跨会话持久化的知识。

我在实际项目中通常会把记忆分成三层:

  • 即时上下文:当前这一轮对话的原始消息,直接放在prompt里,不做压缩。
  • 工作记忆:当前任务相关的关键信息,比如用户的目标、已完成的步骤、待办事项。这部分会随着任务推进动态更新。
  • 长期记忆:跨会话保留的用户偏好、历史决策、领域知识。这部分需要持久化存储,并且要有检索机制。

hindsight如果要做得好,必须把这三层分开处理。混在一起做,最后一定是一团乱麻。

2.3 为什么选MCP和Docker

热搜词里MCP出现了很多次,还有“mcp协议”“mcp是软件协议 硬件协议那个概念叫什么来着”这样的搜索。MCP全称是Model Context Protocol,简单说就是一套让LLM应用和外部工具、数据源之间标准化交互的协议。你可以把它理解成“AI世界的USB接口”——不管你是数据库、文件系统还是API,只要实现了MCP,LLM就能用统一的方式去调用。

对于hindsight这样的记忆系统来说,MCP的价值在于解耦。记忆的存储、检索、更新可以做成独立的MCP Server,Agent通过MCP协议来访问记忆,而不需要把记忆逻辑硬编码在Agent内部。这样换存储后端、换检索算法,都不影响Agent本身。

Docker则是解决部署和环境一致性的问题。记忆系统通常需要依赖数据库(比如PostgreSQL、Redis)、向量库(比如Milvus、Qdrant)、缓存等组件,用Docker Compose一键拉起整个环境,比手动装省事太多。而且热搜词里有“docker安装mysql8.0并使用”“docker安装redis主从”“docker compose”这些,说明很多人在实际部署时确实需要这些操作指引。

3. 核心细节解析:记忆系统的关键组件与实操要点

3.1 记忆的存储结构:从原始对话到结构化记忆

原始对话是一串非结构化的文本,直接存进去检索效率很低。我通常的做法是做一个记忆提取管道,把原始对话转成结构化的记忆条目。

一个典型的记忆条目包含这些字段:

字段说明示例
memory_id唯一标识mem_20250101_001
user_id用户标识user_123
session_id会话标识sess_456
content记忆内容用户偏好喝美式咖啡,不加糖
memory_type记忆类型preference / fact / event
embedding向量表示[0.12, -0.34, ...]
created_at创建时间2025-01-01T10:00:00Z
last_accessed最后访问时间2025-01-02T15:30:00Z
access_count访问次数5
importance重要性评分0.8

这个结构看起来简单,但每个字段都有讲究。memory_type决定了检索时的过滤策略,embedding决定了语义检索的效果,importance和access_count则用于记忆的淘汰和优先级排序。

提取管道的工作流程一般是:原始对话 -> LLM提取 -> 结构化条目 -> 向量化 -> 存入数据库。这里的关键是提取的prompt设计。我试过很多版本,最后发现最有效的方式是让LLM输出JSON格式,并且明确告诉它“只提取值得长期记住的信息,忽略寒暄和临时性内容”。

3.2 检索策略:向量检索加关键词检索的混合方案

记忆存进去之后,怎么在需要的时候精准召回,是另一个核心问题。纯向量检索的问题是,它对精确匹配不敏感。比如用户说“我上次说的那个项目”,向量检索可能召回一堆不相关的项目讨论,但关键词检索能精准定位到“上次”和“项目”这两个词。

我的经验是混合检索效果最好:先用向量检索召回一批语义相关的记忆,再用关键词检索补充精确匹配的结果,最后用重排序模型(比如bge-reranker)做统一排序。实测下来,混合检索的召回准确率比单一方案能提升20%到30%。

具体参数上,向量检索的top_k一般设20到50,关键词检索的top_k设10到20,重排序后取前5到10条注入prompt。这个数量需要根据模型上下文窗口和任务复杂度调整。如果任务很简单,3条就够了;如果任务复杂,可能需要10条以上。

注意:检索到的记忆不要直接全部塞进prompt,最好做一个摘要或压缩。我见过有人把20条记忆原封不动放进去,结果模型反而被干扰了。

3.3 MCP Server的实现要点

把记忆系统做成MCP Server,需要实现几个核心工具(tool):

  • memory_store:存入一条新记忆
  • memory_search:根据query检索相关记忆
  • memory_update:更新已有记忆的内容或重要性
  • memory_delete:删除不再需要的记忆
  • memory_list:列出某个用户或会话的所有记忆

每个工具都需要定义清晰的输入输出schema。比如memory_search的输入是query、user_id、top_k、memory_type等参数,输出是记忆条目列表。这里有个坑:MCP协议对参数类型有严格要求,如果schema定义不严谨,Agent调用时很容易报“provider rejected the request schema or tool payload”这类错误。热搜词里正好有这条,说明不少人踩过这个坑。

我的建议是,schema定义尽量用基础类型,避免嵌套过深。如果确实需要复杂结构,用JSON字符串传参,然后在Server端解析。

3.4 Docker Compose编排:一键拉起记忆系统

一个完整的记忆系统通常需要这些组件:

  • PostgreSQL:存结构化记忆条目
  • Redis:做缓存和会话状态管理
  • Qdrant或Milvus:存向量embedding
  • MCP Server:记忆服务的核心逻辑
  • Agent应用:调用MCP Server的客户端

用Docker Compose编排,关键是处理好网络和依赖关系。我一般会建一个自定义网络,让所有服务在同一个网络里,用服务名互相访问。数据库服务要加healthcheck,确保MCP Server启动时数据库已经就绪。

version: '3.8' services: postgres: image: postgres:16 environment: POSTGRES_DB: hindsight POSTGRES_USER: hindsight POSTGRES_PASSWORD: hindsight123 volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U hindsight"] interval: 5s timeout: 5s retries: 5 networks: - hindsight-net qdrant: image: qdrant/qdrant:latest volumes: - qdrant_data:/qdrant/storage networks: - hindsight-net mcp-server: build: ./mcp-server depends_on: postgres: condition: service_healthy qdrant: condition: service_started environment: DATABASE_URL: postgresql://hindsight:hindsight123@postgres:5432/hindsight QDRANT_URL: http://qdrant:6333 networks: - hindsight-net volumes: pgdata: qdrant_data: networks: hindsight-net: driver: bridge

这个compose文件可以直接用,但有几个细节要注意。PostgreSQL的healthcheck我用了pg_isready,这个命令在postgres镜像里自带,比用curl更可靠。Qdrant的存储卷一定要挂出来,不然容器重启数据就没了。MCP Server的build上下文指向本地目录,你需要确保Dockerfile写对了。

4. 实操过程:从零搭建一个带记忆的Agent

4.1 环境准备与Docker安装

如果你在Windows上,Docker Desktop是最省事的选择。但热搜词里出现了“virtualization support not detected docker desktop failed to start because v”这条,说明很多人遇到了虚拟化支持的问题。这个问题的根源通常是BIOS里没开启虚拟化,或者Hyper-V和WSL2冲突。

我的建议是:Windows 11用户直接用WSL2后端,在BIOS里开启Intel VT-x或AMD-V,然后在“启用或关闭Windows功能”里勾选“虚拟机平台”和“适用于Linux的Windows子系统”。装完Docker Desktop之后,在设置里确认“Use WSL 2 based engine”是勾选的。

Linux用户直接用apt或yum装docker-ce就行,记得把当前用户加到docker组里,不然每次都要sudo。

sudo usermod -aG docker $USER newgrp docker

装完之后用docker run hello-world验证一下,能跑通就说明环境没问题。

4.2 数据库初始化与记忆表设计

PostgreSQL启动之后,需要建表。我一般用SQL migration来管理表结构,这里给一个核心表的建表语句:

CREATE TABLE memories ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id VARCHAR(255) NOT NULL, session_id VARCHAR(255), content TEXT NOT NULL, memory_type VARCHAR(50) NOT NULL DEFAULT 'fact', importance FLOAT DEFAULT 0.5, access_count INT DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), last_accessed TIMESTAMPTZ DEFAULT NOW(), metadata JSONB DEFAULT '{}' ); CREATE INDEX idx_memories_user_id ON memories(user_id); CREATE INDEX idx_memories_type ON memories(memory_type); CREATE INDEX idx_memories_created ON memories(created_at DESC);

向量部分存在Qdrant里,每个记忆条目对应一个point,point的id用memory的UUID,payload里存user_id和memory_type用于过滤。

Qdrant的collection创建参数需要根据embedding模型的维度来定。如果你用的是OpenAI的text-embedding-3-small,维度是1536;如果用bge-m3,维度是1024。距离度量用Cosine,因为文本embedding通常做归一化之后,Cosine和点积等价。

4.3 记忆提取管道的实现

记忆提取是整条链路里最需要调优的部分。我的做法是用一个专门的LLM调用来做提取,prompt大概长这样:

你是一个记忆提取助手。请从以下对话中提取值得长期记住的信息。 提取规则: 1. 只提取用户的偏好、事实性信息、重要事件、决策结论 2. 忽略寒暄、确认、重复内容 3. 每条记忆独立成条,不要合并无关信息 4. 输出JSON数组,每个元素包含content、memory_type、importance三个字段 5. memory_type只能是preference、fact、event、decision之一 6. importance是0到1之间的浮点数,越重要值越高 对话内容: {conversation} 请输出JSON:

这个prompt我迭代了七八个版本,关键改进点在于:明确列出memory_type的枚举值,避免模型自由发挥;要求输出JSON数组而不是单个对象,方便批量处理;加入importance评分,后续检索时可以按重要性加权。

提取出来的记忆,先存PostgreSQL,然后异步做embedding存入Qdrant。这里用异步是为了不阻塞主流程,因为embedding调用通常有几百毫秒的延迟。

4.4 MCP Server的核心代码

MCP Server我用Python实现,基于官方的mcp库。核心是定义tool和对应的处理函数。以memory_search为例:

from mcp.server import Server from mcp.types import Tool, TextContent import json app = Server("hindsight-memory") @app.list_tools() async def list_tools(): return [ Tool( name="memory_search", description="根据query检索相关记忆", inputSchema={ "type": "object", "properties": { "query": {"type": "string", "description": "检索关键词"}, "user_id": {"type": "string", "description": "用户标识"}, "top_k": {"type": "integer", "default": 5}, "memory_type": {"type": "string", "enum": ["preference", "fact", "event", "decision"]} }, "required": ["query", "user_id"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "memory_search": results = await search_memories( query=arguments["query"], user_id=arguments["user_id"], top_k=arguments.get("top_k", 5), memory_type=arguments.get("memory_type") ) return [TextContent(type="text", text=json.dumps(results, ensure_ascii=False))]

这里有个细节:inputSchema里的required字段一定要写对,不然Agent调用时可能传空参数导致报错。另外description要写清楚,因为Agent是根据description来决定什么时候调用这个工具的。

4.5 Agent端的接入与测试

Agent端接入MCP Server,不同框架方式不一样。如果你用的是Claude Desktop,直接在配置文件里加mcp server的地址就行。如果用的是自己写的Agent,需要实现MCP client的逻辑。

测试的时候,我建议分三步走:

第一步,单独测试MCP Server的每个tool,用curl或Python脚本直接调用,确认功能正常。

第二步,测试Agent调用MCP的链路,看Agent能不能正确选择tool并传对参数。

第三步,做端到端的场景测试。比如让Agent记住“用户喜欢喝美式咖啡”,然后在新会话里问“我想喝点什么”,看Agent能不能检索到这条记忆并给出正确回答。

我实测下来,最容易出问题的环节是第二步。Agent有时候会传错参数类型,比如把top_k传成字符串。解决办法是在MCP Server端做参数校验和类型转换,不要完全信任Agent传来的数据。

5. 常见问题与排查技巧实录

5.1 记忆检索不准确怎么办

这是最常见的问题。表现是Agent检索到的记忆和当前对话不相关,或者明明存了某条记忆却检索不到。

排查思路分三层。第一层看存储,确认记忆确实存进去了,用SQL查一下数据库,用Qdrant的API查一下向量。第二层看检索,单独调用memory_search,看返回结果是否合理。第三层看注入,确认检索到的记忆确实被放进了prompt。

如果存储没问题但检索不准,大概率是embedding模型的问题。不同模型对中文的支持差异很大,我试过text-embedding-ada-002、bge-m3、m3e,最后发现bge-m3在中文场景下效果最稳。如果检索到了但Agent没用上,可能是prompt里记忆的呈现方式有问题,试试把记忆放在prompt靠前的位置,或者加一句“请优先参考以下历史记忆”。

5.2 Docker网络不通的排查

热搜词里有“docker网络不通”,这个我踩过好几次。典型表现是MCP Server连不上PostgreSQL,报connection refused。

排查步骤:先用docker compose ps确认所有容器都在运行。然后进到MCP Server容器里,用ping postgres测试网络连通性。如果不通,检查compose文件里是不是在同一个network下。如果ping通但连不上端口,检查PostgreSQL是不是真的在监听5432端口,用docker compose logs postgres看日志。

还有一个坑是防火墙。有些云服务器默认开了防火墙,容器之间的通信可能被拦截。我遇到过一回,最后发现是iptables规则的问题,加了一条允许docker网段的规则就好了。

5.3 MCP工具调用报schema错误

“llm request failed: provider rejected the request schema or tool payload”这个错误,通常是因为tool的inputSchema定义和实际传参不匹配。比如schema里定义top_k是integer,但Agent传了字符串"5"。

解决办法有两个:一是在schema里用oneOf或anyOf允许多种类型,二是在Server端做类型转换。我倾向于后者,因为schema太复杂会影响Agent的理解。

另外,如果tool的description写得太模糊,Agent可能传一些schema里没定义的参数。所以description里最好明确列出支持的参数,并且说明哪些是必填的。

5.4 记忆膨胀与性能下降

跑了一段时间之后,记忆库会越来越大,检索速度变慢,而且噪音增多。这个问题必须提前考虑。

我的做法是加一个记忆淘汰机制。定期(比如每天凌晨)跑一个任务,做三件事:第一,删除importance低于阈值且超过30天没被访问的记忆;第二,合并重复或高度相似的记忆;第三,对长期未访问但importance高的记忆做降权处理。

阈值怎么定?我一般设importance < 0.3且access_count = 0且created_at超过30天。这个参数可以根据实际数据量调整。如果记忆量不大,可以放宽;如果记忆量很大,要收紧。

5.5 常见问题速查表

问题现象可能原因排查方法解决方案
检索不到记忆embedding未生成或存储失败查Qdrant collection是否有数据检查embedding管道日志
检索结果不相关embedding模型不适合中文人工评估top_k结果换bge-m3或m3e模型
Agent不调用memory工具tool description不清晰看Agent的决策日志优化description,加示例
Docker容器启动失败端口冲突或依赖未就绪docker compose logs加healthcheck和depends_on
记忆重复存储提取管道未去重查数据库重复content加唯一索引或相似度去重
响应延迟高检索top_k太大看检索耗时日志降低top_k,加缓存

提示:这张表是我在实际运维中总结的,大部分问题都能覆盖。如果遇到表里没有的,优先看日志,90%的问题日志里都有线索。

6. 记忆系统的扩展方向与个人经验

6.1 从被动检索到主动记忆管理

现在大部分记忆系统都是被动检索:Agent需要的时候去查。但更高级的做法是主动记忆管理,也就是Agent自己决定什么时候该记、什么时候该忘、什么时候该更新。

这需要给Agent加一个“记忆反思”的环节。比如每轮对话结束后,让LLM评估一下这轮对话里有没有值得记住的新信息,如果有就主动调用memory_store。同时,如果发现新信息和已有记忆冲突,主动调用memory_update。

我试过这个方案,效果确实比被动检索好,但成本也更高,因为每轮都要多一次LLM调用。折中方案是只在特定条件下触发反思,比如对话轮次超过5轮,或者用户明确说了“记住”之类的关键词。

6.2 多Agent共享记忆的挑战

如果你的系统里有多个Agent,它们之间的记忆共享是个复杂问题。最简单的做法是共用一个记忆库,用user_id区分。但这样会有隐私和冲突问题。

更合理的做法是分层:每个Agent有自己的私有记忆,同时有一个共享记忆层。私有记忆只对当前Agent可见,共享记忆对所有Agent可见。写入共享记忆需要经过审批或冲突检测。

这个方案实现起来不复杂,就是在memories表里加一个scope字段,检索时根据scope过滤。但策略设计需要仔细考虑,不然容易出现记忆污染。

6.3 我踩过的最大的坑

最后分享一个我踩过的最大的坑。早期做记忆系统的时候,我把所有检索到的记忆都直接拼到prompt里,没有做任何压缩。结果有一次用户问了一个简单问题,Agent检索到了20条记忆,prompt一下子膨胀到8000 token,模型响应变得很慢,而且回答质量反而下降了。

后来我改成:检索到记忆后,先用一个小模型做摘要压缩,把20条记忆压缩成3到5条核心要点,再注入prompt。这样既保留了关键信息,又控制了token消耗。实测下来,响应速度提升了40%,回答准确率也更高了。

这个经验让我意识到,记忆系统的核心不是“存了多少”,而是“在正确的时候用正确的方式取出正确的信息”。存储和检索只是手段,最终目标是为Agent的决策提供有效支撑。

另外一个小技巧:在记忆的metadata里加一个source字段,记录这条记忆是从哪次对话、哪个场景提取的。排查问题的时候,可以顺着source回溯到原始对话,非常有用。这个字段平时用不上,但出问题的时候能省很多时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 3:36:07

Python实现电池寿命预测:KNN、SVM与随机森林回归实战对比

电池寿命预测这个活&#xff0c;网上教程不少&#xff0c;但大多要么偏理论、要么代码全是英文注释、要么只跑一个模型就完事了&#xff0c;很难直接拿来上手。我这次用Python把KNN、SVM、随机森林三种回归模型完整跑了一遍&#xff0c;代码全部带中文注释&#xff0c;整理成一…

作者头像 李华
网站建设 2026/10/3 3:35:38

MySQL连接池爆满排查:从Too many connections到根因修复

公司业务半夜报警&#xff0c;数据库连接池直接打满&#xff0c;用着好好的服务突然就“Too many connections”&#xff0c;随后页面超时、接口504&#xff0c;紧接着一堆任务队列堆积告警。这种情况我处理过不止一次&#xff0c;每次原因都不完全相同&#xff0c;但排查思路是…

作者头像 李华
网站建设 2026/10/3 3:35:36

Agent稳定性收口:重试、幂等与并发控制的工程实践

周五晚上九点&#xff0c;飞书群里静悄悄的。按照设定&#xff0c;当日19:00应该准时出现汇总好的团队日报&#xff0c;但什么都没有。我打开Agent的后台日志&#xff0c;看到一行安静的报错&#xff1a;agent execution terminated due to error。再往前翻&#xff0c;周二早上…

作者头像 李华
网站建设 2026/10/3 3:35:35

Flutter for OpenHarmony电子合同App活动历史模块实现与踩坑总结

做 Flutter for OpenHarmony 电子合同签署App 的这段经历里&#xff0c;我一度以为最硬核的会是签名面板、证书解析、骑缝章渲染这些"看得见"的模块。结果真到了测试和交付阶段&#xff0c;卡住我时间最久的&#xff0c;反而是看起来平平无奇的"活动历史"功…

作者头像 李华
网站建设 2026/10/3 3:35:35

渭河流域12.5米DEM与标准矢量数据交付规范

简介&#xff1a;本资源面向地理信息系统&#xff08;GIS&#xff09;学习者、水文与流域研究者及遥感制图实践者&#xff0c;提供渭河流域高精度空间数据一体化解决方案&#xff0c;有效支撑流域分析、地形可视化、论文成图与教学演示等核心需求。压缩包共18个文件&#xff0c…

作者头像 李华
网站建设 2026/10/3 3:35:18

AUV辅助水下物联网信息收集:基于AoI优化的Matlab仿真方案

水下物联网的数据收集一直是个让人头疼的问题。传统固定节点组网用声学链路通信&#xff0c;速率低、延迟高、能耗也大&#xff0c;而且水下环境信号衰减严重&#xff0c;靠静态中继很难保证数据的新鲜度。这几年学界慢慢转向用AUV&#xff08;自主水下航行器&#xff09;当移动…

作者头像 李华