GraphRAG 多跳推理实战 2026:为什么基础 RAG 会失败

·阅读约15分钟·Evergreen Tools Team
Knowledge Graph

💡 工具推荐处理图数据与实体 JSON 时,试试 Evergreen Tools 的 JSON格式化工具YAML/JSON转换工具正则测试工具,全部免费!

基础 RAG 在 demo 里完美运行,直到你把它部署上线。核心假设是「语义相似 = 相关」,但用户真正问的多跳问题——「Acme 收购的那家公司的 CEO 是谁?」——需要的是 A 连到 B、B 连到 C 的结构化连接,而这些概念通常不在同一个文本块里。2026 年的答案越来越明确:把知识图谱的刚性结构与向量检索的语义能力结合起来,这就是 GraphRAG。本文用 Python 从零实现完整流程。

1. 基础 RAG 为什么会在多跳问题上翻车

假设你的向量数据库里存着一批企业合同。用户问「谁领导 Acme 收购的那家公司?」——回答藏在 Chunk 2:「Sarah Connor 被任命为 BetaTech 的 CEO」。但向量检索只看语义相似度,它会把查询嵌入后找到与「Acme」和「领导」最相似的块,而 Chunk 2 里的「Sarah Connor」和「BetaTech CEO」跟「Acme」没有任何语义重叠,于是答案块永远不会被召回。基础 RAG 还搞不定全局汇总类问题:当答案需要跨几十个块的信息时,top-k 检索天然就漏。

// Why naive vector search fails on multi-hop questions.
// Query: "Who leads the company that Acme Corp acquired?"
// Chunk 2 holds the answer but never gets retrieved,
// because "Sarah Connor" has no semantic overlap with "Acme".

const query = "Who leads the company that Acme Corp acquired?";

const chunks = [
  { id: 1, text: "Acme Corp acquired BetaTech in 2025 for $2.1B..." },
  { id: 2, text: "Sarah Connor was appointed CEO of BetaTech..." }, // <- answer
  { id: 3, text: "BetaTech builds enterprise scheduling software..." },
];

const topK = await vectorSearch(query, chunks, { k: 2 });
// Returns chunk 1 and chunk 3 — chunk 2 is invisible to
// semantic similarity, yet it holds the answer.
Graph Extraction

2. GraphRAG 的核心思路:先建图,再检索

GraphRAG 在摄取阶段就让 LLM 处理文档:从每个块中提取节点(实体)和边(关系),构建知识图谱。这样「Acme Corp -[ACQUIRED]-> BetaTech」和「Sarah Connor -[LEADS]-> BetaTech」就成了显式的结构化事实。检索时不再只靠向量相似度,而是先向量定位起点,再沿图的边做多跳遍历——每一跳都是确定性的关系查询,而不是模糊的语义猜测。

// GraphRAG ingestion: let the LLM extract nodes (entities)
// and edges (relationships) from each chunk to build a
// knowledge graph. This is the key architectural shift.

import { ChatOpenAI } from "@langchain/openai";

const EXTRACT_PROMPT = `Extract all entities and relationships
from the text as JSON. Use the schema:
{ "nodes": [{"name": string, "type": string}],
  "edges": [{"from": string, "to": string, "rel": string}] }`;

async function extractGraph(text: string) {
  const llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });
  const res = await llm.invoke(EXTRACT_PROMPT + "\n\n" + text);
  return JSON.parse(res.content as string);
}

// Chunk 2 -> { nodes: [{name: "Sarah Connor", type: "person"},
//                       {name: "BetaTech", type: "company"}],
//              edges: [{from: "Sarah Connor", to: "BetaTech",
//                       rel: "LEADS"}] }

3. 实体抽取:让 LLM 当图构建器

摄取管线的核心是一个结构化的抽取提示词:让 LLM 输出 JSON 格式的节点和边。温度设为 0 保证可复现。抽取质量直接决定图的质量,所以提示词要明确实体类型(person/company)和关系类型(ACQUIRED/LEADS)。这一步本质上是把非结构化文本「编译」成结构化知识,后续所有查询都建立在这张图上。

// Store the graph in Neo4j (or any graph DB) alongside
// the vector index. Entities get both a node and an embedding,
// so you can start from either semantic or structural search.

from neo4j import GraphDatabase
import openai

driver = GraphDatabase.driver("bolt://localhost:7687")

def upsert_entity(tx, name, etype, embedding):
    tx.run(
        """MERGE (e:Entity {name: $name})
           SET e.type = $etype, e.embedding = $embedding""",
        name=name, etype=etype, embedding=embedding,
    )

def upsert_relationship(tx, src, dst, rel):
    tx.run(
        """MATCH (a:Entity {name: $src}), (b:Entity {name: $dst})
           MERGE (a)-[:$rel]->(b)""",
        src=src, dst=dst, rel=rel,
    )

# Ingest pipeline: for each chunk -> extractGraph() -> upsert
with driver.session() as s:
    s.execute_write(upsert_entity, "Acme Corp", "company", emb1)
    s.execute_write(upsert_entity, "BetaTech", "company", emb2)
    s.execute_write(upsert_relationship, "Acme Corp", "BetaTech", "ACQUIRED")

4. 存储:图数据库 + 向量索引双轨并行

知识图谱存进 Neo4j(或其他图数据库),实体同时保留向量嵌入。这样你既可以从语义入口进入(向量检索找到起点实体),也可以从结构入口进入(直接按关系遍历)。双轨设计是 GraphRAG 的弹性来源:向量负责「模糊召回」,图负责「精确连接」。

Multi-Hop Retrieval

5. 多跳检索:向量找起点,图走完剩下的路

回答「Acme 收购的公司由谁领导」需要三步:向量检索定位 Acme Corp;沿 ACQUIRED 边走到 BetaTech;再沿 LEADS 边反向找到 Sarah Connor。每一步都是确定性的 Cypher 查询,答案稳定可复现——这正是企业场景最看重的。混合检索器把向量召回和图的 k 跳遍历组合起来,把上下文交给 LLM 生成最终回答。

// Retrieval: first hop via vector search, then traverse
// the graph for the remaining hops. This answers the query
// that defeated naive RAG.

def answer_multihop(query: str) -> str:
    # Hop 1: find "Acme Corp" in the vector index
    seed = vector_search(query, top_k=5)          # -> Acme Corp
    # Hop 2: traverse ACQUIRED edge
    acquired = graph.query(
        "MATCH (a:Entity {name:$name})-[:ACQUIRED]->(t) RETURN t.name",
        name=seed[0],
    )                                            # -> BetaTech
    # Hop 3: traverse LEADS edge
    leader = graph.query(
        "MATCH (c:Entity {name:$name})<-[:LEADS]-(p:Person) RETURN p.name",
        name=acquired[0],
    )                                            # -> Sarah Connor
    return leader[0]

print(answer_multihop("Who leads the company Acme acquired?"))
# Sarah Connor

6. 什么时候该升级到 GraphRAG

如果用户问题集中在单一事实(「合同编号是多少」),基础 RAG 就够了。一旦出现跨实体的关系问题、汇总问题、或者「为什么」类需要推理路径的问题,就该上 GraphRAG。迁移成本不算低——要跑实体抽取、要维护图数据库——但换来的是企业级查询的准确性和可解释性。从一个小型试点域开始,比如只建合同领域的关系图,跑通后再扩展。

// Full pipeline wiring in Python: chunk -> extract ->
// graph + vector -> hybrid retrieval -> LLM answer.

import asyncio
from graphrag import GraphIndex, HybridRetriever

async def main():
    docs = load_contracts("contracts/")          # corporate contracts

    index = GraphIndex(llm="gpt-4o", neo4j_uri="bolt://localhost:7687")
    await index.ingest(docs)                     # extraction + upsert

    retriever = HybridRetriever(index)
    ctx = retriever.retrieve(
        "Who leads the company that Acme Corp acquired?",
        vector_k=5, graph_hops=3,
    )
    answer = await llm_complete(ctx.context_text())
    print(answer)

asyncio.run(main())

📌 常见问题 FAQ

基础 RAG 为什么回答不了多跳问题?

基础 RAG 假设语义相似即相关,但多跳问题需要的连接(A 收购 B、B 的 CEO 是 C)分散在不同 chunk 里,答案块与查询没有语义重叠,向量检索永远召不回它。

GraphRAG 和向量 RAG 的本质区别是什么?

GraphRAG 在摄取阶段用 LLM 抽取实体和关系构建知识图谱,检索时先向量定位起点、再沿图遍历多跳;向量 RAG 只做语义相似度 top-k 召回。

GraphRAG 需要什么基础设施?

一个图数据库(如 Neo4j)+ 向量索引 + LLM 抽取管线。实体同时存节点和嵌入,向量负责模糊召回,图负责精确连接。

GraphRAG 比基础 RAG 慢吗?

摄取阶段更慢(多一次 LLM 抽取),但查询阶段多跳遍历是确定性的图查询,通常很快。总体是拿摄取成本换查询准确性和可解释性。

什么时候不该用 GraphRAG?

当问题都是单一事实查询(如「合同编号是多少」)、数据规模小、或关系结构不明显时,基础 RAG 更简单便宜。GraphRAG 面向跨实体关系和汇总类问题。