向量、图谱还是文件:Agent 记忆怎么选

做 Agent 记忆,最容易走的一条路是:先上向量库,觉得不够再加图谱,最后发现自己在维护一套比业务还复杂的系统。这篇想回答一个很朴素的问题:选型到底该看什么。

一、一个让我重新想这件事的现象

同一个记忆系统,在不同团队的测评里分数可以差出十几个点。Arize 的博客里就列过:Zep 自己报的 LoCoMo 成绩,和 Mem0 论文里给 Zep 的成绩、Mem0 团队重跑的成绩,三者并不一致。数字对不上,说明光看分数很难判断哪种方案更好。

另一件事更直接:Arize 的 Agent 助手 Alyx,一开始做的是向量检索、情景与语义分库、再加时序知识图谱,最后上线的却是每个用户每个空间一份、上限 8000 字符、每次请求整份读入的文本文件。

这两件事放在一起,提醒我先别问「哪种技术更先进」,而是问:我的 Agent 每次回答前,到底需要知道什么?

二、先分清三种东西

  • 文件回答的是「这个用户是谁、有什么约定、手头在做什么」。整份读进上下文,不存在「存在却没取到」,代价是容量有限,而且写什么、怎么改,全靠你自己定规矩。
  • 向量回答的是「历史里哪些内容和这句话相关」。容量几乎不设限,代价是检索质量本身成了要调的东西:取回多少条、用什么重排、阈值多少。
  • 图谱回答的是「当时谁和谁是什么关系、哪条事实取代了哪条」。它擅长关系和时间,代价是写入和建模成本。

这三者并不互斥。LangMem 的概念文档把语义记忆分成两种写法:profile 是一份只存最新状态、原地更新的文档,collection 是无上限、运行时检索的记录集合。也就是说,「文件式」和「检索式」在主流框架里本来就是并列选项。

三、文件式:Arize 的 8000 字符和它踩过的坑

先讲最简单的一种:给每个用户留一份小文件,每次回答前整份读进上下文。Arize 的 Agent Alyx 就是这么做的。

文件长什么样。 每个用户在每个空间下一份,上限 8000 字符,分成固定的六块:用户画像、目标、项目、资产、约定、交互纠正。什么值得写进去,有一条很清楚的规则:要同时满足「下个月还成立」「工具查不回来」「对以后好几次请求都有用」。配置、ID、链接、实时指标这类随时能查到的东西,一律不存。

怎么写进去。 不是 Agent 在聊天中途随手记,而是回答发出去之后,由后台慢慢整理:先总结这一轮,再让模型给出「在某处添加」或「把某句替换成某句」这样的小修改,而不是把整份文件重写一遍。替换要能唯一对上原文,对不上就带着错误信息重试,最多三次。文件超过 7000 字符就压缩到 6000 左右,超过 8000 字符的写入会被直接拒绝。新写入的每一段文字,还要先过一道安全检查:这份记忆会被塞进之后每一次对话,一旦被人塞进恶意指令,就会一直生效。

效果怎么样。 Arize 自己搭了一批多轮对话的测试场景,每个场景跑一次,人工看回答打分,作者说这只能当回归检查,不是正式基准。最后一轮里,需要靠记忆才答得对的 18 个问题答对了 16 个,那些「记忆反而可能带偏答案」的问题则全部答对。

踩过的坑。 这几条比成绩更值得看:

  • 用户说过「数据集正好 70 行」,记忆里只留下一句关于某个数据集的备注,后来一次小修改把它悄悄删了,再也没回来。
  • 事实明明在记忆里,模型回答时却没用上。常驻文件解决了「查不到」,解决不了「看到了不用」。
  • 模型把自己做错的做法,当成「约定」写回了记忆,之后一直照着错的来。
  • 压缩是最容易出问题的一环:早期版本里,48 轮对话有 22 轮触发压缩却没压到目标,其中 6 轮因为文件超限,这一轮新增的内容直接丢了。

什么时候够用。 作者给的边界很克制:范围窄、只服务一个用户、大部分实时信息工具都能查到的 Agent,一个文件就够;记忆量大、多人共用、事实之间关系复杂或随时间变化(比如「3 月份这个账户归谁」)的场景,再考虑图谱。我的理解是:这是范围窄的 Agent 的合理默认起点,不是放之四海而皆准的答案。

同样的思路在常见产品里也能看到:Letta 的记忆块是始终放在上下文里的几个小块,官方说法是「始终可见,无需检索」;Claude Code 的 CLAUDE.md 每次会话整份加载,官方建议控制在 200 行以内,太长会占上下文、也更容易被忽略,而且它只是上下文,不是强制规则,要保证一定执行得用 hook;Anthropic 的记忆工具走另一条路:Claude 向一个 /memories 目录发出读写请求,由你的应用执行,强调的是用到才读,而不是一次全装进去。

四、向量与图谱:贵在写入,难在调参

向量检索和图谱听起来更高级,但代价在别处。

写入贵。 以 Graphiti 为例,每处理一轮对话,要先让大模型做几件固定的事:抽实体、判断是不是同一个实体、抽事实;之后每条事实、每个实体还会各自再调用一次。Arize 估算,一轮对话里有 5 个实体、6 条事实,大约要调用 10 次大模型,用自定义类型会超过 20 次。这笔钱,每个用户的每一轮对话都要花。

调参难。 记忆一旦变成检索,要操心的就变成:能不能找全,嵌入模型选哪个,相似度阈值设多少,会不会把两个名字相近的东西当成同一个。要回答这些,得先有一套标好答案的测试集,这本身就是一个项目。

图谱真正擅长的事。 Zep 的上下文图把事实、关系和变化都放在时间轴上:新信息让旧事实失效时,会在那条关系上记下失效时间,每条事实还能追溯到它来自哪段原始内容。Graphiti 也强调事实有有效期,失效了不删除,只标记。研究方面,CogMem 的论文把「主观陈述」和「客观事实」分成不同节点,保留来源,用固定规则的图操作还原上下文;IBM Research 有一篇 ISWC 2026 的论文,用知识图谱存对话记忆,强调透明、可追溯、答案更稳定,据说已经开源。这两项目前没有可核对的性能数字,所以这里只讲思路。

把这些放在一起看,图谱真正解决的是「某个事实在什么时候、凭什么成立」,而不是「把相关内容找出来」。如果你的 Agent 不用回答这类问题,图谱的好处很可能被写入成本吃掉一大半。

五、我的对照:knowledge-os 的记忆现状

我维护的开源项目 knowledge-os,面向的是业务知识库:文档入库后抽取带证据的 Claim,再和 Chunk、Wiki、图谱配合做带引用的问答。下面的状态,依据是公开仓库里的代码。

已实现。

  • 四种形态并存:Claim 是结构化事实加证据,Chunk 是原文切片加向量,Wiki 是由证据编译出来的 Markdown 页,图谱放实体与关系,并且可以按知识库开关。
  • 问答时 Claim、Wiki、Chunk 三路并行召回,融合重排后做证据核验,再综合作答。
  • 事实有「事实族」和版本,新版本生效后旧事实标记为「已取代」而不是消失,问答支持按 as_of 站在某个时间点回答。这正是上一节说的「事实在什么时候为真」。

半成品。

  • 会话记忆:有一个记忆端口,按会话保存问答片段,每个会话最多保留最近 10 条,超出就删最旧的,有内存版和按知识库隔离的 Postgres 版。内存版的取回方式很朴素:用问句里的词做子串匹配,一条都没命中就取最近 3 条。Postgres 版的取回排序这里不展开。
  • 端口里还留了语义记忆和流程记忆的接口。流程记忆有存取,语义记忆目前看不到写入它的调用,这里不下结论。

有一点要说清楚:knowledge-os 不是 Alyx 那类场景。它的知识是共享的、来自文档的,要给出处、有版本,正好落在 Arize 自己划出的「该用图谱或检索」的那一侧。它的 Wiki 层某种意义上也是「文件」,但那是知识的人读视图,不是某个用户的偏好记忆。

六、选型决策表

条件 倾向 代表做法(形态)
单用户、范围窄、记忆在几千 token 内 常驻文件 Arize Alyx(文件);Claude Code 的 CLAUDE.md(文件);Letta memory blocks(文件式常驻块)
记忆持续增长、整份读不下 文件做核心,检索补长尾 Claude Code auto memory:索引常驻、专题文件按需读(文件);Letta Filesystem(混合:文件加语义检索)
要语义召回大量对话片段 向量检索 Mem0(向量为主);LangGraph store 配嵌入检索(混合)
要回答「某时点什么为真」「谁取代了谁」 带有效期的事实结构 Zep / Graphiti(图)
多用户共享,要按权限过滤、给出处 结构化事实加证据与权限,原文兜底 knowledge-os(混合)
团队没有评测能力 优先选可读、可对比差异的形态 文件式

再单独列一张常见产品的归类表,只挑最常用的 7 个:

产品 记忆形态 适用场景
Claude Code 的 CLAUDE.md 文件(每次整份加载) 项目约定与规则
Anthropic 记忆工具 文件(目录,按需读写) 跨会话保留进度与经验,存储由应用自管
Letta 文件式常驻记忆块;另有文件系统加语义检索的混合方案 用户画像、人设;大量文档与对话历史
LangGraph store 与 LangMem 混合(JSON 文档,可配嵌入做语义检索) 跨会话的长期记忆
Mem0 向量为主,并在记忆之间关联实体 跨会话个性化
Zep / Graphiti 图(带时间的上下文图,检索混合语义、关键词与图遍历) 事实随时间变化、要追溯来源
Arize Alyx 文件(每用户一份,8000 字符上限) 范围窄的单用户 Agent

七、落地:写入规则、回归评测、事故演练

如果我从零给一个 Agent 加记忆,会先做三件事,而不是先选存储。

第一,写下「什么该进、什么不该进」。 照 Arize 的筛选思路,可以把规则写成这样:

css 复制代码
对每条候选信息:
  若 它在下个月仍成立 且 工具查不到 且 会影响多次后续请求:
      写入;用户亲口说的标 [用户],压缩时优先保护
  否则:
      丢弃

第二,评测同时测两个方向。 一类问题必须靠记忆才答得对;另一类问题是记忆可能把答案带偏,比如别的项目的偏好被错用到当前项目。读文件本身也要成为评测的一部分,因为 Arize 那几个坑,都是看最终回答发现不了、看记忆状态才发现的。

第三,演练三种事故。

  • 记忆被投毒:新内容先过检查,检查不了就跳过写入。
  • 压缩丢字段:压缩失败时保留上一份好文件,别写半成品。
  • 并发覆盖:Letta 的文档明说,直接更新块是整体替换,多个进程同时改,最后写入者胜。多副本写同一份记忆,需要串行化。

八、已实现、没做完与以后打算

内容
已公开、可核的 文件式的完整做法与失败案例(Arize);常驻与按需两种加载策略(Claude Code、Letta、Anthropic 记忆工具);图谱的事实有效期与出处机制(Zep、Graphiti);knowledge-os 的四种形态、版本与 as_of、会话记忆端口
没做完、公开资料里没有 文件、向量、图谱在同一任务下的直接对比;CogMem 与 IBM 工作的可核性能数字;各家常驻文件容量上限的实验依据(Arize 是 8000 字符,Letta 示例是 5000,Claude Code 建议 200 行,都是各自选的);图谱记忆在中文场景的公开评测

回到开头的问题。我现在的答案是:先问你的 Agent 每次回答前必须知道什么。如果答案是几千 token 以内的画像和约定,就从一个文件开始,把写入规则和评测做扎实;当评测告诉你文件装不下、查不全,再补检索;只有当你必须回答「某个时点什么为真」「凭什么为真」,再为事实建图谱。技术可以后加,但「该记什么」这件事,越早想清楚越省钱。

项目地址:github.com/liuyanqun08...

相关推荐
乱世军军1 小时前
安装最新版本
人工智能
Mickey同学1 小时前
大模型应用的运行原理:Token、上下文、采样、成本与工程边界
人工智能
tiger从容淡定是人生1 小时前
从 Gemini 的任务示例出发:AI 能否从知识中推导工作流?
java·人工智能·aigc·知识图谱·创业创新·零知识证明
向哆哆1 小时前
外墙裂缝目标检测数据集:6,000+张图像 | 目标检测
人工智能·目标检测·计算机视觉
鶴哥只手遮天1 小时前
第 06 章 SQLite 持久化契约与数据模型
人工智能·开源
suliqiang1 小时前
AI 编程新范式:Vibe、Agentic 与 Spec 实战指南
人工智能·ai编程·ai-native
武子康1 小时前
LingBot-World 能连续生成一小时,为什么回到原地仍可能变样?
人工智能·llm·agent
AIGCmagic社区1 小时前
多模态Jev专题:OmniMed-Jev多标签可靠误差降到原来的1/126
人工智能·aigc·jev模型
鶴哥只手遮天1 小时前
第 16 章 模型与 Provider 抽象层
人工智能·开源