相比向量存储,图谱带来了什么
大多数 Agent 系统从向量存储起步,并且在一段时间内运转良好。文档被切块、嵌入,并按相似度检索。失效模式悄然而至:那些需要遍历而非查找的问题。「触发我们上季度弃用组件审计的事件报告中,提到的是哪家供应商?」这不是一个相似度问题。这是一个 join 问题,而扁平检索恰恰最先在这类问题上退化,因为没有任何单个 chunk 包含答案。答案是一条路径。
图谱让路径显式化。实体是节点,关系是边,多跳问题变成一次可规划、可界定、可解释的遍历。这是图谱带来的第一件事:查询形态与问题形态相匹配。
第二件事是溯源。当一条断言以边的形式存储时,这条边可以携带自己的元数据:它由哪次观测产生、何时产生、由哪个 Agent 产生、处于哪个 schema 版本之下。这意味着 Agent 不仅能说「该组件已弃用」,还能说「该组件已弃用,依据是这次观测,记录于此时」。溯源随边而行,而不是事后从检索分数中重建——检索分数是糟糕的正当性代理。
第三是写入隔离。多个 Agent 可以写入互不相交的子图,而无需重写彼此的 embedding。在向量存储中,每新增一份文档都会改变其他所有文档被检索时所处的邻域结构;新增一个 chunk 可能改变另一个查询的返回结果。在图谱中,向无人读取的区域添加一个节点和一条边,不会触动图谱的其余部分。随着并发写入者数量增长,这一性质愈发重要。
代价是什么
代价不在查询层。属性图和 RDF 的查询引擎已经成熟,在几百万条边上做遍历不是研究难题。代价在于让图谱保持为真。

实体消解变成一项持续运行的后台任务。 同一个组织会以三个标签略有差异的节点出现:一次来自文档,一次来自结构化数据流,一次来自 Agent 的推断。去重不是一次性迁移。每一次新写入都是一次候选合并,每一次合并都是一次候选错误,而这项任务永远不会结束。任务一停,图谱就开始漂移,而除非你专门为此建一个看板,否则漂移在任何仪表盘上都不可见。
Schema 演进是一个迁移问题。 写入自由形式三元组的 Agent 会产出一张没人能查询的图谱,因为谓词彼此近乎同义,类型也不一致。带类型的词表不是官僚主义;它正是让遍历可规划的东西。但带类型的词表必须变化,而变化意味着迁移既有边、对 schema 做版本管理,并决定那些仍期待旧形态的读取方会怎样。
写入冲突需要合并策略,而不是 last-write-wins。 两个 Agent 同时观测同一实体,必然产生分歧。Last-write-wins 会静默丢弃其中一次观测,这对溯源而言是最坏的结果。合并策略必须按谓词决定:取值是单值、集合值还是时间序列;冲突写入是新建一条边、取代旧边,还是触发一个复核任务。这是一个必须在第一次冲突之前做出的设计决策,而不是之后。
诚实的权衡是这样的:图谱通过让一致性变贵,使遍历和溯源变便宜。向量存储通过让遍历和溯源变贵,使一致性变便宜。两者都不免费;它们在不同的地方失效。
一种可行的分工
在实践中站得住脚的模式,是把必须 join 的东西与仅仅需要存储的东西分开。
图谱只保留实体、关系和溯源。 节点携带稳定标识符。边携带来源、时间戳,以及产生它的那次观测的标识符。原始文本不放在图谱里;它放在对象存储中,通过标识符引用。

把 Agent 的写入约束在带类型的词表内。 Agent 不发明谓词。它们针对一个带版本的 schema 提出边,schema 变更走人工复核流程。这听起来会拖慢 Agent,确实如此,而且是刻意的:替代方案是一张谓词与其生成 prompt 一样五花八门的图谱。这里一个有用的机制是写入校验器,在边界处拒绝未知谓词和未知实体类型,让坏写入在摄入时大声失败,而不是在查询时静默失败。
把图谱新鲜度当作带可度量陈旧度上界的 SLO。 如果消解任务滞后,读取方应当知情。把陈旧度上界暴露给读取图谱的 Agent,让读取方能够决定是信任一次遍历,还是回退到原始检索。一张悄悄落后三天的图谱,比一张明说自己落后的图谱更糟。
为并发写入者按谓词给出合并策略。 单值谓词以取代方式解决,旧值作为历史保留。集合值谓词取并集。时间序列谓词追加。任何有歧义的情况都进入复核队列,而不是按到达顺序解决。
该埋哪些点
如果团队正在考虑用共享图谱作为 Agent 上下文,埋点比选哪个数据库更重要。跟踪消解积压量及其年龄。跟踪校验器处被拒写入与接受写入的比例。跟踪每种实体类型的谓词数量,因为这个数字向上爬升是 schema 腐化的先行指标。跟踪读取方因图谱过于陈旧而不可信、回退到原始检索的频率。
这些都不是查询层指标。它们是维护指标,而正是它们预测这张图谱在六个月后是否仍然有用。查询层不会有问题。难点在于 join,而 join 是靠维护出来的,不是靠建出来的。
封面图:Google DeepMind / Pexels。
