尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

LLM Agent记忆系统实战:基于MCP与Docker的分层记忆架构

发布时间:2026/9/28 15:50:44

资讯中心
01
ARTICLE

LLM Agent记忆系统实战:基于MCP与Docker的分层记忆架构

LLM Agent记忆系统实战:基于MCP与Docker的分层记忆架构
1. 从“hindsight”说起为什么我们需要给Agent装一个“后视镜”“hindsight”这个词本身很有意思字面意思是“事后的洞察力”也就是我们常说的“后见之明”。放在LLM Agent的语境里它指向一个非常具体且迫切的问题Agent的记忆机制。你可能已经注意到现在大部分Agent在单轮对话里表现惊艳但一旦对话轮次拉长或者跨会话切换任务它就像换了个人——忘记之前聊过什么、忘记用户偏好、忘记自己踩过的坑。这不是模型能力不够而是记忆架构没跟上。我最早接触Agent记忆这个话题是在做一个多轮客服场景的自动化项目时。当时用的模型在单轮问答上准确率能到90%以上但连续对话超过五轮之后回答质量断崖式下跌。排查了一圈才发现问题不在模型本身而在于每次请求都是“无状态”的——Agent没有持久化的记忆层每次对话都是重新开始。后来我开始系统性地研究Agent Memory这个方向发现社区里已经有不少方案从简单的对话历史拼接到向量数据库检索再到结构化的记忆图谱各有各的适用场景。而“hindsight”这个项目标题恰好切中了其中一个关键痛点如何让Agent从过去的交互中提取可复用的经验而不是简单地堆砌历史记录。这篇文章适合谁看如果你正在做LLM Agent相关的开发或者对Agent记忆机制感兴趣想了解如何从零搭建一个可落地的记忆系统那接下来的内容应该对你有帮助。我会从整体设计思路讲起拆解核心模块的实现细节然后给出完整的实操步骤和参数配置最后分享一些我在实际项目中踩过的坑和排查技巧。整个过程中会涉及Docker环境搭建、MCP协议对接、向量检索优化等具体技术点但我会尽量用通俗的方式解释清楚让不同基础的读者都能跟上。提示本文涉及的所有代码和配置均基于公开可用的开源工具和标准协议不涉及任何特定商业平台或敏感服务。2. 整体设计思路Agent记忆到底该怎么“记”2.1 为什么简单的对话历史拼接不够用很多人刚开始做Agent记忆时第一反应是把所有对话历史拼接到prompt里。这个方法在对话轮次少的时候确实能用但一旦超过十几轮就会遇到两个硬伤上下文窗口限制和信息噪声。假设每轮对话平均200个token20轮就是4000个token再加上系统提示词和工具描述很容易就逼近模型的上下文上限。更麻烦的是历史记录里大量信息是冗余的——用户重复确认、Agent的礼貌性回复、与当前任务无关的闲聊——这些内容会稀释真正重要的信息导致模型在生成回答时抓不住重点。我试过在prompt里加一句“请重点关注最近三轮对话”效果有限。因为模型对prompt中不同位置的注意力分布是不均匀的中间部分的信息容易被忽略这就是所谓的“lost in the middle”现象。所以单纯靠拼接历史记录本质上是在用“蛮力”对抗模型的注意力机制投入产出比很低。2.2 分层记忆架构的核心思路“hindsight”这个项目标题给我的启发是记忆不应该是一锅粥而应该分层。我最终采用的方案是把Agent记忆分成三层短期记忆当前会话的最近N轮对话直接放在prompt里保证即时上下文连贯。长期记忆跨会话的持久化信息存储在向量数据库中按需检索。反思记忆从历史交互中提炼出的经验教训以结构化形式存储用于指导未来决策。这三层的关系可以这样理解短期记忆是“工作台”长期记忆是“档案柜”反思记忆是“笔记本”。工作台上只放当前正在处理的东西档案柜里存着所有原始记录笔记本上记的是“下次遇到类似情况该怎么做”。这种分层设计的好处是每一层都有明确的职责和淘汰机制不会互相干扰。2.3 为什么选择MCP作为对接协议在工具选型上我选择了MCPModel Context Protocol作为Agent与记忆系统之间的对接协议。原因有三点第一MCP是标准化的协议不同Agent框架之间的迁移成本低第二MCP支持工具调用和资源暴露两种模式既能主动查询记忆也能被动接收记忆更新第三社区生态在快速完善像Playwright MCP、Chrome DevTools MCP这些工具已经可以直接复用。当然MCP也不是没有缺点。它的协议规范还在演进中不同版本的兼容性需要留意。我在实际使用中遇到过llm request failed: provider rejected the request schema or tool payload这类报错排查下来多半是MCP server返回的schema和客户端期望的不一致。解决办法后面会详细讲。2.4 Docker在整体架构中的角色整个记忆系统我选择用Docker来部署核心原因是环境隔离和可复现性。Agent记忆系统涉及多个组件向量数据库、MCP server、嵌入模型服务、可能还有Redis做缓存。如果全部裸机安装版本冲突和依赖问题会让人崩溃。用Docker Compose编排之后每个组件跑在独立容器里网络配置和存储卷管理都清晰很多。不过Docker在Windows上的安装确实是个坎。我见过太多人卡在virtualization support not detected或者Docker Desktop failed to start这两个报错上。这部分我会在实操环节给出完整的排查路径。3. 核心模块拆解与关键参数配置3.1 短期记忆的窗口大小怎么定短期记忆的窗口大小也就是保留最近多少轮对话不是一个拍脑袋决定的数字。我的经验是它取决于三个因素任务复杂度、模型上下文长度、响应延迟要求。对于简单的问答类Agent保留最近5到8轮就够了。对于需要多步推理的任务比如代码调试或者数据分析可能需要保留10到15轮。但窗口越大prompt越长推理延迟和成本就越高。我一般会做一个简单的测算假设每轮对话平均消耗150个token模型上下文窗口是8K token系统提示词和工具描述占2K那么留给对话历史的预算大约是6K也就是40轮左右。但实际使用中我不会用满通常留30%的余量给模型生成回答。具体配置上我会在MCP server里设置一个short_term_window参数默认值是10。这个参数控制从Redis里读取最近多少条消息。如果对话轮次超过这个值最早的消息会被移出短期记忆但不会丢失——它们已经写入了长期记忆的向量库。3.2 长期记忆的向量化策略长期记忆的核心是向量化存储和相似度检索。这里有几个关键决策点嵌入模型的选择。我试过几种方案OpenAI的text-embedding-3-small、开源的BGE-M3、还有Cohere的embed。综合下来BGE-M3在中文场景下表现最稳而且可以本地部署不依赖外部API。它的向量维度是1024对于大多数Agent记忆场景够用了。如果你需要更高的检索精度可以考虑BGE-M3的large版本但推理成本会翻倍。分块策略。对话记录不能整段直接向量化需要先分块。我的做法是按“对话轮次”分块每一轮用户输入Agent回复作为一个独立的chunk。如果单轮内容超过500个token再按语义边界切分。每个chunk会附带元数据时间戳、会话ID、参与角色、话题标签。这些元数据在检索时可以用来做过滤比如“只检索最近一周的对话”或者“只检索与代码相关的内容”。相似度阈值。检索时返回多少个结果、相似度阈值设多少直接影响记忆的召回率和准确率。我的经验值是top_k设为5相似度阈值设为0.65。低于0.65的结果基本是噪声返回太多反而会干扰模型判断。这个阈值需要根据你的嵌入模型和实际数据分布做微调建议先用一批标注数据跑一下precision-recall曲线。3.3 反思记忆的生成逻辑反思记忆是“hindsight”这个项目里最有价值的部分也是最难做好的部分。它的核心思路是定期让LLM回顾历史交互提炼出可复用的经验。具体实现上我会在每天凌晨触发一个定时任务把当天所有对话记录喂给一个专门的“反思Agent”。这个Agent的prompt大概是这样的REFLECTION_PROMPT 你是一个经验提炼助手。请分析以下对话记录提取出 1. 用户反复提到的偏好或需求 2. Agent回答中出现的错误或不足 3. 可以复用的解决方案或话术模板 4. 需要避免的常见陷阱 输出格式为JSON每个条目包含type、content、confidence三个字段。 对话记录 {conversation_history} 生成的反思条目会存入一个独立的向量库检索时和长期记忆一起参与相似度匹配但权重更高。我一般会给反思记忆设置1.5倍的权重系数确保它们优先被召回。这里有个坑要注意反思Agent本身也可能产生幻觉编造出并不存在的“经验”。我的做法是给每个反思条目加一个confidence字段只有confidence大于0.8的条目才会被实际使用。同时我会定期人工抽检一部分反思条目发现错误就及时修正或删除。3.4 MCP Server的接口设计MCP Server是Agent访问记忆系统的入口它的接口设计直接决定了整个系统的易用性。我设计了四个核心工具工具名称功能输入参数输出memory_store存储一条记忆content, metadata, memory_typememory_idmemory_retrieve检索相关记忆query, top_k, threshold, filters记忆列表memory_reflect触发反思生成session_id, time_range反思条目列表memory_forget删除指定记忆memory_id 或 filter条件操作结果每个工具都遵循MCP的schema规范返回结构化的JSON。这里要特别注意memory_retrieve的返回格式我见过不少人在这一步踩坑——返回的JSON里如果包含嵌套过深的结构某些MCP客户端会解析失败报provider rejected the request schema or tool payload。我的建议是保持返回结构扁平化最多两层嵌套。4. 完整实操流程从零搭建Agent记忆系统4.1 环境准备Docker安装与避坑第一步是装Docker。如果你用的是Ubuntu直接跑官方脚本就行curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USERWindows用户会麻烦一些。首先确认你的系统是Windows 10 64位专业版或企业版家庭版不支持Hyper-V然后在BIOS里开启虚拟化支持。如果装完之后Docker Desktop启动报virtualization support not detected按这个顺序排查任务管理器 - 性能 - CPU看“虚拟化”是否显示“已启用”。如果是“已禁用”进BIOS开启Intel VT-x或AMD-V。控制面板 - 程序 - 启用或关闭Windows功能勾选“Hyper-V”和“虚拟机平台”。如果还是不行检查是否安装了其他虚拟化软件如VMware、VirtualBox它们可能和Hyper-V冲突。需要先卸载或禁用。装好Docker之后验证一下docker --version docker run hello-world如果hello-world能正常输出说明Docker环境没问题。4.2 用Docker Compose编排记忆系统接下来用Docker Compose把各个组件串起来。我的docker-compose.yml大概长这样version: 3.8 services: redis: image: redis:7-alpine ports: - 6379:6379 volumes: - redis_data:/data command: redis-server --appendonly yes qdrant: image: qdrant/qdrant:latest ports: - 6333:6333 - 6334:6334 volumes: - qdrant_data:/qdrant/storage embedding: image: ghcr.io/huggingface/text-embeddings-inference:latest ports: - 8080:80 volumes: - embedding_cache:/data command: --model-id BAAI/bge-m3 --max-client-batch-size 32 mcp-server: build: ./mcp-server ports: - 3000:3000 environment: - REDIS_URLredis://redis:6379 - QDRANT_URLhttp://qdrant:6333 - EMBEDDING_URLhttp://embedding:80 depends_on: - redis - qdrant - embedding volumes: redis_data: qdrant_data: embedding_cache:这里解释几个关键点。Redis用来存短期记忆开启appendonly yes保证持久化。Qdrant是向量数据库负责长期记忆和反思记忆的存储。Embedding服务用HuggingFace的TEI镜像跑BGE-M3模型。MCP Server是我自己写的负责对接Agent和底层存储。启动命令docker compose up -d第一次启动会拉取镜像可能需要几分钟。启动完成后用docker compose ps检查各容器状态确保都是running。4.3 MCP Server的核心代码实现MCP Server我用Python写的基于mcp官方SDK。核心逻辑分三块存储、检索、反思。存储部分async def memory_store(content: str, metadata: dict, memory_type: str): # 生成嵌入向量 embedding await get_embedding(content) # 存入Qdrant point_id str(uuid.uuid4()) await qdrant_client.upsert( collection_namememory_type, points[{ id: point_id, vector: embedding, payload: { content: content, **metadata, timestamp: datetime.now().isoformat() } }] ) # 如果是短期记忆同时写入Redis if memory_type short_term: await redis_client.lpush( fsession:{metadata[session_id]}, json.dumps({content: content, timestamp: metadata[timestamp]}) ) await redis_client.ltrim(fsession:{metadata[session_id]}, 0, 9) return {memory_id: point_id, status: stored}检索部分async def memory_retrieve(query: str, top_k: int 5, threshold: float 0.65, filters: dict None): query_vector await get_embedding(query) results await qdrant_client.search( collection_namelong_term, query_vectorquery_vector, limittop_k, score_thresholdthreshold, query_filterfilters ) # 同时检索反思记忆权重更高 reflection_results await qdrant_client.search( collection_namereflection, query_vectorquery_vector, limittop_k, score_thresholdthreshold * 0.9 ) # 合并结果反思记忆加权 merged [] for r in results: merged.append({content: r.payload[content], score: r.score, type: long_term}) for r in reflection_results: merged.append({content: r.payload[content], score: r.score * 1.5, type: reflection}) merged.sort(keylambda x: x[score], reverseTrue) return merged[:top_k]反思部分async def memory_reflect(session_id: str, time_range: str): # 从Redis和Qdrant拉取指定时间范围的对话 conversations await fetch_conversations(session_id, time_range) # 调用LLM生成反思 prompt REFLECTION_PROMPT.format(conversation_historyconversations) response await llm_client.chat(prompt) # 解析并存储反思条目 reflections json.loads(response) for item in reflections: if item[confidence] 0.8: await memory_store( contentitem[content], metadata{type: item[type], confidence: item[confidence]}, memory_typereflection ) return {reflection_count: len(reflections), status: completed}4.4 与Agent框架的对接MCP Server跑起来之后需要在Agent框架里配置MCP连接。以常见的配置为例{ mcpServers: { hindsight-memory: { url: http://localhost:3000/mcp, transport: sse } } }配置好之后Agent就可以通过MCP协议调用memory_store、memory_retrieve等工具了。我一般会在Agent的系统提示词里加一段说明你拥有一个持久化记忆系统。在回答用户问题前先调用memory_retrieve检索相关记忆。在对话结束后调用memory_store存储重要信息。每天结束时系统会自动触发memory_reflect生成反思。这样Agent就会在合适的时机主动使用记忆工具而不是被动等待。4.5 参数调优与性能测试系统跑起来之后需要做一轮参数调优。我主要关注三个指标检索命中率、响应延迟、存储成本。检索命中率方面我构造了一个包含200个问答对的测试集每个问题都有明确的相关记忆。然后调整top_k和threshold看命中率的变化top_kthreshold命中率平均延迟30.7072%120ms50.6585%180ms50.6089%210ms80.6587%260ms100.6091%340ms最终我选了top_k5, threshold0.65这组命中率和延迟比较平衡。如果你的场景对延迟不敏感可以放宽到top_k8, threshold0.60。存储成本方面BGE-M3的向量维度是1024每个向量占4KB左右。一万条记忆就是40MB对于Qdrant来说完全没压力。但如果你的记忆量到百万级就需要考虑分片和索引优化了。5. 常见问题与排查技巧实录5.1 Docker相关报错速查报错信息原因解决方法virtualization support not detectedBIOS未开启虚拟化进BIOS开启Intel VT-x/AMD-VDocker Desktop failed to startHyper-V冲突或WSL2未安装卸载冲突虚拟化软件安装WSL2docker network not foundCompose网络未创建先跑docker network create hindsight-netport is already allocated端口被占用改端口或杀掉占用进程no space left on device磁盘满docker system prune -a清理5.2 MCP连接失败的排查路径MCP连接失败最常见的报错是llm request failed: provider rejected the request schema or tool payload。这个报错信息很模糊实际原因可能有三种第一MCP Server返回的JSON schema不符合客户端期望。解决办法是用MCP Inspector工具手动测试每个工具的输入输出确保schema定义严格符合规范。特别注意required字段和type字段不能有歧义。第二网络不通。如果MCP Server跑在Docker里Agent跑在宿主机上需要确认端口映射正确并且防火墙没有拦截。我一般先用curl http://localhost:3000/mcp/health测试连通性。第三认证问题。有些MCP Server需要token认证如果token过期或格式不对也会报类似的错误。检查请求头里的Authorization字段是否正确。5.3 记忆检索不准的优化思路检索不准通常表现为明明存了相关记忆但检索时没返回或者返回了一堆不相关的内容。我的排查顺序是先检查嵌入模型是否一致。存储时用的BGE-M3检索时也必须用BGE-M3。如果中途换了模型向量空间不兼容检索结果会完全乱掉。再检查分块策略。如果chunk太大一个chunk里混了多个话题向量会变得“模糊”相似度计算不准。我的经验是单个chunk控制在200到500个token之间。最后检查元数据过滤。如果设置了filters但条件太严格可能会把相关记忆过滤掉。建议先用不加过滤的检索测试确认基础检索没问题后再逐步加过滤条件。5.4 反思记忆的“幻觉”问题前面提到过反思Agent可能编造不存在的经验。除了用confidence字段过滤我还会做一件事交叉验证。具体来说每条反思条目生成后我会用另一个LLM调用去验证它是否真的能从原始对话中推导出来。验证prompt大概是VERIFY_PROMPT 以下是一条从对话中提取的反思条目 {reflection} 以下是原始对话记录 {conversation} 请判断这条反思是否能够从原始对话中合理推导出来回答YES或NO并给出理由。 只有验证通过的条目才会被最终存储。这个步骤会增加一些计算成本但能显著降低幻觉率。实测下来加了交叉验证之后反思条目的准确率从75%左右提升到了92%以上。5.5 性能瓶颈的定位与解决系统跑了一段时间之后如果发现响应变慢可以按这个顺序排查先看Redis的慢查询日志。短期记忆的读写如果出现瓶颈通常是lpush和lrange操作太频繁。解决办法是加一个本地缓存层减少对Redis的直接访问。再看Qdrant的检索延迟。如果向量数量超过10万检索延迟会明显上升。这时候需要调整HNSW索引参数m值调大可以提高召回率但增加内存占用ef_construct调大可以提高索引质量但增加构建时间。我的经验值是m16, ef_construct100在大多数场景下够用。最后看Embedding服务的吞吐。如果并发请求太多TEI服务会排队。解决办法是加一个批处理层把多个嵌入请求合并成一个batch发送。TEI支持max_client_batch_size参数我一般设为32。6. 一些实操心得和后续扩展方向这套记忆系统我在两个项目里实际跑过一个是多轮客服Agent一个是代码助手Agent。客服场景下记忆系统让用户重复描述问题的比例下降了60%以上代码助手场景下Agent能记住用户的项目结构和技术栈偏好生成的代码更贴合实际需求。踩过的坑里最让我头疼的是记忆污染问题。所谓记忆污染就是错误的信息被存入长期记忆然后在后续检索中被反复召回导致Agent持续犯错。我的解决办法是加一个“记忆审核”环节所有写入长期记忆的内容先经过一个轻量级分类器判断是否包含事实性错误或矛盾信息。这个分类器可以用小模型跑成本很低但效果很明显。后续扩展方向有几个。一是记忆的时效性管理给每条记忆加一个衰减因子时间越久权重越低避免过时信息干扰。二是跨Agent记忆共享多个Agent共用一个记忆池但通过权限控制隔离敏感信息。三是记忆的可视化做一个Dashboard展示记忆的存储、检索、反思全流程方便调试和优化。如果你也在做Agent记忆相关的项目我的建议是先从最简单的短期记忆做起跑通之后再逐步加长期记忆和反思记忆。不要一上来就追求大而全那样很容易在细节里迷失。先把一个场景做深做透再考虑泛化。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。