GraphRAG 与多跳检索:当 top-k 切片回答不了问题时。
扁平向量 RAG 检索与查询最相似的 k 个切片。对"文档关于 X 说了什么"这正是对的工具,对"整个语料里的主要主题是什么"或"是谁批准了那个搞坏了客户投诉的服务的变更"这正是错的工具。有些问题需要的是聚合或遍历,而不是相似度。GraphRAG 是这些问题的答案——而构建它的成本,正是它应当仅仅是这些问题的答案的理由。
扁平 RAG 结构性地无法服务的两类查询。
Top-k 相似度搜索有一个硬上限,任何重排器或更大的 k 都无法移除,因为失败的不是检索质量——而是这个操作的形状。
- 全局/主题型查询。"这 4,000 份事故报告里占主导的主题是什么?"答案不在任何一个切片里;它是整个语料的一个属性。Top-k 返回与"主题"一词最相似的五个切片,那是噪声。相似度无法聚合。
- 关系型多跳查询。"是哪位工程师评审了那个引入了让值班被呼叫的回归的迁移?"回答这个需要在几乎不共享表面词汇的文档之间串起回归 → 迁移 → 评审者。每一跳的证据住在不同的切片里,没有任何单个切片与整个问题相似。相似度无法遍历。
诊断性问题:答案是包含在一小组段落里,还是关于多少段落彼此相关的一个函数?若是前者,扁平 RAG(见 advanced-rag-architectures)是对的,图是过度工程。若是后者,再多的切片检索也拼不出它——你需要索引当前没有的结构。
GraphRAG:在索引时抽取出的结构。
微软的 GraphRAG(2024 年发布,github.com/microsoft/graphrag)让语料潜在的结构显式化。索引时一个 LLM 读取每个切片并抽取一张实体–关系图;一个社区检测算法(Leiden)把这张图划分成稠密连接实体的嵌套簇;然后 LLM 为每个社区写一份自然语言摘要。语料不再是一袋切片——它是一张图加上一层摘要的摘要的层级。
# graphrag/index_pipeline.py (conceptual) def build_graph_index(chunks): triples = [] for ch in chunks: # LLM call per chunk: the expensive part. triples += llm_extract(ch, schema="(entity, relation, entity)") g = build_graph(triples) # merge co-referent entities communities = leiden(g) # hierarchical clusters summaries = {} for c in communities: # bottom-up: leaf summaries summaries[c.id] = llm_summarize( # roll up into parents entities=c.members, edges=c.internal_edges) return g, communities, summaries # the queryable artefact
查询时则按问题类别分流。本地搜索锚定在查询里点名的实体上,遍历它们的邻域(相关实体、连接关系、各自被抽取自的切片),并从那个聚焦的子图作答——这是多跳关系路径。全局搜索是对社区摘要的 map-reduce:每个相关社区摘要独立产出一个部分答案(map),这些部分被规约成一个语料级响应(reduce)——这是扁平 RAG 根本无法执行的主题聚合路径。
无需预计算图的多跳检索。
一张完整抽取的图不是遍历的唯一方式。更轻的模式是迭代式检索–推理循环:为当前子问题检索,让模型识别还缺什么,从那个缺口生成下一个查询,重复直到链条闭合。这里的"图"是在查询时由推理逐查询临场构建的——索引时什么都不构建。
# multihop/iterative.py def multihop_answer(question, retrieve, llm, max_hops=4): facts, sub_q = [], question for hop in range(max_hops): ctx = retrieve(sub_q, k=5) # flat vector search step = llm.reason(question, facts, ctx) facts += step.new_facts if step.is_sufficient: # chain closed return llm.synthesize(question, facts) sub_q = step.next_query # the next hop return llm.synthesize(question, facts) # best effort
这以零索引时图成本买到了大部分关系收益,代价是更高的查询时延迟与 token 开销(每个问题数次 LLM 往返)。当关系型查询只占流量少数时用它;只有当遍历是主导工作负载时,一张预计算图才挣回它的构建成本。更广义的知识图谱支撑检索——查询你已维护的一张策展 KG,或一张从结构化记录物化出的图——完全绕开抽取,当图已存在时是最便宜的选项。
成本与陈旧化税。
GraphRAG 的威力是在索引时买来的,而账单是真实的。抽取是每个切片一次或多次 LLM 调用,加上每个社区、每个层级的摘要——对大语料这实质上很昂贵,常常比把同样文本嵌入一次贵几个数量级。这个成本不是付一次就忘掉的。
陈旧化是被讨论不足的失效。扁平向量索引接受增量 upsert——新文档、新嵌入,完事。图不能局部更新:一个新文档就可能引入重塑社区边界的实体,进而让那些社区喂养的摘要失效。每天都在变的语料要么持续付重抽取的钱,要么从一张不再匹配源的图上提供答案。多数团队低估的不是构建成本,而是维护成本。
收益只有在一个特定画像下才抵得过这个税:语料大(主题没法略读出来)、关系稠密(实体确实互联)、且相对稳定(重抽取是偶尔的,不是持续的),并服务于真实占比的全局或多跳查询。去掉其中任何一个,经济账就反转了。
何时不该用它,以及决策启发式。
对一批相互独立文档的大多数问答——产品文档、支持知识库、政策 PDF——并不需要图。答案住在一个或少数几个段落里;带重排器的扁平混合检索更快、更便宜、增量轻而易举,而且一样准确。因为听起来有原则就动用图,与 memory-stores 中点名的过早精巧化是同一个错误:图是成本最高的选项,也是最常在其成本被论证之前就被采用的那个。
清晰的启发式:
- 扁平 RAG——答案包含在一小组段落里。默认选项。没有证据不要离开它。
- 迭代多跳(无预计算图)——有些查询需要遍历,但它们是少数,而且语料经常变。在查询时逐问题付成本。
- GraphRAG(预计算图)——全局/主题型或稠密多跳查询是流量的持续占比,语料大且相对稳定,并且你已测得扁平检索在它们上损失了真实准确率。
- 混合——现实的生产答案:把可扁平回答的查询路由到便宜路径,只让主题/关系型的进图或迭代循环。多数值得建图的语料,仍然在不用图的情况下回答了大多数问题。
用一次查询类别审计来决定,而非直觉。采样真实查询,把每个标记为段落本地、多跳或主题型,并按类别测量扁平 RAG 的准确率。若段落本地这一类占主导且得分良好——常见情况——你不需要图。只为可证明失败的类别建图,并让其余一切绕开它路由。