> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 图 RAG 实战
- URL: https://www.hubwiz.com/blog/graph-rag-in-action/
- Published: 2026-08-12T05:35:45.000Z
- Updated: 2026-10-13T15:47:20.000Z
- Description: 任何需要关联事实的问题，向量 RAG 都会力不从心。
- Author: admin
- Tags: RAG, #Import 2026-10-14 14:41

问标准向量 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. **抽取：** 原始文本被解析为结构化的实体-关系三元组（`source`、`relation`、`target`）。
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 SDK**、**LangChain** 或 **LlamaIndex** 等框架为这些步骤提供了现成组件，或者你可能需要用 **Apache Airflow** 或 **Prefect** 构建自定义工作流。

### 6.2 持久化企业级图存储

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

- **技术选型：** 根据你的规模和查询模式评估企业级图数据库。
- **托管解决方案：** **Azure Cosmos DB for Gremlin / NoSQL** 非常适合 Azure 生态内的企业集成，提供完全托管、可扩展的图存储。
- **开源/商业：** **Neo4j** 凭借丰富的查询语言（Cypher），仍然是深度图分析的行业标准。其他选择包括 **Amazon Neptune** 和 **TigerGraph**。
- **模式演进：** 你最初的抽取模式可能不够用。生产系统需要无需完全重建即可演进图模式的策略。
- **混合存储：** 图 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](https://www.hubwiz.com/blog/content/images/2026/08/1-DPhgv164Dba3YAt3ebJCoQ.png)

### 7.1 POC 系统设计一览

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

![System Design Diagram for POC on Graph RAG](https://www.hubwiz.com/blog/content/images/2026/08/1-cfb7E51Eu1FqQUEM7owrAg.png)

### 7.2 结果

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

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

![Screenshot of POC execution](https://www.hubwiz.com/blog/content/images/2026/08/1-b1SsSqqHyMcquZJ2wjr7YA.png)

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

**以下情况使用向量 RAG：**

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

**以下情况使用图 RAG：**

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

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

## 9、为什么这很重要

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

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

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

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

## 10、结束语

这个 Graph RAG 演示的完整可运行 POC 在[这个仓库](https://github.com/KishanChoudhury/GraphRag?ref=hubwiz.com)中，如果你想在本地运行它或把它作为起点。

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

- 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)](https://medium.com/towards-artificial-intelligence/graph-rag-in-action-why-standard-rag-fails-at-complex-queries-and-how-graph-rag-fixes-it-8e81aec03ffd?ref=hubwiz.com)

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

> **这是「RAG / 数据 / 文档智能」专题的一部分。**完整地图见：[RAG / 数据 / 文档智能完全指南](https://www.hubwiz.com/blog/rag-data-stack/)