在真实的 AI 编程工作流中,每次新会话的开始往往伴随着上下文重建的沉重代价。代理需要重新理解项目的技术栈、已做出的架构决策、以及某些关键的调试结论。传统的方案 —— 无论是 CLAUDE.md 还是 MEMORY.md—— 本质上都是静态文件,它们的容量受限(通常被限制在 200 行左右),且无法在多个代理之间共享。agentmemory 试图解决的核心问题,是在不引入外部向量数据库的前提下,实现一个能够跨会话、跨代理运行的持久化记忆引擎,并在 LongMemEval-S 基准测试中达到 R@5 为 95.2% 的召回精度,同时将每年代理的 token 消耗从 1950 万以上压缩至约 17 万。
四层记忆合并:从原始观测到结构化事实
agentmemory 的记忆 pipeline 并不是在会话结束时一次性将所有数据倾倒进存储中,而是借鉴了人类记忆的认知模型,设计了一套四层递进的记忆合并机制。
第一层为工作记忆(Working Memory),对应的是代理在会话过程中产生的原始观测。每当 PostToolUse 钩子触发时,系统会记录工具名称、输入参数和输出结果,经过 SHA-256 去重(在 5 分钟时间窗口内避免重复写入同一操作)后,以原始观测的形式暂存。去重机制的存在是为了避免在高频交互场景(如循环调用 linter 或 formatter)中产生大量冗余记录。去重之后,数据会通过隐私过滤器,剥离 API 密钥、令牌以及带有 <private> 标签的内容。隐私过滤是增量写入链路中的第一个性能敏感节点 —— 如果过滤器使用正则表达式逐条匹配敏感模式而非基于预编译的词汇表,压缩吞吐会显著下降。
第二层为情节记忆(Episodic Memory),发生在会话终止时。代理的行为轨迹被压缩为一段结构化的会话摘要,其中包含关键决策、遇到的问题以及最终采用的解决方案。这个压缩过程默认由 LLM 驱动,但如果在 ~/.agentmemory/.env 中设置了 ANTHROPIC_API_KEY 或其他 LLM 提供者的凭证,系统会自动调用。如果未配置任何 LLM 提供者(默认状态),系统会退化为基于 BM25 的合成压缩 —— 即通过关键词提取生成摘要。这种降级策略保证了无外部依赖情况下的基本可用性,但也意味着在未配置 LLM 时,情节记忆的信息密度会显著低于 LLM 驱动的压缩结果。
第三层为语义记忆(Semantic Memory),是系统从情节记忆中进一步提炼出的事实与模式。系统会提取关键实体(如文件名、函数名、库名)、它们之间的关系,以及代码层面的决策逻辑。当 GRAPH_EXTRACTION_ENABLED=true 时,这一步还会构建知识图谱,节点为实体,边为关系类型。知识图谱的提取是可选的,因为图谱构建和 BFS 遍历会带来额外的索引开销。
第四层为程序性记忆(Procedural Memory),存储工作流模式和决策模式。它回答的是 “如何做” 而非 “是什么”—— 例如 “在此项目中,Bug 报告的处理流程是先在本地复现,然后提交最小可复现用例,最后在对应的 issue 中附上测试用例”。程序性记忆支持 /recall 技能的高阶检索,当代理搜索 “如何修复 N+1 查询” 时,系统会同时匹配语义记忆中的实体关系和程序性记忆中的工作流模式。
记忆并非永久存储。系统实现了基于艾宾浩斯遗忘曲线的衰减机制 —— 频繁访问的记忆会加强,长期未访问的记忆会逐渐降级直至自动清除。矛盾检测机制会在新记忆与旧记忆产生冲突时触发,通常表现为旧记忆被标记为 “已过时” 并创建新的版本链接。自动遗忘策略包括 TTL 过期、重要性评分驱逐和矛盾覆盖三种触发条件,这三者在记忆合并过程中按优先级依次评估。
三流检索架构:BM25、向量与知识图谱的 RRF 融合
检索是记忆系统的核心性能瓶颈。agentmemory 没有选择单一的检索策略,而是实现了三层检索流的 Reciprocal Rank Fusion(RRF,k=60)融合架构。
BM25 流始终处于激活状态。BM25 是一种基于词频 - 逆文档频率的概率检索模型,它对精确关键词匹配高度敏感 —— 例如搜索 "JWT 认证" 时,BM25 会优先返回明确包含 "JWT" 和 "认证" 这两个词的记忆条目。agentmemory 在 BM25 之上增加了同义词扩展,例如 "bug" 和 "defect" 被视为相关词。BM25 的优势在于它的确定性 —— 结果可复现、延迟可预测 —— 但它的根本局限在于无法理解语义等价性:搜索 "数据库性能优化" 时,基于关键词的 BM25 无法返回 "N+1 查询修复" 这条记忆,即使两者在语义上高度相关。
向量流弥补了 BM25 的语义盲区。系统支持多种 embedding 提供者,本地推荐使用 all-MiniLM-L6-v2 模型,这是一个轻量级的 384 维嵌入模型,可以在 CPU 上以约 50-100 毫秒的延迟完成单条观测的向量化。如果选择本地模式,向量计算完全免费且无需网络调用;如果选择云端 embedding(如 OpenAI 的 text-embedding-3-small 或 Voyage AI 的 voyage-code-3),则需要对应的 API 密钥和相应的网络延迟预算。向量检索的核心参数是余弦相似度阈值 —— 阈值过低会引入语义相关但上下文无关的噪音结果,阈值过高则可能遗漏语义相近但表述不同的有效记忆。
知识图谱流在查询中检测到实体时激活。当用户搜索 "认证中间件" 时,系统首先识别 "认证" 和 "中间件" 为实体,然后通过 BFS 遍历知识图谱,找到所有与这些实体直接或间接相连的节点。知识图谱检索的优势在于它能够捕捉跨领域的关系 —— 例如找到与 "中间件" 关联的文件同时也与 "安全" 关联的情况。
三层检索流的结果通过 RRF(k=60)进行融合。RRF 的核心思想是将不同检索流中同一结果项的排名倒数进行加权求和,然后按总分重新排序。参数 k=60 的作用是抑制单个检索流中排名过高结果对最终排名的过度主导 —— 如果一个结果在 BM25 中排名第一而在向量检索中排名第十,其 RRF 分数为 1/60 + 1/(60+10),而非简单的 1 + 1/10,这种设计确保了多流结果的均衡性。
会话多样性约束进一步优化了结果的覆盖度:系统强制保证最终返回的结果中,来自同一会话的记忆条目不超过 3 条。这一约束防止了高度活跃的会话在结果集中过度占据位置,从而保证了检索结果的多样性。在实际测试中,如果不加会话多样性约束,某些高频会话的记忆条目会占据 R@5 结果中的 4-5 个位置,导致其他会话的相关记忆被完全排除。
增量写入的成本分析:token 消耗与压缩策略
在增量写入场景中,token 消耗主要来自三个环节:原始观测的 LLM 压缩、向量化计算和会话摘要生成。根据基准测试数据,agentmemory 在一年内约消耗 17 万 tokens(使用本地 embedding 模型),折合成本约 10 美元。相比之下,将完整上下文粘贴进每个会话会消耗 1950 万以上 tokens,直接超出任何模型的上下文窗口容量。
LLM 压缩是将原始观测转化为结构化记忆的核心环节。通过 AGENTMEMORY_AUTO_COMPRESS 环境变量可以控制是否在每次 PostToolUse 触发时立即调用 LLM 进行压缩。默认状态下此选项是关闭的,因为活跃会话中的每次工具使用都调用 LLM 会导致 token 消耗急剧上升。推荐的做法是在会话结束(Stop 钩子触发)时一次性压缩整个会话的观测,这比逐条压缩节省约 60% 的 token 消耗。
向量化的 token 消耗与 embedding 模型的维度直接相关。all-MiniLM-L6-v2 的 384 维输出意味着每条观测生成 384 个浮点数,存储开销约为 1.5KB。这在 SQLite 数据库中可以高效存储和检索,因为 SQLite 原生支持数值类型的数组操作。如果使用更高维度的 embedding 模型(如 OpenAI 的 text-embedding-3-small 支持 1536 维),存储开销会增至约 6KB,但检索精度的提升在大多数场景下并不显著。对于代码密集型的记忆库,voyage-code-3 可能是更优选择,因为它针对代码语义进行了优化,能够更好地理解函数调用关系和代码结构。
会话摘要的压缩率是决定整体 token 效率的关键因素。一小时的编程会话可能产生 200-300 条原始观测,总 token 消耗约为 1.5 万到 2 万 tokens。经过 LLM 压缩后,摘要通常可以压缩至 500-800 tokens,压缩比约为 20:1 到 25:1。这意味着每 20-25 条原始观测被压缩为一条结构化记忆,大幅降低了长期存储和检索的成本。
基准测试数据与召回精度的实际意义
agentmemory 在 LongMemEval-S(ICLR 2025,500 个问题)上的表现显示了分层记忆合并与混合检索架构的有效性。R@5 达到 95.2% 意味着在 500 个测试问题中,系统在返回的 Top-5 结果中包含了正确答案的情况占 95.2%。作为对比,仅使用 BM25 降级方案时,R@5 下降至 86.2%,R@10 从 98.6% 下降至 94.6%,MRR(平均倒数排名)从 88.2% 骤降至 71.5%。
MRR 的下降幅度远大于 R@5 的下降幅度,这揭示了一个关键洞察:BM25 降级方案并非完全无法找到正确答案,而是将正确答案的排名推后了。当正确答案从 Top-5 降到 Top-10 甚至更低时,R@5 仍然会记录一次 “找到” 的命中(如果正确答案恰好在第 6-10 名),但 MRR 会显著下降,因为倒数排名从约 1/3 降至约 1/8。对于依赖首次检索结果的应用场景 —— 比如自动将记忆注入到代理的上下文窗口 ——MRR 的下降意味着需要更大的 token 预算来容纳更多候选记忆,或者接受更高的上下文窗口浪费率。
R@10 保持在 94.6% 的高位表明,即使在仅使用 BM25 的最坏情况下,系统仍然能够在绝大多数情况下找到正确答案,只是可能需要翻阅更多候选结果。这对于需要高召回率的场景(如安全审计或合规检查遍历所有相关记忆)是可以接受的,但对于需要低延迟响应的交互式场景,这种延迟是不可接受的。向量检索的加入将 R@5 从 86.2% 提升至 95.2%,绝对提升约 9 个百分点,但更重要的是将 MRR 从 71.5% 提升至 88.2%—— 这意味着正确答案在平均意义上从第 6-7 名左右提升至第 2-3 名,大幅缩短了代理定位正确上下文所需的时间。
工程落地的关键配置参数
在实际部署中,有几个参数对系统性能和资源消耗有决定性影响。
TOKEN_BUDGET 控制每次会话启动时注入上下文的 token 上限,默认值为 2000。对于大型项目,这个值可能不足以容纳所有相关记忆,系统会在注入前按 RRF 分数排序并截断。如果发现代理频繁抱怨 “上下文不够”,可以尝试将 TOKEN_BUDGET 提高至 3000-4000,但需要权衡代理的上下文窗口利用率 —— 超过 60% 的上下文被记忆占据可能挤压模型进行实际推理的空间。
BM25_WEIGHT 和 VECTOR_WEIGHT 控制两层检索流在 RRF 融合中的权重配比。默认值分别为 0.4 和 0.6,这个配比是基于 LongMemEval-S 基准测试结果调优的 —— 向量检索在语义匹配上略占优势,但 BM25 在精确术语匹配上不可替代。如果你的项目高度依赖特定术语或领域专有名词,可以将 BM25_WEIGHT 调高至 0.5;如果你更多地是进行模糊的意图匹配而非精确术语搜索,可以将 VECTOR_WEIGHT 调高至 0.7。
AGENTMEMORY_AUTO_COMPRESS 默认为 false,建议保持此设置。活跃会话中的逐条压缩会产生约 3-5 倍于会话末压缩的 token 消耗,且压缩结果的质量并没有显著提升。如果确实需要实时压缩(例如为了在会话中途提供更精确的记忆检索),可以考虑设置 AGENTMEMORY_REFLECT=true,它会在 Stop 钩子触发时异步扫描近期观测并自动更新待办事项,不会阻塞会话。
GRAPH_EXTRACTION_ENABLED 默认为 false。知识图谱的构建和遍历会带来额外的索引时间,尤其在观测数量较多时。对于个人项目或小型团队,GRAPH_EXTRACTION_ENABLED=true 带来的检索精度提升可能不值得等待图谱构建的时间。但对于大型代码库,知识图谱能够捕捉跨文件的依赖关系和实体关联,其收益会超过额外的索引开销。
AGENTMEMORY_INJECT_CONTEXT 默认为 false,但它是真正决定代理是否能感知到记忆的关键开关。开启后,SessionStart 钩子会将约 1-2K 字符的项目上下文注入到会话的第一轮交互中。需要注意的是,Claude Code 将 SessionStart 的标准输出视为调试日志而非上下文注入,如果你的代理不是 Claude Code,则需要查阅对应代理的文档确认它如何处理 SessionStart 钩子的输出。
多代理场景下的记忆共享机制
agentmemory 支持在多个代理之间共享记忆,但共享不是自动发生的 —— 需要显式配置团队 ID 和用户 ID。在 ~/.agentmemory/.env 中设置 TEAM_ID、USER_ID 和 TEAM_MODE=shared 后,记忆的可见性会在私有模式和共享模式之间切换。私有记忆仅对创建者可见,共享记忆则对团队内所有成员可见。
多代理记忆共享的核心挑战不是存储,而是协调。在没有协调机制的情况下,多个代理可能同时编辑同一条记忆,导致版本冲突。agentmemory 通过租约(lease)机制解决这个问题 —— 当一个代理锁定某条记忆进行编辑时,其他代理在该租约到期前无法修改该条目。租约的有效期默认设置为 30 秒,超时后自动释放。如果一个代理在编辑过程中崩溃,租约会在 30 秒后自动失效,不会导致记忆被永久锁定。
信号(signal)机制补充了租约的协调能力。代理可以通过 memory_signal_send 向其他代理发送消息,并通过 memory_signal_read 读取消息和回执。这类似于代理之间的即时通讯,但消息是持久的 —— 与临时消息队列不同,信号被存储在记忆中,可以在代理离线后被读取。信号机制适用于跨代理的工作流协调,例如当代理 A 完成数据库迁移后,通过信号通知代理 B 可以安全地运行集成测试。
资料来源
- GitHub: rohitg00/agentmemory
- Benchmark 报告: benchmark/LONGMEMEVAL.md
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。