拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于MCP与Docker的Agent Memory实战:从hindsight到分层记忆架构

基于MCP与Docker的Agent Memory实战:从hindsight到分层记忆架构

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

第一次看到“hindsight”这个词,我脑子里蹦出来的不是词典释义,而是自己踩过的一个坑。去年我搭了一个基于LLM的客服Agent,跑demo的时候一切正常,用户问“我上周买的那个订单到哪了”,它能准确调出订单信息。但上线第三天,同一个用户回来问“我上次说的那个退款问题解决了吗”,Agent一脸茫然——它完全不记得三天前发生过什么。那一刻我意识到,一个没有记忆的Agent,本质上就是个每次都要重新开机的计算器。

hindsight这个词,字面意思是“事后之明”,也就是回头看的时候才明白事情的前因后果。放到Agent memory这个领域里,它指向的核心问题是:Agent如何有效地回顾、检索和利用过去的交互经验。这不是简单的“把聊天记录存下来”就完事了,而是涉及存储结构、检索策略、上下文注入、记忆衰减等一系列工程问题。

我后来花了不少时间研究这块,发现社区里关于Agent memory的讨论虽然多,但大多停留在概念层面,真正能落地的方案不多。直到我接触到MCP协议和Docker化的部署方式,才找到一条相对清晰的路径。这篇文章就是把我这段时间的实践整理出来,从hindsight这个视角切入,聊聊Agent memory到底该怎么设计、怎么实现、怎么避坑。

适合谁看?如果你正在做LLM应用开发,尤其是涉及多轮对话、长期记忆、知识库检索的场景,这篇文章应该能帮你少走一些弯路。如果你只是刚接触Agent概念,也没关系,我会尽量用生活化的类比把原理讲清楚。

2. Agent Memory的核心设计思路拆解

2.1 为什么“存下来”不等于“记得住”

很多人对Agent memory的第一个误解,就是觉得把对话历史塞进数据库就完事了。我早期也是这么想的,结果发现两个致命问题。

第一个问题是上下文窗口的物理限制。你不可能把过去三个月的对话全部塞进prompt里,就算模型支持128K token,成本和延迟也扛不住。第二个问题是检索的精准度。就算你存了所有历史,用户问“上次那个问题”的时候,你怎么知道“上次”是哪次?“那个问题”又是什么问题?

这就引出了hindsight的核心设计理念:记忆不是日志,而是经过提炼和索引的经验。日志是流水账,记忆是有结构的认知。就像你自己回忆事情的时候,不会把过去一周每一秒都过一遍,而是提取关键节点和关联信息。

所以我在设计Agent memory的时候,遵循了三个原则:

  • 分层存储:短期记忆(当前会话)、工作记忆(近期任务上下文)、长期记忆(跨会话的知识和经验)分开管理,不同层级的存储介质、检索策略、过期策略都不一样。
  • 语义索引:不是靠时间戳或关键词来检索,而是靠语义相似度。用户说“退款问题”,系统能关联到“退货流程”“订单异常”“客服工单”等相关记忆。
  • 主动遗忘:不是所有记忆都值得保留。低价值、重复、过期的记忆需要被清理或降权,否则检索质量会被噪声淹没。

2.2 从“token三点”看记忆的检索逻辑

社区里有个很形象的比喻,说LLM的token有三个关键点:key是“我是谁”,query是“我在找什么”,value是“我能提供什么”。这个比喻放到Agent memory的检索上特别贴切。

当用户发起一个请求时,Agent需要做三件事:

  1. 确定自己的身份和角色(key):我是一个客服Agent?还是一个代码助手?还是一个知识库问答机器人?不同角色对记忆的需求完全不同。
  2. 理解用户当前在找什么(query):用户的问题表面上是“退款进度”,但深层需求可能是“我的钱什么时候能回来”。
  3. 匹配能提供价值的记忆(value):从记忆库中找到与当前query最相关的历史信息,注入到当前上下文中。

我实际实现的时候,用的是向量检索+元数据过滤的组合方案。向量检索负责语义匹配,元数据过滤负责缩小范围(比如只检索最近30天的记忆,或者只检索与当前任务类型相关的记忆)。这样既保证了召回率,又控制了噪声。

2.3 MCP协议在记忆管理中的角色

MCP(Model Context Protocol)是最近社区里讨论很多的一个协议,它的核心价值在于标准化了LLM与外部工具、数据源之间的交互方式。放到Agent memory的场景里,MCP解决了一个很实际的问题:记忆存储不应该和Agent逻辑耦合在一起。

我之前的做法是把记忆管理逻辑直接写在Agent代码里,结果每次换存储方案(从SQLite换到Redis,从Redis换到向量数据库)都要改一大堆代码。后来用MCP的思路重构,把记忆存储抽象成一个独立的服务,Agent通过标准协议调用,换存储方案的时候只需要改配置,不用动业务逻辑。

具体来说,MCP定义了几种交互模式:资源读取、工具调用、提示模板。记忆管理主要用到工具调用——Agent通过MCP调用“存储记忆”“检索记忆”“更新记忆”等工具,具体的存储实现对Agent透明。

注意:MCP目前还在演进中,不同实现的兼容性有差异。我在选型的时候踩过坑,有些库声称支持MCP但实际只实现了部分功能,建议优先选择社区活跃、文档完整的实现。

2.4 Docker化部署:让记忆服务独立运行

把记忆服务Docker化,是我觉得最值得的一个工程决策。原因很简单:记忆服务需要独立于Agent的生命周期。Agent可能重启、升级、迁移,但记忆数据不能丢。

我用Docker Compose编排了三个服务:

  • Agent服务:跑LLM推理和业务逻辑
  • Memory服务:负责记忆的存储、检索、更新
  • Vector DB服务:专门跑向量数据库(我用的是Qdrant,轻量且性能不错)

三个服务通过内部网络通信,Memory服务暴露MCP接口给Agent服务调用。这样即使Agent服务挂了重启,记忆数据依然在Vector DB里完好无损。

Docker化还有一个好处是环境一致性。我之前在本地开发的时候用SQLite存记忆,部署到服务器上换成PostgreSQL,结果SQL方言不兼容,调了半天。现在全部Docker化,开发环境和生产环境用同一套镜像,这种问题基本消失了。

3. 核心细节解析与实操要点

3.1 记忆的分层结构设计

我在实际项目中把Agent memory分成了四层,每层的职责和实现方式都不一样:

层级存储内容存储介质过期策略检索方式
瞬时记忆当前对话轮次内存会话结束即清除直接拼接
工作记忆当前任务上下文Redis任务完成后24小时结构化查询
短期记忆近期会话摘要Redis/PostgreSQL7天向量+时间过滤
长期记忆跨会话知识经验向量数据库永久(带衰减权重)向量检索+重排序

这个分层结构不是拍脑袋定的,而是根据实际使用场景反推出来的。比如瞬时记忆之所以放内存,是因为它只在当前对话轮次内有效,存数据库纯属浪费。工作记忆放Redis,是因为它需要快速读写,而且任务完成后就可以清理。长期记忆放向量数据库,是因为它需要语义检索能力。

3.2 记忆的写入策略:什么时候该记,什么时候不该记

这是我觉得最容易踩坑的地方。早期我的做法是“每轮对话都存”,结果记忆库迅速膨胀,检索质量急剧下降。后来我总结了一套写入策略:

必须写入的记忆:

  • 用户明确表达的偏好(“我喜欢简洁的回答”)
  • 任务的关键决策点(“用户选择了方案B”)
  • 错误和纠正(“之前说的日期是错的,应该是周三”)
  • 跨会话的承诺(“下次登录时提醒用户续费”)

不应该写入的记忆:

  • 寒暄和闲聊(“你好”“谢谢”)
  • 重复确认(“好的”“明白了”)
  • 临时性信息(“我现在在开会”)
  • 模型自己的推理过程(除非用户明确要求保留)

实现上,我用了一个轻量的分类器来判断当前对话轮次是否值得写入长期记忆。分类器本身也是LLM调用,但用的是小模型(比如7B级别的),成本可控。判断逻辑大概是:如果这轮对话包含新的信息、决策、偏好或承诺,就写入;否则只保留在短期记忆中。

实操心得:写入策略的阈值需要根据业务场景调。客服场景可以宽松一些,因为用户偏好很重要;代码助手场景可以严格一些,因为大部分对话都是调试过程,不值得长期保留。

3.3 记忆的检索与注入:怎么让Agent“想起来”

检索这块我试过好几种方案,最后稳定下来的流程是:

  1. Query改写:用户的问题往往很模糊(“上次那个事”),需要先用LLM改写成适合检索的形式(“用户上次咨询的退款问题”)。
  2. 多路召回:同时用向量检索、关键词检索、时间范围检索三条路召回候选记忆。
  3. 重排序:用交叉编码器对候选记忆进行精排,选出最相关的Top-K条。
  4. 上下文注入:把选出的记忆格式化成自然语言,注入到当前prompt中。

这里有个细节很重要:注入的记忆要标注时间。比如“用户在2024年3月15日提到过退款问题”,而不是“用户提到过退款问题”。时间信息能帮助LLM判断记忆的时效性,避免用过期的信息回答当前问题。

另一个细节是记忆的冲突处理。如果检索到两条矛盾的记忆(比如用户先说“我喜欢红色”,后来说“我现在喜欢蓝色”),需要在注入时标注时间顺序,让LLM自己判断哪条更新。

3.4 MCP接口的具体实现

我用Python实现了一个MCP Server,暴露了以下几个工具:

# 记忆存储工具 @mcp.tool() def store_memory(content: str, memory_type: str, metadata: dict) -> str: """存储一条记忆 Args: content: 记忆内容 memory_type: 记忆类型(preference/decision/error/commitment) metadata: 元数据(时间戳、会话ID、用户ID等) """ # 向量化 embedding = embed(content) # 存储到向量数据库 vector_db.upsert(embedding, content, metadata) return "记忆已存储" # 记忆检索工具 @mcp.tool() def retrieve_memory(query: str, top_k: int = 5, time_range: str = None) -> list: """检索相关记忆 Args: query: 检索查询 top_k: 返回条数 time_range: 时间范围过滤(如"7d"表示最近7天) """ # Query改写 rewritten_query = rewrite_query(query) # 向量检索 results = vector_db.search(rewritten_query, top_k, time_range) # 重排序 reranked = rerank(results, query) return reranked

Agent端通过MCP客户端调用这些工具,不需要关心底层用的是Qdrant还是Milvus,也不需要关心向量化用的是什么模型。这种解耦带来的灵活性,在后期换模型、换数据库的时候体现得特别明显。

3.5 Docker Compose编排配置

我的docker-compose.yml大概长这样:

version: '3.8' services: agent: build: ./agent ports: - "8000:8000" environment: - MCP_SERVER_URL=http://memory:8001 depends_on: - memory networks: - agent-net memory: build: ./memory ports: - "8001:8001" environment: - VECTOR_DB_URL=http://vectordb:6333 - REDIS_URL=redis://redis:6379 depends_on: - vectordb - redis networks: - agent-net vectordb: image: qdrant/qdrant:latest volumes: - ./data/qdrant:/qdrant/storage networks: - agent-net redis: image: redis:7-alpine volumes: - ./data/redis:/data networks: - agent-net networks: agent-net: driver: bridge

这个编排有几个关键点:数据卷挂载保证容器重启后数据不丢;内部网络保证服务间通信不走公网;依赖顺序保证启动时不会因为服务未就绪而报错。

注意:Docker Desktop在Windows上安装时经常遇到“Virtualization support not detected”的问题,需要在BIOS里开启虚拟化支持。另外WSL2后端比Hyper-V后端性能更好,建议优先用WSL2。

4. 实操过程与核心环节实现

4.1 环境准备:从零搭建开发环境

我假设你用的是Windows或者macOS,Linux用户应该已经很熟悉这些操作了。

第一步:安装Docker Desktop

Windows用户去Docker官网下载安装包,安装时勾选“Use WSL 2 instead of Hyper-V”。macOS用户直接下载dmg安装即可。安装完成后在终端运行:

docker --version docker compose version

如果都能正常输出版本号,说明安装成功。

第二步:拉取基础镜像

docker pull qdrant/qdrant:latest docker pull redis:7-alpine docker pull python:3.11-slim

第三步:创建项目结构

agent-memory/ ├── agent/ │ ├── Dockerfile │ └── main.py ├── memory/ │ ├── Dockerfile │ └── server.py ├── data/ │ ├── qdrant/ │ └── redis/ └── docker-compose.yml

第四步:编写Memory服务的Dockerfile

FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python", "server.py"]

requirements.txt里主要包含:mcp、qdrant-client、redis、sentence-transformers、fastapi、uvicorn。

4.2 记忆服务的核心代码实现

Memory服务的核心逻辑我拆成了三个模块:向量化模块、存储模块、检索模块。

向量化模块负责把文本转成向量。我用的是sentence-transformers的all-MiniLM-L6-v2模型,384维,速度快,效果够用。如果你对精度要求更高,可以换成bge-large-zh,但推理速度会慢一些。

from sentence_transformers import SentenceTransformer model = SentenceTransformer('all-MiniLM-L6-v2') def embed(text: str) -> list: return model.encode(text).tolist()

存储模块负责把向量和元数据写入Qdrant。我设计了一个collection叫agent_memory,每个point包含:向量、原始文本、记忆类型、时间戳、会话ID、用户ID。

from qdrant_client import QdrantClient from qdrant_client.models import PointStruct, VectorParams, Distance client = QdrantClient(url="http://vectordb:6333") client.recreate_collection( collection_name="agent_memory", vectors_config=VectorParams(size=384, distance=Distance.COSINE) ) def store(content, memory_type, metadata): vector = embed(content) point = PointStruct( id=generate_id(), vector=vector, payload={ "content": content, "type": memory_type, "timestamp": metadata["timestamp"], "session_id": metadata["session_id"], "user_id": metadata["user_id"] } ) client.upsert(collection_name="agent_memory", points=[point])

检索模块负责根据query召回相关记忆。我用了两阶段检索:先用向量相似度召回Top-20,再用时间衰减和类型权重做重排序。

def retrieve(query, top_k=5, time_range=None): query_vector = embed(query) # 构建过滤条件 filters = {} if time_range: filters["timestamp"] = {"gte": calculate_time_threshold(time_range)} # 向量检索 results = client.search( collection_name="agent_memory", query_vector=query_vector, limit=20, query_filter=filters ) # 重排序:时间越近权重越高,偏好类记忆权重更高 scored = [] for r in results: score = r.score # 时间衰减 days_ago = (now() - r.payload["timestamp"]).days time_weight = 1.0 / (1.0 + days_ago * 0.1) # 类型权重 type_weight = {"preference": 1.2, "decision": 1.1, "error": 1.0, "commitment": 1.3}.get(r.payload["type"], 1.0) scored.append((r, score * time_weight * type_weight)) scored.sort(key=lambda x: x[1], reverse=True) return [s[0] for s in scored[:top_k]]

4.3 Agent端的记忆注入实现

Agent端通过MCP客户端调用Memory服务,把检索到的记忆注入到prompt中。我用的prompt模板大概是这样的:

你是一个智能助手,以下是用户的历史记忆: {memories} 请根据以上记忆和当前对话,回答用户的问题。 当前对话: 用户:{user_input}

memories的格式化方式:

[2024-03-15] 用户偏好:喜欢简洁的回答,不需要过多解释 [2024-03-20] 任务决策:用户选择了方案B,放弃了方案A [2024-03-22] 错误纠正:用户之前说的日期是周三,实际是周四

这种格式化的好处是LLM能清楚看到每条记忆的时间和类型,便于判断时效性和相关性。

4.4 完整启动流程

# 1. 构建镜像 docker compose build # 2. 启动服务 docker compose up -d # 3. 检查服务状态 docker compose ps # 4. 查看日志 docker compose logs -f memory # 5. 测试记忆存储 curl -X POST http://localhost:8001/store \ -H "Content-Type: application/json" \ -d '{"content": "用户喜欢简洁的回答", "memory_type": "preference", "metadata": {"timestamp": "2024-03-15T10:00:00", "session_id": "s1", "user_id": "u1"}}' # 6. 测试记忆检索 curl -X POST http://localhost:8001/retrieve \ -H "Content-Type: application/json" \ -d '{"query": "用户的回答偏好", "top_k": 3}'

如果一切正常,你应该能看到检索结果返回了刚才存储的记忆。

4.5 性能调优参数

在实际压测中,我调整了几个关键参数来平衡性能和效果:

参数默认值调整后影响
向量维度384384维度越高精度越高但速度越慢
召回数量2030召回越多重排序效果越好但延迟增加
返回数量55注入太多记忆会挤占上下文窗口
时间衰减系数0.10.05系数越小记忆衰减越慢
批量写入大小1100批量写入能显著提升吞吐量

实测下来,单次检索延迟在50-80ms左右(不含LLM推理),写入延迟在20-30ms。对于大部分应用场景来说够用了。

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

5.1 记忆检索不准确怎么办

这是最常见的问题。用户问“上次那个问题”,检索出来的记忆完全不相关。排查思路:

第一步:检查Query改写是否生效。如果Query改写没做好,原始query太模糊,向量检索自然不准。可以在检索日志里打印改写前后的query对比。

第二步:检查向量模型是否适合中文。all-MiniLM-L6-v2对中文的支持一般,如果业务以中文为主,建议换成bge-base-zh或text2vec-base-chinese。

第三步:检查重排序逻辑。有时候向量检索召回了正确的记忆,但重排序把它排到了后面。可以临时关闭重排序,看看原始检索结果是否包含正确答案。

第四步:检查记忆写入质量。如果写入的记忆本身就是模糊的(比如“用户说了什么”),检索自然不准。写入时应该尽量保留原始语义,不要过度摘要。

5.2 Docker网络不通的排查

Docker Compose默认会创建一个bridge网络,服务间通过服务名互相访问。如果遇到网络不通,按以下顺序排查:

# 1. 检查服务是否在同一网络 docker network inspect agent-memory_agent-net # 2. 进入容器测试连通性 docker exec -it agent-memory-agent-1 ping memory # 3. 检查端口是否监听 docker exec -it agent-memory-memory-1 netstat -tlnp # 4. 检查防火墙规则 # Windows: 检查Windows Defender防火墙 # macOS: 检查系统偏好设置-安全性与隐私-防火墙

我遇到最多的问题是服务启动顺序。Agent服务启动时Memory服务还没就绪,导致连接失败。解决方案是在docker-compose.yml里加depends_on和健康检查:

memory: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8001/health"] interval: 10s timeout: 5s retries: 3

5.3 记忆库膨胀导致检索变慢

跑了一段时间后,记忆库可能积累了几万条记录,检索延迟从50ms涨到500ms。解决方案:

  • 定期归档:把超过90天的低权重记忆移到冷存储,不参与实时检索。
  • 去重:相似度超过0.95的记忆只保留最新的一条。
  • 索引优化:Qdrant支持HNSW索引,调整m和ef_construct参数可以平衡速度和精度。
  • 分片:如果数据量特别大,可以按用户ID分片,每个用户一个collection。

5.4 常见问题速查表

问题现象可能原因解决方案
检索结果不相关Query改写未生效检查改写逻辑,打印改写前后对比
检索延迟高记忆库过大或索引未优化归档旧记忆,调整HNSW参数
记忆丢失容器重启未挂载数据卷检查docker-compose volumes配置
写入失败向量维度不匹配检查embedding模型和collection配置
服务启动失败端口冲突或依赖未就绪检查端口占用,加健康检查
中文检索效果差向量模型不支持中文换成bge-base-zh或text2vec
记忆冲突新旧记忆矛盾注入时标注时间,让LLM判断
上下文超长注入记忆过多减少top_k,或压缩记忆内容

5.5 几个独家避坑技巧

技巧一:记忆写入时保留原始时间戳。不要用写入时间,要用对话发生的时间。否则用户补录历史对话时,时间线会乱。

技巧二:给记忆加“置信度”字段。用户明确说的偏好置信度高,模型推断的偏好置信度低。检索时按置信度加权,能有效减少误召回。

技巧三:定期做记忆“体检”。每周跑一次脚本,统计记忆库的类型分布、时间分布、重复率。如果发现某类记忆异常增长,说明写入策略需要调整。

技巧四:用A/B测试验证记忆效果。开两组Agent,一组带记忆,一组不带,对比用户满意度和任务完成率。数据会告诉你记忆到底有没有用。

技巧五:记忆注入的位置很重要。放在system prompt里比放在user message里效果更好,因为LLM对system prompt的注意力权重更高。

6. 记忆衰减与主动遗忘的实现细节

6.1 为什么需要主动遗忘

人的大脑会遗忘,这不是缺陷,而是特性。如果所有记忆都同等重要,检索的时候就会被噪声淹没。Agent memory也一样,需要一套衰减机制来区分“值得记住的”和“可以忘掉的”。

我试过几种衰减策略,最后稳定下来的是指数衰减+类型权重的组合。每条记忆有一个权重值,初始为1.0,随着时间推移按指数衰减:

weight = initial_weight * exp(-lambda * days_ago)

lambda是衰减系数,我设的是0.05,意味着大约14天后权重降到初始值的一半。但不同类型的记忆衰减速度不一样:偏好类记忆衰减慢(lambda=0.02),临时决策衰减快(lambda=0.1)。

6.2 遗忘策略的具体实现

遗忘不是直接删除,而是分三步走:

第一步:降权。超过一定时间的记忆,权重自动降低,在检索排序中靠后。

第二步:归档。权重低于阈值的记忆,从热存储移到冷存储。冷存储可以是本地文件或者低成本对象存储,不参与实时检索,但需要的时候还能查。

第三步:清理。超过保留期限且权重极低的记忆,直接删除。保留期限根据业务需求定,我一般设90天。

def decay_and_archive(): # 查询所有记忆 all_memories = client.scroll(collection_name="agent_memory", limit=10000) for memory in all_memories: days_ago = (now() - memory.payload["timestamp"]).days lambda_val = {"preference": 0.02, "decision": 0.1, "error": 0.05, "commitment": 0.01}.get(memory.payload["type"], 0.05) weight = math.exp(-lambda_val * days_ago) if weight < 0.1: # 归档 archive_to_cold_storage(memory) client.delete(collection_name="agent_memory", points_selector=[memory.id]) elif weight < 0.3: # 降权 client.set_payload( collection_name="agent_memory", payload={"weight": weight}, points=[memory.id] )

6.3 记忆冲突的处理

当新旧记忆矛盾时,我的处理策略是保留两条,但标注时间。不直接删除旧记忆,因为有时候用户会回溯(“我之前说的那个方案,再想想还是用旧的吧”)。

注入的时候按时间倒序排列,LLM看到最新的记忆在前面,自然会优先采用。如果用户明确问“我之前说过什么”,LLM也能看到历史记忆的演变过程。

实操心得:记忆冲突在客服场景特别常见,用户今天说“我要退款”,明天说“算了不退了”。如果直接覆盖旧记忆,用户反悔的时候Agent就懵了。保留两条记忆,让LLM根据当前语境判断,效果更好。

7. 从hindsight视角看Agent Memory的演进方向

7.1 当前方案的局限性

说实话,我现在这套方案离“理想中的Agent memory”还有距离。最大的局限是记忆的主动性。现在的记忆检索是被动的——用户问了才去查。但真正的hindsight应该是主动的——Agent在回答之前就预判需要哪些记忆,甚至主动提醒用户“你上次说过要处理这件事”。

另一个局限是跨模态记忆。现在只处理文本,但实际场景中用户可能发图片、语音、文件。这些非文本信息的记忆存储和检索,目前还没有特别成熟的方案。

7.2 社区里值得关注的方向

最近社区里有个叫a-memguard的项目,思路挺有意思。它把记忆安全作为一个独立问题来对待,提出了“主动防御”的概念——不是等记忆被污染了再修复,而是在写入和检索环节就做过滤和验证。这个思路我觉得值得借鉴,尤其是面向企业级应用的时候,记忆的可靠性和安全性比性能更重要。

另外LLM wiki知识库和GraphRAG的结合也是一个方向。传统的向量检索是扁平的,GraphRAG能建立记忆之间的关联关系,检索的时候可以沿着关系链扩展,召回更全面。我试过用Neo4j做记忆图谱,效果不错,但工程复杂度高了不少,小项目可能不太划算。

7.3 给不同阶段开发者的建议

如果你刚开始做Agent memory,我的建议是先用最简单的方案跑起来。SQLite+关键词检索就够了,别一上来就上向量数据库。等业务量上来了,检索质量成为瓶颈了,再逐步升级。

如果你已经在用向量数据库了,下一步可以优化的是检索策略。多路召回+重排序是性价比最高的优化,比换更大的向量模型效果更明显。

如果你在做企业级应用,记忆的安全和合规要优先考虑。哪些记忆可以存、存多久、谁能访问,这些都需要在设计阶段就想清楚。

最后再分享一个小技巧:定期用真实用户query做检索测试。我每周会抽100条真实query,人工标注哪些记忆应该被召回,然后对比系统实际召回的结果,算准确率和召回率。这个习惯帮我发现了好几个隐藏的检索bug,比看日志管用多了。

返回列表