图 RAG 实战

任何需要关联事实的问题,向量 RAG 都会力不从心。

图 RAG 实战
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

问标准向量 RAG 系统一个直接的事实性问题,比如*"我们的差旅政策是什么?",它能完美回答。但如果你问一个多跳问题,比如"我们主要供应商在 X 地区的合规问题,如何影响我们一级产品的交付时间线及相关供应商合同?"*,它就会彻底崩溃。

为什么?因为向量搜索找到的是相似的文本片段,它不理解关系。

如果你构建过检索增强生成(RAG)管线,你一定熟悉这个经典模式:

  • 将文档切成多个块
  • 在向量存储中嵌入它们
  • 检索最接近的片段
  • 让模型作答

对于简单问题,这套流程运转得很好。但任何需要关联事实的问题,向量 RAG 都会力不从心。

向量 RAG 擅长找到正确的文本。图 RAG 更擅长找到正确的事实链。

1、真正的问题:断裂的碎片

想象一下问这样一个问题:"谁在影响 Acme 公司的 CEO?"

普通的向量 RAG 系统可能会检索出:

  • 一个关于 John Doe 是 CEO 的块
  • 一个关于 Acme 公司的块
  • 一个关于 Jane Smith 与该公司有关联的块

这些都是有用的碎片,但它们彼此孤立。系统仍然需要把这些点串起来。一个可靠的答案需要一条清晰的事实链:

  1. John Doe 是 Acme 公司的 CEO。
  2. Jane Smith 是 Acme 公司的董事会成员。
  3. 因此,Jane Smith 通过公司董事会网络与 CEO 产生关联。

向量搜索看到的是零散的片段。图 RAG 看到的是底层的关系。

2、图 RAG 有何不同

图 RAG 将非结构化文本转化为结构化网络。它不是只处理文档块,而是映射实体及其关系。

Feature            | Vector RAG          | Graph RAG
-------------------|---------------------|-------------------------
Storage unit       | Text chunks         | Entities + relationships
Retrieval method   | Semantic similarity | Graph traversal
Best for           | Direct lookup       | Multi-hop reasoning
Context scope      | Local fragment      | Connected network

简单来说:

  • 向量 RAG 回答的是**"哪些文本相似?"**
  • 图 RAG 回答的是**"哪条事实路径可以解释这个问题?"**

3、抽取瓶颈

在深入遍历之前,有必要先正视房间里的大象:构建图非常困难。

遍历和排序算法是确定性的、快速的,但图 RAG 真正的挑战在于,把杂乱无章的非结构化企业文档转化为干净的实体-关系三元组(source, relation, target)。在生产系统中,这第一步依赖于基于 LLM 的抽取管线:解析文本块、识别核心实体、梳理关系动词。

一旦三元组建立起来,图的结构性威力就开始发挥作用。

4、图 RAG 管线如何工作

flowchart LR
    Q[User query] --> E[Entity extraction]
    E --> G[Graph construction]
    G --> T[Traversal + path ranking]
    T --> C[Path context]
    C --> L[LLM answer generation]
    L --> R[Final response]

在查看实现代码之前,我们先拆解图 RAG 查询的概念生命周期:

  1. 抽取: 原始文本被解析为结构化的实体-关系三元组(sourcerelationtarget)。
  2. 构建图: 这些三元组被加载到网络结构中,节点表示实体,边表示关系。
  3. 遍历: 当查询到来时,系统识别种子实体,并在图连接上按定义的深度行走,找出所有可能的关系路径。
  4. 评分与排序: 引擎使用 token 重叠度、实体相关性和连接长度来评估候选路径,过滤噪声,隔离出最相关的事实链。
  5. 上下文化: 胜出的路径被转化为干净、可读的散文,形成高度受约束的提示词上下文。
  6. 生成: LLM 消化结构化的图事实,生成精确、可验证的答案。

5、实现蓝图(POC)

要看到这个概念流程的实际效果,下面是用 Python 和 NetworkX 构建功能核心的方法:

5.1 抽取结构化事实

核心知识结构从示例事实开始,构建一个节点表示实体、边表示关系的图。

import networkx as nx

SAMPLE_TRIPLES = [
    ("John Doe", "is CEO of", "Acme Corp"),
    ("Jane Smith", "sits on board of", "Acme Corp"),
    ("Jane Smith", "mentors", "John Doe"),
]

def build_graph(triples):
    graph = nx.DiGraph()
    for source, relation, target in triples:
        graph.add_node(source)
        graph.add_node(target)
        graph.add_edge(source, target, relation=relation)
    return graph

5.2 寻找候选路径

构建图之后,系统从查询中识别相关实体,并使用无向遍历行走图结构。

def traverse_graph(graph, seeds, depth=2):
    paths = []
    seen = set()
    undirected = graph.to_undirected()

    for seed in seeds:
        for target in graph.nodes:
            if seed == target:
                continue
            for path in nx.all_simple_paths(undirected, source=seed, target=target, cutoff=depth):
                canonical = tuple(path) if tuple(path) <= tuple(reversed(path)) else tuple(reversed(path))
                if canonical in seen:
                    continue
                seen.add(canonical)
                paths.append(path)
    return paths

5.3 为路径评分

并非每条路径权重相同。候选路径使用查询 token 重叠度、实体相关性和关系匹配分数进行排序。

def score_path(graph, path, query):
    query_tokens = set(tokenize(query))
    path_nodes = [node.lower() for node in path]
    path_rels = []

    for i in range(len(path) - 1):
        rel, _ = get_edge_relation(graph, path[i], path[i + 1])
        path_rels.append(rel.lower())

    path_text = " ".join(path_nodes + path_rels)

    overlap_score = len(query_tokens.intersection(set(tokenize(path_text)))) * 10
    node_score = sum(1 for node in path_nodes if any(token in node for token in query_tokens)) * 5
    rel_score = sum(1 for rel in path_rels if any(token in rel for token in query_tokens)) * 8

    length_penalty = max(0, len(path) - 2) * 2
    connection_bonus = sum(len(node.split()) for node in path_nodes)

    return overlap_score + node_score + rel_score + connection_bonus - length_penalty

5.4 把最佳路径转化为上下文

一旦选出最优路径,它就会被转化为干净、无歧义的文本上下文供模型使用。

def path_to_text(graph, path):
    lines = []
    for i in range(len(path) - 1):
        source = path[i]
        target = path[i + 1]
        relation, reversed_edge = get_edge_relation(graph, source, target)
        if reversed_edge:
            lines.append(f"{target} {relation} {source}.")
        else:
            lines.append(f"{source} {relation} {target}.")
    return " ".join(lines)

5.5 用选定的路径询问 LLM

结构化的路径上下文被直接喂入 LLM 提示词,以约束最终回答。

def generate_llm_answer(query, context):
    prompt = (
        "You are a helpful assistant. Use only the graph facts below to answer the query clearly. "
        "Do not introduce any new information. "
        f"If the answer is not directly supported by these facts, say you don't know.\n\n"
        f"Question: {query}\n\n"
        "Graph facts:\n"
        f"{context}\n\n"
        "Answer with a short explanation of the supporting facts:"
    )

    response = client.responses.create(
        model=config["deployment_name"],
        input=prompt,
        max_output_tokens=250,
        temperature=0.1,
    )
    return response.output_text.strip()

5.6 将其暴露为演示 API

一个极简的 FastAPI 后端把查询端点直接接到推理工作流上。

@app.post("/query")
def query_graph_rag(request: QueryRequest):
    query = request.query.strip()
    graph = build_graph(SAMPLE_TRIPLES)
    answer, path, context, ranked_paths = answer_query(query, graph)
    return {
        "query": query,
        "answer": answer,
        "reasoning_path": context,
        "path_nodes": path,
        "ranked_paths": ranked_paths,
    }

6、从原型到生产:规模化扩展

虽然我们概念验证(POC)中使用 NetworkX 和硬编码三元组的内存 Python 脚本非常适合快速原型和机制理解,但将图 RAG 扩展到真实的企业级工作负载,需要一次稳健的架构重构。从玩具图走向处理数百万文档的系统,需要解决关键的基础设施、自动化和运维挑战。

以下是架构设计生产就绪的图 RAG 系统时的核心考量:

6.1 自动化抽取管线(真正的瓶颈)

图 RAG 最困难的部分不是遍历,而是把海量非结构化数据转化为干净、结构化的实体-关系三元组。

  • 超越手工三元组: 生产系统需要自动化管线。该管线通常从文档分块开始,把原始文档拆分成可管理的片段。接着是实体与关系抽取阶段,使用 GPT-4o 或 Claude 3.5 Sonnet 这样的强大 LLM 处理这些块,识别核心实体——包括人物、组织和地点——以及连接它们的关系动词。由于原始抽取往往会产生噪声,去重与规范化步骤至关重要,确保代表同一真实事物的不同文本引用(如"Acme Corp"、"Acme Corporation"和"Acme Inc")被解析为单一唯一节点;这种实体解析策略通常需要基于嵌入的聚类或二次 LLM 遍历。最后,对于大规模企业图,扁平存储不足以支撑高效查询,需要分层索引。现代框架采用各种技术在不同层级汇总图社区,例如 Microsoft 的 GraphRAG 方法,以支持快速回答高层级的全局问题。
  • 编排: 在大规模下管理这种复杂、通常异步的管线,需要健壮的编排工具。Microsoft GraphRAG SDKLangChainLlamaIndex 等框架为这些步骤提供了现成组件,或者你可能需要用 Apache AirflowPrefect 构建自定义工作流。

6.2 持久化企业级图存储

本地内存图在重启后就会消失。要处理大规模数据,必须迁移到专用的图数据库。

  • 技术选型: 根据你的规模和查询模式评估企业级图数据库。
  • 托管解决方案: Azure Cosmos DB for Gremlin / NoSQL 非常适合 Azure 生态内的企业集成,提供完全托管、可扩展的图存储。
  • 开源/商业: Neo4j 凭借丰富的查询语言(Cypher),仍然是深度图分析的行业标准。其他选择包括 Amazon NeptuneTigerGraph
  • 模式演进: 你最初的抽取模式可能不够用。生产系统需要无需完全重建即可演进图模式的策略。
  • 混合存储: 图 RAG 常常与向量 RAG 结合使用。你的架构应该支持图数据库(用于遍历关系)与向量数据库(用于文本块的语义相似度)之间的无缝集成。

6.3 优化检索与延迟管理

NetworkX 遍历的复杂度是 O(N+E),对于需要在亚秒级响应、面对数百万节点的生产 API 来说太慢了。

  • 数据库索引: 确保图数据库在节点标签、属性和关系类型上有合适的索引,以加速种子实体查找和邻接节点展开。
  • 遍历限制: 生产级遍历引擎必须对遍历的深度(跳数)和每个节点的宽度(展开范围)设置硬限制,防止失控查询导致 API 超时。
  • 缓存: 实施激进的缓存策略。缓存常见查询结果、频繁访问的子图,甚至缓存重复问题的 LLM 响应。

6.4 运维化与安全

在生产环境运行 AI 需要严格的运维和安全控制。

  • 访问控制(RBAC): 在企业环境中,用户不应有权访问整个图。你需要一个治理层在图数据库上实施行级安全(RLS),确保用户只能遍历他们有权查看的文档所衍生出的关系。
  • PII 脱敏: 在把原始文本块发送到公有云 LLM 进行三元组抽取之前,确保有健壮的个人身份信息(PII)脱敏机制,防止数据泄露。
  • 可观测性: 你需要对 RAG 管线有完整可见性。这包括记录 LLM 提示词和响应、追踪某个答案所走的精确遍历路径(用于审计),以及监控数据库性能指标。

7、架构总结

下图展示了一个现代、可扩展的图 RAG 架构:自动化抽取、使用混合存储,并以安全的 API 服务形式运行。

Architecture for an enterprise-grade Graph RAG system

7.1 POC 系统设计一览

对于 POC,我们把事情保持得非常简单。

System Design Diagram for POC on Graph RAG

7.2 结果

在完全接线的浏览器 UI 中,概念验证展示:

  • 用户输入的查询
  • 用于生成回答的选定推理路径
  • 遍历引擎考虑过的候选路径排名
  • 基于清晰关系事实的最终模型回答
Screenshot of POC execution

8、何时使用向量 RAG vs 图 RAG

以下情况使用向量 RAG:

  • 问题是直接的事实性问题
  • 答案完全包含在单个段落内
  • 查询速度和实现简单性最重要

以下情况使用图 RAG:

  • 答案依赖于跨独立文档的多跳推理
  • 实体间的关系比表面文本相似度更重要
  • 可审计性、可解释性和显式推理路径是硬性要求

图 RAG 在法律合规、财务审计、医疗诊断和复杂供应链追踪等领域表现出色,这些领域里结构上下文与原始文本同样关键。

9、为什么这很重要

经典 RAG 管线虽然强大,但本质上是文档中心的。

图 RAG 引入了结构化推理能力,让系统能够明确陈述:

  • "这些分散的事实是相互关联的。"
  • "这是最相关的关系路径。"
  • "这条具体的边序列支撑了这个结论。"

这就是单纯检索相似文本与真正检索可解释答案之间的决定性区别。

10、结束语

这个 Graph RAG 演示的完整可运行 POC 在这个仓库中,如果你想在本地运行它或把它作为起点。

在本地运行它需要几样东西:

  • Python 3.10+ 以及 requirements.txt 中的包。
  • 一个兼容 OpenAI 的模型或 Azure OpenAI 部署。设置你的 OPENAI_API_KEY,或者设置 Azure 对应的端点、部署名和密钥。
  • 本项目启动命令:uvicorn web_app:app --reload

原文链接:Graph RAG in Action: Why Standard RAG Fails at Complex Queries (And How Graph RAG Fixes It)

汇智网翻译整理,转载请标明出处