news 2026/7/14 11:59:09

RAG 知识图谱增强:结构化关系 + 非结构化文本的联合检索

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RAG 知识图谱增强:结构化关系 + 非结构化文本的联合检索

RAG 知识图谱增强:结构化关系 + 非结构化文本的联合检索

一、"退货政策"和"退货流程"是两个文档,用户需要同时知道

用户问"买到假货怎么退货",RAG 系统从知识库检索出了《退换货政策》文档,给出了退货条件的回答。但用户接着问"具体怎么操作"——系统又检索了一次,返回了《退货操作流程》文档。两个文档明明高度关联,但向量检索把它们当成了独立的文本块。

传统 RAG 的局限:它只知道文档和问题的语义相似度,不知道文档之间的关系。知识图谱补充了这块——它用结构化的关系(如"退货政策→规定→退货流程")补上了向量检索的"孤立性"。

二、知识图谱增强 RAG 的架构

核心思路:两路召回 → 融合排序 → 送给模型。

flowchart TD Q[用户问题] --> E[查询向量化] E --> V[向量检索: 语义相似] E --> NER[实体识别: 提取关键实体] V --> VK[Top-K 文档片段] NER --> KG[知识图谱查询] KG --> KG1[实体关系: 1-hop 邻居] KG --> KG2[关联文档: 关系指向的文档] KG1 --> KG3[图上下文: 结构化关系描述] KG2 --> KG4[关联文本: 图谱节点绑定的文本] VK --> Merge[多路融合排序] KG3 --> Merge KG4 --> Merge Merge --> Context[拼接上下文] Context --> LLM[模型推理] LLM --> R[回答]

关键设计:知识图谱不只是词网络的图,而是实体-关系-实体的三元组集合。比如:(退货政策, 引用_法规, 消费者权益法)(退货流程, 前置步骤, 退货政策)。这些关系帮助模型理解跨文档的信息组织方式。

三、Python 实现:图谱增强检索

import json from dataclasses import dataclass, field from typing import Optional from collections import defaultdict import numpy as np # ========== 知识图谱数据结构 ========== @dataclass class Entity: """知识图谱实体""" id: str name: str entity_type: str # policy, process, product, service properties: dict = field(default_factory=dict) @dataclass class Relation: """知识图谱关系(三元组)""" subject_id: str # 头实体 predicate: str # 关系类型 object_id: str # 尾实体 weight: float = 1.0 @dataclass class Document: """文档片段""" id: str content: str entity_ids: list[str] = field(default_factory=list) # 绑定的实体 # ========== 知识图谱 ========== class KnowledgeGraph: """知识图谱存储与查询""" def __init__(self): self.entities: dict[str, Entity] = {} self.relations: list[Relation] = [] # 索引:实体 → 关联文档 self.entity_docs: dict[str, list[str]] = defaultdict(list) # 索引:文档 → 实体 self.doc_entities: dict[str, list[str]] = defaultdict(list) def add_entity(self, entity: Entity): self.entities[entity.id] = entity def add_relation(self, relation: Relation): self.relations.append(relation) def bind_document(self, entity_id: str, doc_id: str): """将文档绑定到实体""" self.entity_docs[entity_id].append(doc_id) self.doc_entities[doc_id].append(entity_id) def get_neighbors(self, entity_id: str, hop: int = 1) -> list[dict]: """获取实体的 N 跳邻居""" neighbors = [] # 1-hop:直接关系 for rel in self.relations: if rel.subject_id == entity_id: obj = self.entities.get(rel.object_id) if obj: neighbors.append({ "entity": obj, "relation": rel.predicate, "hop": 1, "direction": "out", }) elif rel.object_id == entity_id: subj = self.entities.get(rel.subject_id) if subj: neighbors.append({ "entity": subj, "relation": f"被{rel.predicate}", "hop": 1, "direction": "in", }) return neighbors def get_entity_by_name(self, name: str) -> Optional[Entity]: """按名称查找实体""" for entity in self.entities.values(): if entity.name == name: return entity return None # ========== 图谱增强检索器 ========== class GraphEnhancedRetriever: """知识图谱增强的检索器""" def __init__(self, kg: KnowledgeGraph, documents: list[Document]): self.kg = kg self.documents = {d.id: d for d in documents} # 简化的向量模拟(实际项目用向量数据库) self.doc_vectors: dict[str, np.ndarray] = {} def retrieve(self, query: str, top_k: int = 5) -> list[dict]: """ 融合检索:向量相似 + 图谱关系 返回增强后的文档列表,每条包含来源标注。 """ results = [] seen_doc_ids = set() # === 第 1 路:向量语义检索 === vector_docs = self._vector_search(query, top_k) for doc_id, score in vector_docs: if doc_id not in seen_doc_ids: results.append({ "doc_id": doc_id, "content": self.documents[doc_id].content, "score": score, "source": "vector", "relation": "语义相似", }) seen_doc_ids.add(doc_id) # === 第 2 路:知识图谱实体检索 === # 从 query 中识别实体 query_entities = self._extract_entities(query) for entity in query_entities: # 获取实体的关联文档 for doc_id in self.kg.entity_docs.get(entity.id, []): if doc_id not in seen_doc_ids: doc = self.documents[doc_id] results.append({ "doc_id": doc_id, "content": doc.content, "score": 0.8, # 图谱来源的基础分 "source": "kg", "relation": f"实体关联: {entity.name}", }) seen_doc_ids.add(doc_id) # 获取邻居实体的文档 neighbors = self.kg.get_neighbors(entity.id, hop=1) for neighbor in neighbors: for doc_id in self.kg.entity_docs.get(neighbor["entity"].id, []): if doc_id not in seen_doc_ids: doc = self.documents[doc_id] results.append({ "doc_id": doc_id, "content": doc.content, "score": 0.7, "source": "kg", "relation": ( f"{entity.name}" f"→{neighbor['relation']}" f"→{neighbor['entity'].name}" ), }) seen_doc_ids.add(doc_id) # === 第 3 路:排序 === results.sort(key=lambda x: x["score"], reverse=True) return results[:top_k] def _vector_search(self, query: str, top_k: int) -> list[tuple[str, float]]: """模拟向量检索(实际用向量数据库)""" # 简化:返回所有文档按名称匹配度排序 results = [] for doc_id, doc in self.documents.items(): overlap = len(set(query) & set(doc.content)) / max(len(query), 1) results.append((doc_id, overlap)) results.sort(key=lambda x: x[1], reverse=True) return results[:top_k] def _extract_entities(self, query: str) -> list[Entity]: """实体识别(简化版:字符串匹配)""" entities = [] for entity in self.kg.entities.values(): if entity.name in query: entities.append(entity) return entities def build_graph_context(self, entity: Entity) -> str: """构建图谱上下文:实体关系描述""" neighbors = self.kg.get_neighbors(entity.id, hop=1) if not neighbors: return "" lines = [f"关于「{entity.name}」,以下信息可能有帮助:"] for n in neighbors[:5]: # 限制数量 lines.append( f"- {entity.name} {n['relation']} 「{n['entity'].name}」" ) return "\n".join(lines) # ========== 使用示例 ========== def demo(): # 1. 构建知识图谱 kg = KnowledgeGraph() policy_entity = Entity("e1", "退货政策", "policy") process_entity = Entity("e2", "退货流程", "process") law_entity = Entity("e3", "消费者权益保护法", "law") kg.add_entity(policy_entity) kg.add_entity(process_entity) kg.add_entity(law_entity) kg.add_relation(Relation("e1", "引用_法规", "e3")) kg.add_relation(Relation("e2", "前置步骤", "e1")) # 2. 文档绑定 doc1 = Document("d1", "退货政策:购买后7天内可无理由退货...", ["e1"]) doc2 = Document("d2", "退货流程:1.提交申请 2.等待审核 3.寄回商品...", ["e2"]) doc3 = Document("d3", "消费者权益保护法第25条...", ["e3"]) kg.bind_document("e1", "d1") kg.bind_document("e2", "d2") kg.bind_document("e3", "d3") # 3. 检索 retriever = GraphEnhancedRetriever(kg, [doc1, doc2, doc3]) query = "买到假货怎么退货、需要走什么流程" results = retriever.retrieve(query, top_k=5) print(f"查询: {query}\n") print("检索结果:") for i, r in enumerate(results, 1): print(f" {i}. [{r['source']}] {r['relation']}") print(f" {r['content'][:60]}...\n") if __name__ == "__main__": demo()

四、知识图谱增强的边界与权衡

图谱建设需要额外投入。向量检索可以自动搞定,但知识图谱需要人工或半自动化构建。对于知识体量小(< 100 个文档)的场景,图谱的收益可能抵不上构建成本。当文档数量上千且关系复杂时,图谱才真正体现价值。

实体识别的准确率影响检索质量。如果 NER 把"苹果"识别为水果而非科技公司,图谱召回就偏了。实体识别模型需要根据领域做微调。

两路融合的权重需要调优。纯向量检索的结果(语义相似)和图谱召回的结果(结构关联)如何加权?建议从 0.6:0.4 开始,根据用户反馈(点赞/踩)动态调整。

图谱需要持续更新。知识库更新时,图谱也需要同步更新实体和关系。如果更新不同步,图谱召回的文档可能指向已过期的知识。

五、总结

知识图谱增强 RAG 的核心是"补上向量检索看不到的结构关系"。实施路径:先用向量检索跑起来 → 分析高频查询中"检索到了但不完整"的案例 → 针对性构建图谱关系 → 两路融合排序。不要一上来就建万级别的全量图谱——从 20-50 个核心实体开始,验证图召回对回答质量的提升效果后再扩大。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 11:58:14

PCB电感磁场干扰的布局隔离与走线规范

PCB电源EMC整改中&#xff0c;电感磁场辐射干扰是最核心、最顽固的问题点。很多工程师花费大量精力优化电容滤波、增加屏蔽器件&#xff0c;却忽略了功率电感作为核心磁场辐射源的布局问题。电感工作时产生的交变磁场会通过空间耦合、走线耦合干扰板上模拟信号、高速数字信号&a…

作者头像 李华
网站建设 2026/7/14 11:56:53

2026新手零基础课程视频总结制作避坑指南 包教包会可直接上手

2026年零基础新手做课程视频总结&#xff0c;跟着「导音频转写→抓核心要点→工具辅助记忆自测」走就能直接上手&#xff0c;最要避开的坑就是手动逐帧逐字转录。这个方法适合刚上新岗位需要快速掌握岗位知识的零基础职场新人&#xff0c;前提是你已经有课程原视频或者现场培训…

作者头像 李华
网站建设 2026/7/14 11:55:52

LDO并联设计实战:提升电流与降低噪声的工程方案

1. 项目概述&#xff1a;为什么我们需要并联LDO&#xff1f;在电源设计的江湖里&#xff0c;低压差线性稳压器&#xff08;LDO&#xff09;一直扮演着“净水器”的角色。它的核心任务&#xff0c;就是从可能带有纹波和噪声的输入电源中&#xff0c;“过滤”出极其纯净、稳定的直…

作者头像 李华
网站建设 2026/7/14 11:53:41

如何快速掌握Ren‘Py脚本反编译:开源工具unrpyc的完整实战指南

如何快速掌握RenPy脚本反编译&#xff1a;开源工具unrpyc的完整实战指南 【免费下载链接】unrpyc A renpy script decompiler 项目地址: https://gitcode.com/gh_mirrors/un/unrpyc unrpyc是一个专门用于反编译RenPy游戏引擎编译脚本(.rpyc文件)的开源工具。如果你曾经面…

作者头像 李华
网站建设 2026/7/14 11:53:22

DC-DC转换电路实战:从原理到PCB布局的降噪与滤波设计

1. DC-DC转换器的噪声从哪来&#xff1f;当你第一次听到DC-DC转换器发出"滋滋"的高频噪声时&#xff0c;可能会误以为是电路接触不良。但实际这是开关器件快速切换时产生的电磁干扰&#xff08;EMI&#xff09;。我在设计射频模块时就遇到过这种情况——明明电源输出…

作者头像 李华
网站建设 2026/7/14 11:53:14

ChatGPT用户手册生成终极 checklist(2024 Q2更新):涵盖17个合规项、8类无障碍访问标准、6种多模态交互支持方案

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;ChatGPT用户手册生成的定位与核心价值 ChatGPT用户手册生成并非通用文档自动化工具&#xff0c;而是面向开发者、产品团队与技术支持人员的垂直场景化解决方案。其核心定位在于将大语言模型的能力精准锚定在“…

作者头像 李华