基于MCP与Docker的Agent Memory实战:从记忆提炼到向量检索
发布时间:2026/10/1 11:44:54来源:尧图网络
1. 从“hindsight”说起为什么记忆是Agent落地的最后一公里“hindsight”这个词本身很有意思字面意思是“事后的洞察力”也就是我们常说的“后见之明”。把这个词放在Agent Memory智能体记忆的语境下它指向的核心问题非常明确一个LLM驱动的Agent如何在经历了一系列交互之后把“发生过的事”变成“下次能用的经验”。这不是简单的聊天记录堆叠而是要让Agent具备一种类似人类的回顾能力——事情做完了回头看一眼把值得留下的东西结构化地存起来下次遇到类似场景直接调用。我接触过不少做Agent落地的团队大家普遍卡在一个地方模型能力本身够用工具调用也能跑通但一旦对话轮次拉长、任务链条变复杂Agent就开始“失忆”。用户上周提过的偏好今天再问它完全不记得同一个项目里已经确认过的参数换个会话窗口就要重新问一遍。这种体验上的割裂感本质上就是记忆层缺失导致的。而“hindsight”这个项目标题恰好切中了这个痛点——它要解决的不是“记住”而是“回顾并提炼”。从热搜词来看agent memory、LLM、MCP、Docker这几个关键词构成了这个项目的技术底座。Agent Memory是目标能力LLM是推理引擎MCP是工具和数据源的接入协议Docker是部署和隔离的手段。这四个词放在一起基本勾勒出了一个可落地、可复现的Agent记忆系统该有的样子。适合谁来参考如果你正在做Agent应用开发或者对LLM的记忆机制感兴趣又或者你只是想让自己的AI助手别那么“健忘”这篇内容应该能给你一些可以直接抄作业的思路。2. 整体设计思路记忆不是存储是分层提炼2.1 为什么不能把聊天记录直接当记忆用很多人第一反应是记忆嘛把对话历史存下来下次拼到prompt里不就行了我试过这条路在短对话里能跑一旦超过二三十轮就彻底崩了。原因有两个一是token成本线性增长二是噪声淹没信号。你想想用户随口说的一句“今天天气不错”和一句“我习惯用Python 3.11”在原始记录里权重是一样的但后者才是真正值得记住的。所以“hindsight”这类项目的核心设计思路一定是分层的。我把它拆成三层原始交互层、提炼记忆层、检索应用层。原始交互层就是完整的对话日志只做归档不做检索提炼记忆层是从日志里抽出来的结构化信息比如用户偏好、任务结论、关键参数检索应用层是在新会话开始时根据当前query去匹配最相关的记忆片段拼接到上下文里。这个分层的好处是原始日志可以无限增长但真正进入prompt的记忆是经过压缩和筛选的。压缩比可以做到几十比一既省token又提精度。2.2 MCP在记忆系统里扮演什么角色MCPModel Context Protocol这两年被讨论得很多但很多人对它的理解还停留在“工具调用协议”的层面。在“hindsight”这个场景里MCP的价值其实更大——它让记忆的读写变成了标准化的资源操作。你可以把记忆库封装成一个MCP ServerAgent通过MCP协议去读写记忆而不是把记忆逻辑硬编码在Agent内部。这样做的好处是解耦。记忆的存储介质可以是Redis、PostgreSQL、甚至本地文件Agent不需要关心底层是什么只需要按照MCP定义的接口去调用。换存储方案的时候Agent代码一行不用改。我实测下来这种架构在迭代阶段特别省事今天用SQLite跑通逻辑明天换Postgres上生产切换成本几乎为零。2.3 Docker为什么是必选项Agent Memory系统涉及多个组件LLM推理服务、记忆存储、MCP Server、可能还有向量数据库。这些组件之间的依赖关系如果靠手动装光是版本冲突就能耗掉一整天。Docker的价值在于把每个组件封装成独立的容器用docker-compose编排起来一键启动。更重要的是环境一致性。你在本地跑通的记忆提炼逻辑打包成镜像之后在测试环境和生产环境跑出来的结果是一样的。不会出现“我本地好好的服务器上就不行”这种经典问题。对于需要长期运行、持续积累记忆的Agent来说环境稳定性是底线。3. 核心细节拆解记忆的写入、提炼与检索3.1 记忆写入什么时候该记什么时候不该记不是所有交互都值得写入记忆。我的经验是设置一个触发阈值当一轮对话中出现了以下信号时才触发记忆写入——用户明确表达了偏好“我喜欢”“我习惯”“以后都”、确认了关键参数“就用这个”“确定是”、或者完成了某个阶段性结论“那就这么定了”。具体实现上可以在Agent的system prompt里加一段判断逻辑让LLM在每轮对话结束时输出一个should_remember的布尔值和一个memory_content字段。如果should_remember为true就把memory_content写入记忆库。这个判断本身消耗的token很少但能过滤掉大量噪声。注意不要每轮都写记忆。我踩过的坑是早期版本每轮都写结果记忆库里塞满了“好的”“明白了”这种无意义内容检索的时候反而把真正重要的记忆挤下去了。3.2 记忆提炼从原始对话到结构化知识写入记忆库的内容不应该是原始对话的复制粘贴而应该是经过提炼的结构化信息。我通常用这样一个schema{ memory_id: uuid, timestamp: 2025-01-15T10:30:00Z, memory_type: preference | fact | conclusion | task_state, content: 用户偏好使用Python 3.11进行开发, source_context: 用户在讨论项目技术栈时提到, confidence: 0.95, tags: [python, tech_stack, preference] }memory_type这个字段很关键。不同类型的记忆在检索时的权重不一样。比如preference类型的记忆在个性化场景下权重高task_state类型的记忆在任务续接场景下权重高。confidence字段用来标记这条记忆的可靠程度LLM提炼出来的记忆不总是准确的低置信度的记忆在检索时可以降权处理。3.3 记忆检索怎么找到“对的那条”检索环节是整个记忆系统里最考验设计的地方。最简单的做法是关键词匹配但效果很差因为用户不会用同样的词来提问。比如记忆里存的是“用户偏好Python 3.11”用户新query是“帮我写个脚本”关键词匹配完全找不到。我的方案是向量检索标签过滤的组合。把每条记忆的content字段做embedding存到向量数据库里。新query来的时候先做embedding然后做相似度搜索同时用tags字段做过滤。比如当前对话涉及“技术栈”话题就只检索tags里包含tech_stack的记忆。检索数量上我一般取Top 5。太少可能漏掉关键记忆太多会稀释prompt里的有效信息。Top 5是一个实测下来比较平衡的值。3.4 记忆更新与遗忘不是所有记忆都该永久保留记忆系统需要遗忘机制。我见过一些实现记忆只增不减跑几个月之后检索精度断崖式下降。合理的做法是给每条记忆加一个last_accessed字段记录最后一次被检索到的时间。如果一条记忆超过30天没有被访问过就把它归档到冷存储不再参与常规检索。另外当新记忆和旧记忆冲突时比如用户之前说“偏好Python”后来改口说“现在主要用Go”需要有一个冲突解决策略。我的做法是保留两条记忆但把旧记忆的confidence降权新记忆的confidence设为高值。检索时按confidence排序自然就优先返回新记忆。4. 实操过程从零搭一套可跑的记忆系统4.1 环境准备与Docker编排先确保Docker Desktop已经装好并且能正常启动。Windows环境下如果遇到Virtualization support not detected的报错需要进BIOS把虚拟化支持打开这个坑我踩过好几次不是Docker本身的问题。目录结构建议这样组织hindsight/ ├── docker-compose.yml ├── agent/ │ ├── Dockerfile │ └── main.py ├── memory-server/ │ ├── Dockerfile │ └── server.py └── data/ └── postgres/docker-compose.yml的核心配置version: 3.8 services: postgres: image: postgres:16 environment: POSTGRES_DB: hindsight POSTGRES_USER: agent POSTGRES_PASSWORD: agent_pass volumes: - ./data/postgres:/var/lib/postgresql/data ports: - 5432:5432 memory-server: build: ./memory-server depends_on: - postgres environment: DATABASE_URL: postgresql://agent:agent_passpostgres:5432/hindsight ports: - 8080:8080 agent: build: ./agent depends_on: - memory-server environment: MEMORY_SERVER_URL: http://memory-server:8080这个编排文件定义了三个服务Postgres做记忆存储memory-server做记忆的读写接口agent做对话逻辑。三者通过Docker内部网络通信端口只在需要的时候暴露给宿主机。4.2 记忆服务的核心接口实现memory-server需要暴露几个关键接口写入记忆、检索记忆、更新记忆、归档记忆。用FastAPI写起来很快from fastapi import FastAPI from pydantic import BaseModel import psycopg2 import uuid from datetime import datetime app FastAPI() class MemoryWrite(BaseModel): content: str memory_type: str tags: list[str] confidence: float 0.9 app.post(/memory/write) def write_memory(mem: MemoryWrite): memory_id str(uuid.uuid4()) conn psycopg2.connect(DATABASE_URL) cur conn.cursor() cur.execute( INSERT INTO memories (memory_id, content, memory_type, tags, confidence, created_at, last_accessed) VALUES (%s, %s, %s, %s, %s, %s, %s), (memory_id, mem.content, mem.memory_type, mem.tags, mem.confidence, datetime.utcnow(), datetime.utcnow()) ) conn.commit() return {memory_id: memory_id, status: written}检索接口稍微复杂一点需要结合向量相似度和标签过滤。如果不想引入额外的向量数据库可以用Postgres的pgvector扩展直接在Postgres里做向量检索少维护一个组件。4.3 Agent侧的记忆读写逻辑Agent在每轮对话结束后调用一次记忆判断逻辑。如果判断需要写入就调memory-server的写入接口。在新会话开始时用当前query去检索记忆把Top 5的记忆拼到system prompt里。def build_system_prompt(base_prompt, query): memories retrieve_memories(query, top_k5) if not memories: return base_prompt memory_text \n.join([ f- [{m[memory_type]}] {m[content]} for m in memories ]) return f{base_prompt} 以下是你之前记住的关于用户的信息请在回答时参考 {memory_text} 这个拼接逻辑看起来简单但效果提升非常明显。用户会感觉Agent“记得住事”体验上的连贯性一下子就上来了。4.4 参数选择与性能调优几个关键参数我列一下实测值参数推荐值说明检索Top K5太少漏信息太多稀释prompt相似度阈值0.75低于这个值的记忆不返回记忆归档天数30天超过30天未访问的记忆归档写入置信度阈值0.7低于这个值的记忆不写入Embedding维度1536用text-embedding-3-small的默认维度Embedding模型的选择上如果预算有限可以用开源的bge-small效果差一些但成本低很多。如果追求精度text-embedding-3-small是性价比比较高的选择。5. 常见问题与排查技巧实录5.1 记忆检索不准怎么办最常见的原因是embedding质量不够。先检查embedding模型是否适合中文场景很多英文模型在中文上的表现会打折扣。其次检查记忆的content字段是否写得太短或太模糊比如“用户说了Python”这种记忆检索时很难匹配到“帮我写个脚本”这种query。解决办法是在写入记忆时让LLM把内容写完整比如“用户偏好使用Python 3.11进行后端开发”。5.2 Docker网络不通的排查思路Agent容器访问memory-server容器失败先检查两者是否在同一个Docker网络中。docker-compose默认会创建一个共享网络但如果手动docker run启动的容器需要显式指定--network。另外检查memory-server是否真的监听在0.0.0.0而不是127.0.0.1后者在容器里只能被容器内部访问。5.3 记忆冲突怎么处理用户改口的情况很常见。我的策略是新记忆写入时先检索是否有相似记忆如果有且内容冲突就把旧记忆的confidence乘以0.5新记忆的confidence设为0.95。检索时按confidence降序排列自然优先返回新记忆。旧记忆不删除保留作为历史参考。5.4 常见问题速查表问题现象可能原因解决方向Agent完全不记得之前的事记忆写入未触发检查should_remember判断逻辑检索到的记忆不相关embedding质量差换中文优化模型或补全记忆内容记忆库增长过快写入阈值太低提高confidence阈值过滤噪声容器间通信失败网络配置问题检查Docker网络和监听地址检索速度变慢记忆量过大加索引或启用归档机制5.5 几个我踩过的坑第一个坑是记忆写入太频繁。早期版本每轮对话都写结果一周下来记忆库里有几千条检索精度反而下降。后来加了置信度阈值和类型过滤写入量降到原来的十分之一效果反而更好。第二个坑是embedding模型换版本。换模型之后旧记忆的向量和新query的向量不在同一个空间里检索完全失效。解决办法是换模型时重新生成所有记忆的embedding或者干脆清空重建。第三个坑是Docker volume权限。Postgres容器写入数据目录时如果权限不对会启动失败。在Linux下需要确保宿主机目录的UID和容器内Postgres用户的UID一致Windows下用Docker Desktop一般不会有这个问题。6. 记忆系统的扩展方向与个人体会这套记忆系统跑通之后扩展空间其实很大。一个方向是跨Agent共享记忆多个Agent共用一个memory-server每个Agent写入的记忆带上自己的标识检索时可以按Agent过滤也可以全局检索。另一个方向是记忆的自动摘要当某个话题下的记忆积累到一定数量时自动生成一条高层摘要替代零散的记忆片段。我个人在实际操作中的体会是记忆系统的核心难点不在技术实现而在判断什么值得记。这个判断逻辑需要根据具体场景反复调优没有一劳永逸的参数。我建议刚开始的时候宁可少记也不要多记先保证检索到的记忆都是高质量的再逐步放宽写入条件。另外记忆的content字段一定要写完整、写具体模糊的记忆等于没有记忆。
网站建设高端定制企业官网