当前位置: 首页 > news >正文

超越向量检索:用 Graph RAG 构建具备推理能力的企业知识问答系统

当 RAG 遇到知识图谱,LLM 终于能像人类一样“联想起义”而非“关键词匹配”。

引言

传统的 RAG(Retrieval-Augmented Generation)已经成为 LLM 应用的标准范式——用户提问,系统从向量数据库中检索相关片段,再丢给 LLM 生成答案。然而,这种方案隐藏着一个致命缺陷:它丢失了信息之间的关联结构。对于一个企业来说,文档中的人物、产品、日期、事件之间存在着复杂的图状关系,而向量相似度搜索只能找到“词面相近”的孤立段落,无法回答“A 公司与 B 公司有什么间接合作?”这类需要多跳推理的问题。

这就是Graph RAG登场的理由。微软研究院于 2024 年发布的 Graph RAG 方案,通过构建知识图谱代替纯向量索引,让 LLM 在回答问题前先理解实体与关系网络,然后沿着图路径推理。效果有多惊人?在需要多源信息整合的复杂 QA 任务上,Graph RAG 的答案完整性比传统 RAG 提高了 70% 以上。

本文将从零实现一个轻量级 Graph RAG 系统。你将学会:

  • 如何从任意文本集合中自动抽取实体和关系

  • 如何利用图算法(社区发现、最短路径)辅助检索

  • 如何生成带有推理路径的可解释答案

全部代码基于LangChain + NetworkX + OpenAI,无需昂贵的图数据库,一台笔记本即可运行。


第一步:Graph RAG 核心原理(五分钟速通)

传统 RAG 的工作流是:Query → Embedding → 向量相似度 top-k → 拼接上下文 → LLM 生成

Graph RAG 的工作流是:
Query → 实体链接 → 子图提取 → 图算法(社区/路径) → 结构化上下文 → LLM 生成

中间多出的两步——实体识别和图遍历——正是它强大的来源。例如对于问题“诺基亚的竞争对手后来收购了哪家 AI 芯片公司?”,传统 RAG 可能分别返回“诺基亚竞争对手”段落和“AI 芯片公司收购”段落,但无法建立跨段落的逻辑链。Graph RAG 会先在图中找到“诺基亚”实体,沿“竞争对手”边找到“爱立信”,再沿“收购”边找到“Graphcore”,最终给出精确答案。

我们的实现将包含三个核心模块:

  1. 图谱构建器:用 LLM 抽取实体和关系,存入 NetworkX 有向图

  2. 混合检索器:结合向量相似度和图遍历(Personalized PageRank / 最短路径)

  3. 推理生成器:将检索到的子图序列化为文本,交给 LLM 生成最终答案


第二步:环境准备与数据

创建新项目并安装依赖:

bash

pip install langchain langchain-openai networkx matplotlib tiktoken

我们使用一段假想的科技公司并购新闻作为测试语料,你也可以换成任意中文文档。

python

# data.py documents = [ """ 2023年6月,微软宣布收购了AI基础设施公司Volterra AI。 Volterra AI 此前曾与英伟达在GPU云服务领域有深度合作。 微软的竞争对手包括谷歌和亚马逊。 """, """ 谷歌在2024年初投资了AI芯片初创公司Cortex Labs。 Cortex Labs 的创始人曾来自英伟达的GPU设计团队。 同时,亚马逊也在积极布局AI芯片,其Trainium芯片直接对标英伟达的产品。 """, """ 英伟达与微软保持着战略合作关系,微软的Azure云服务大量采购英伟达的H100 GPU。 而谷歌则与AMD合作开发自己的AI加速器。 """ ]

第三步:用 LLM 自动抽取实体与关系

我们需要设计一个提示词,让 LLM 从每个文档中输出 JSON 格式的实体和关系。为了降低成本,可以使用gpt-3.5-turbo

创建graph_builder.py

python

import json import networkx as nx from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) EXTRACTION_PROMPT = """ 你是一个知识图谱抽取专家。从以下文本中识别出所有实体及其类型(如:公司、人物、产品、技术),以及实体之间的有向关系。 返回格式必须为 JSON,结构如下: { "entities": [{"name": "实体名", "type": "类型"}], "relations": [{"source": "实体名", "target": "实体名", "relation": "关系描述"}] } 只返回 JSON,不要有其他文字。 文本: {text} """ def extract_graph_from_text(text: str) -> tuple[list, list]: """调用 LLM 抽取实体和关系""" prompt = EXTRACTION_PROMPT.format(text=text) response = llm.invoke([HumanMessage(content=prompt)]) content = response.content.strip() # 去除可能的 markdown 代码块标记 if content.startswith("```json"): content = content[7:] if content.endswith("```"): content = content[:-3] data = json.loads(content) return data.get("entities", []), data.get("relations", [])

接下来,把抽取出的数据构建成一个全局 NetworkX 图:

python

def build_graph(documents: list[str]) -> nx.DiGraph: graph = nx.DiGraph() for doc in documents: entities, relations = extract_graph_from_text(doc) for ent in entities: graph.add_node(ent["name"], type=ent.get("type", "unknown")) for rel in relations: graph.add_edge(rel["source"], rel["target"], relation=rel["relation"]) return graph # 测试 if __name__ == "__main__": from data import documents G = build_graph(documents) print(f"节点数: {G.number_of_nodes()}") print(f"边数: {G.number_of_edges()}") for node in G.nodes(data=True): print(node)

运行后,你会看到类似('微软', {'type': '公司'})('英伟达', {'type': '公司'}),以及边('微软', 'Volterra AI', {'relation': '收购'})等。


第四步:图增强的检索器

当我们收到用户查询时,需要执行两个并行的检索路径:

  1. 向量检索:使用传统嵌入,找到语义相似的文档片段。

  2. 图检索:从查询中提取实体,然后在图上进行 Personalized PageRank 或 BFS,找出与这些实体高度相关的其他实体和关系。

为了简化,我们只实现图检索部分,并最终将检索到的子图序列化为文本。

首先,从查询中识别实体(也可以用 LLM 做 NER,这里用简单的关键词匹配示例):

python

def extract_entities_from_query(query: str, graph: nx.DiGraph) -> list[str]: """从查询中提取出现在图中的实体名""" entities_in_graph = set(graph.nodes) words = query.lower().split() # 简单的包含匹配(生产环境建议用 NLP 工具) found = [node for node in entities_in_graph if node.lower() in query.lower()] return found

然后,提取子图:对于每个找到的实体,获取其 k 步邻居(这里取 2 跳),合并子图:

python

def retrieve_subgraph(graph: nx.DiGraph, seed_entities: list[str], hops: int = 2) -> nx.DiGraph: """返回包含种子实体及其 hops 步邻居的子图""" nodes_to_include = set(seed_entities) for node in seed_entities: # 向前走 hops 步 for _ in range(hops): new_nodes = set() for n in nodes_to_include: new_nodes.update(graph.successors(n)) new_nodes.update(graph.predecessors(n)) nodes_to_include.update(new_nodes) return graph.subgraph(nodes_to_include).copy()

最后,将子图转换成 LLM 友好的文本格式:

python

def subgraph_to_text(subgraph: nx.DiGraph) -> str: lines = [] lines.append("知识图谱三元组:") for u, v, data in subgraph.edges(data=True): lines.append(f"({u}) -[{data.get('relation', '关联')}]-> ({v})") # 附上节点属性 for node, attr in subgraph.nodes(data=True): lines.append(f"实体: {node} (类型: {attr.get('type', '未知')})") return "\n".join(lines)

第五步:组合 Graph RAG 回答管道

现在我们组装最终的回答流程。简单起见,我们不接入向量检索,只演示纯图检索 + LLM 生成。

创建graph_rag.py

python

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, SystemMessage from graph_builder import build_graph from data import documents import networkx as nx llm = ChatOpenAI(model="gpt-4o", temperature=0) # 构建全局知识图谱 G = build_graph(documents) def graph_rag_answer(query: str) -> str: # 1. 提取查询中的实体 seed_entities = extract_entities_from_query(query, G) if not seed_entities: return "无法从问题中识别出已知实体,请提供更多上下文。" # 2. 检索子图 subgraph = retrieve_subgraph(G, seed_entities, hops=2) if subgraph.number_of_nodes() == 0: return "未找到相关图谱信息。" # 3. 转换为文本上下文 context = subgraph_to_text(subgraph) # 4. 生成答案 system_prompt = """你是一个知识问答助手。请基于提供的知识图谱信息回答问题。 如果图谱信息不足以回答,请明确说明。尽量引用图谱中的关系链来解释你的推理过程。""" user_prompt = f"""知识图谱信息: {context} 问题:{query} 请给出准确、简洁的答案。""" response = llm.invoke([ SystemMessage(content=system_prompt), HumanMessage(content=user_prompt) ]) return response.content if __name__ == "__main__": questions = [ "微软收购了哪家公司?", "英伟达与哪些公司有合作?", "谷歌的AI芯片合作伙伴是谁?" ] for q in questions: print(f"问题:{q}") print(f"答案:{graph_rag_answer(q)}\n")

运行脚本,你会看到类似这样的输出:

text

问题:微软收购了哪家公司? 答案:根据知识图谱,微软收购了 Volterra AI。 问题:英伟达与哪些公司有合作? 答案:英伟达与微软有战略合作关系(微软的Azure采购英伟达H100 GPU);此外,英伟达还与Volterra AI在GPU云服务领域有过合作。 问题:谷歌的AI芯片合作伙伴是谁? 答案:谷歌与AMD合作开发自己的AI加速器。

神奇的事情发生了:第三个问题中,原始语料并没有直接出现“谷歌的AI芯片合作伙伴”这个短语,但图谱里存在谷歌 -[合作]-> AMD的关系(从“谷歌则与AMD合作开发自己的AI加速器”中抽取得到),因此系统能够正确回答。


第六步:进阶优化 —— 社区检测与多跳推理

上面的基础版本已经能处理单跳关系。但对于复杂问题“哪些公司在AI芯片领域既与英伟达合作又与英伟达竞争?”,我们需要引入图社区检测实体重要性排序

我们可以利用networkx.community.louvain找出模块,然后对每个社区内的实体进行加权检索。另一项关键技术是最短路径推理:给定查询中的两个实体,找出它们之间的多条路径,并让 LLM 沿着这些路径归纳答案。

下面给出一个最短路径辅助检索的示例:

python

def find_paths_between_entities(graph, entity_a, entity_b, max_length=3): """返回两个实体之间所有长度≤max_length的简单路径""" paths = list(nx.all_simple_paths(graph, source=entity_a, target=entity_b, cutoff=max_length)) return paths # 在 graph_rag_answer 中集成路径推理 def graph_rag_answer_with_paths(query: str): # 假设我们已经用 LLM 从 query 中抽取出两个关键实体 e1, e2 # 这里简化为手动或规则 entities = extract_entities_from_query(query, G) if len(entities) >= 2: paths = find_paths_between_entities(G, entities[0], entities[1], max_length=3) if paths: path_text = "\n".join([f"路径: {' -> '.join(p)}" for p in paths]) return llm.invoke([HumanMessage(content=f"基于以下关系路径回答问题:{path_text}\n问题:{query}")]).content # 回退到子图模式 return graph_rag_answer(query)

第七步:性能评估与工程化考量

离线评估指标:对于有标准答案的测试集,可以计算答案的 Hit@k 或 BERTScore。通常 Graph RAG 在 Multi-hop QA(如 WebQSP、MetaQA)上的表现显著优于 vanilla RAG。

实时性优化

  • 使用更便宜的抽取模型(如gpt-3.5-turbo-16k)批量构建图谱,可以离线完成。

  • 在线查询时,避免每次都执行 LLM 实体抽取;用现成的 NER 模型(如 spaCy)或基于规则的匹配。

  • 子图检索结果可以缓存(相同实体组合的查询复用子图)。

扩展性:当文档达到百万级别时,NetworkX 内存受限。此时应当迁移到图数据库(如 Neo4j)或使用kuzu嵌入式图引擎。检索算法也需改为更高效的索引结构。


总结

我们在不到 150 行核心代码中,实现了一个能够理解实体关系、进行多跳推理的 Graph RAG 系统。相比传统 RAG,它的优势不仅在于准确率,更在于可解释性——你可以向用户展示图谱路径,告诉答案的来源链条。

Graph RAG 已经在金融风控、医药研发、企业内部知识库等领域展现出巨大的潜力。未来,我们可以结合向量检索和图检索做混合排序,也可以让 LLM 自主决定在图上行走的步数和方向(Agentic Graph RAG)。

技术迭代从未停止,但用结构化的知识增强生成这一理念,会持续存在很久。

*所有的代码都可以直接复制运行,如果遇到 API 配额问题,可改用本地模型(如 Ollama + Llama 3)。欢迎在评论区探讨你的 Graph RAG 落地经验。*

互动问题:你认为在图构建过程中,如何解决实体冲突(例如“苹果公司”与“苹果水果”)?有什么好的消歧策略?分享你的思路。

http://www.jsqmd.com/news/886649/

相关文章:

  • 2026年家居定制观察:木饰面隐形门护墙板工艺解析 - 产品测评官
  • 历史建筑隐形门铃系统设计:物联网与智能交互的工程实践
  • 大模型开发中format_messages、invoke、format三种方法的对比
  • 搜维尔科技:Xsens动作捕捉在人形机器人研发中的应用
  • 【会议征稿通知 | 绵阳师范学院主办 | IET出版 | EI 、Scopus稳定检索】第五届电力工程与电气技术学术会议(ICPEET 2026)
  • 2026年老面小笼包面粉出数高选哪家:出品率与耐发酵对比 - 科技焦点
  • Awoo Installer:终极Nintendo Switch游戏安装解决方案
  • 基于扩散模型的电网故障智能生成:从N-1筛选到主动风险预测
  • 官方发布 | 2025年5月份西宁旅游市场经营主体(企业)红黑榜 - 寻茫精选
  • 基于GMR传感器的DIY示波器电流钳探头设计与实现
  • 荣耀出征官网下载:1.03H经典副本复刻,高阶装备稳定掉落
  • 2026年一键生成论文工具对比实测:5款神器从选题到格式全流程护航
  • DeepSeek技术债务爆发前夜:7个被忽视的代码腐化信号与紧急止损方案
  • 告别Linux依赖!Windows下用CloudCompare和MATLAB查看PCD点云的保姆级教程
  • DMA使用心得-STM32
  • 暗黑破坏神2存档修改器:5分钟掌握Diablo Edit2终极指南
  • eqMac开源工具功能对比与技术选择指南:技术解析与决策框架
  • 打不开JupyterLab
  • 35岁那年我考下AI认证,职业反而越走越宽
  • 论坛水友查看树节点插件cc-inspector
  • YOLOv11卫生间卫浴设备目标检测数据集-2978张-washroom-1
  • 暗黑破坏神2存档修改器:Diablo Edit2让你的游戏体验随心所欲
  • ai-agent框架spring ai alibaba (三)外部调用II-1 MCP
  • P.4文本统计工具
  • HDI 高密度互连板阶数的深度理解
  • 运维必看:CentOS7开机全链路分析+root密码/引导故障急救方案
  • 构建高安全本地智能家居:基于MQTT over TLS与双向认证的实践
  • 2026年老面小笼包面粉怎么挑?五大品牌发酵力与出品表现横评 - 科技焦点
  • 黑盒模型数据最小化合规审计:对抗性攻击视角下的隐私风险度量
  • 炉石传说脚本终极指南:智能自动对战助手完整教程