ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

知识图谱增强RAG:解决企业知识库问答召回质量问题的实战指南

知识图谱增强RAG:解决企业知识库问答召回质量问题的实战指南

之前在做内部知识库问答系统时,我用通用大模型直接答,效果很一般;改成RAG架构之后,问题变成“检索质量决定了生成质量”。你召回的是错误片段,大模型写得再流畅也是错的。试过不少表面工程之后,真正把效果稳住的是用知识图谱增强RAG知识库应用。这篇文章就把这套思路、落地步骤和踩过的坑完整拆给你。

如果你正在做企业知识库问答、智能客服、文档问答,或者只是被RAG的召回质量折磨过,这篇文章应该能帮你找到一条具体可走的路。

1. 为什么RAG需要知识图谱来补位

1.1 向量检索最大的痛点是“语义相似,事实错了”

先说一个很典型的现象。底层知识库里有一份运维手册,上面写着:

  • 张三负责A模块
  • 李四负责B模块

RAG会把手册按固定大小切成长度不等的chunk,再把每个chunk编码成向量。当你问“李四负责哪个模块”时,向量检索大概率能命中正确片段,因为提问里的“李四”和“负责”都在同一段文本中。

但你换成“A模块的负责人是谁”,行为就开始不稳了。A模块可能出现在前言、架构说明、排障手册、接口文档等多个位置,向量模型会同时召回到多块包含“A模块”与“负责人”相关词的内容。如果其中某一块写了“A模块的负责人曾经是王五,后来交接给李四”,你就需要靠排序把真正的答案顶上去。可大多数情况下,排序模型并没有把这层“人事关系”理解得足够深。

我见过最夸张的情况是,一份文档里同时有“默认主节点”“从节点”“仲裁节点”的配置说明,用户问“哪个节点负责仲裁”,向量检索把“节点”相关的段落全召回了,大模型最后把主节点和仲裁节点混成一个概念。这种错,不是微调提示词能解决的,因为根子在“关系”上。

1.2 知识图谱补的是“结构化关系”这层信息

向量检索擅长找相似文本,但知识库问答里很大一部分答案是事实型、关系型的。比如“A模块依赖哪个中间件”“B模块的接口由谁维护”“C系统上线以来变更过几次”。这些问题的答案,在语义上不一定和提问高度相似,但它们在“实体关系”上完全匹配。

这就是知识图谱的强项。知识图谱把知识表达成三元组:

  • (A模块, 负责人, 张三)
  • (A模块, 依赖, 中间件X)
  • (B模块, 接口, 查询接口)

每一条都是明确的结构化事实。图谱不关心文本长什么样,只关心实体和关系是否成立。因此当RAG召回结果存疑时,图谱可以做一个独立的校验:你召回的片段里声称的“A模块负责人是张三”,图谱里有没有这条记录?有,通过;没有,降权或丢弃。

用知识图谱增强RAG,不是把两种检索平行堆在一起,而是让图谱在“检索前”“检索后”“生成前”三个节点分别发挥约束、过滤、补充证据的作用。后面我逐个拆。

2. 整体架构与数据组织

2.1 一套完整的数据流:文本到图谱,图谱进提示词

我先画一个整体的流程,免得后面细节把你绕晕。

第一步,文档入库后,系统做文本清洗与切分,一部分走向量化,生成向量库;另一部分走实体识别和关系抽取,生成知识图谱。这里要注意,不是每个文档都适合抽图谱,纯流程性文本或散文式内容抽出来的三元组质量很差。建议先按文档类型筛选,只对高信息密度的内容抽。

第二步,用户提问后,并行执行两条检索路径。路径一是常规向量检索,从向量库中取top-K候选chunk;路径二是图谱查询,先从问句中识别实体,在图中定位实体,再按关系做多跳扩展。

第三步,两条路径的结果汇聚到“证据融合”模块。这一步是重点,不是简单拼接,而是把图谱路径与文本片段做实体级对齐。比如图谱路径是(A模块, 负责人, 张三),向量召回有一块文本写着“张三也是A模块的负责人之一”,两者互为佐证,证据权重就高。

第四步,把融合后的结构化证据组织成自然语言上下文,连同原始提问一起送入大模型。大模型不是看图谱代码,而是看“根据知识库关系记录,A模块的负责人是张三”这类句子。

这个流程的本质,是把RAG从“只读文本”升级为“文本+事实双重证据”。图谱不直接生成答案,但它能约束生成方向。

2.2 图谱Schema设计:先做最小可行模型

一上来就设计三四十种实体类型、五十种关系类型,结果通常不是更准,而是更乱。我比较推荐先做一个小而稳的Schema,覆盖知识库里最高频的业务概念。

以一个内部技术知识库为例,实体类型我可以最小化成五种:

  • 概念:如“负载均衡”“熔断”
  • 组件:如“网关”“订单服务”
  • 负责人:如“张三”“李四”
  • 文档:如《部署指南》《变更记录》
  • 接口:如“查询接口”“回调接口”

关系类型起步也不需要太多,常见的就几种:

  • (组件, 依赖, 组件)
  • (组件, 负责人, 负责人)
  • (组件, 文档, 文档)
  • (文档, 描述, 概念)
  • (接口, 属于, 组件)

这个设计足够覆盖大量运维、研发、业务侧问法了。真正跑的时候,你发现新需要再说:比如加一个属性字段,把“上线时间”“版本号”“状态”挂到实体上。属性字段在检索时非常有用,用户问“订单服务最新版本是多少”,如果你在组件实体上有version属性,图谱可以直接回答,不需要绕多跳。

2.3 存储与更新:图谱不是一次建完就结束

图谱存储方案,如果是自用或小团队部署,完全可以用最常见的开源图数据库,数据量在百万级三元的规模下,多跳查询性能完全够用。如果你不想额外引入一个组件,也可以留在关系数据库里,用一张实体表和一张关系表来存,查询时通过递归CTE实现多跳。

这里给一个经验:真正限制你的不是存储选型,而是更新机制。

新旧文档交替时,很容易出现图谱里老关系没删、新关系又加进去的情况。一个陈旧实体在路径上反复出现,比没有图谱更糟糕。所以我建议每批次文档入库时,给三元组打上来源文档ID和入库时间,文档下线时,把对应三元组标记失效或直接删除,而不是永远追加。这条看起来很小,实践中价值极高。

3. 图谱与RAG的三处集成点

3.1 检索前增强:把用户问句改写成“实体加关系”

很多RAG系统的问题,不是大模型不行,而是提问被直接丢进向量检索里,没有任何结构化处理。图谱增强之后,第一步就要做查询解析。

做法是先用大模型或小模型对问句做命名实体识别,把问题里的实体挑出来。比如“订单服务的负责人是谁”解析成:

  • 实体:订单服务
  • 目标关系:负责人
  • 问题类型:单实体单关系

再比如“订单服务依赖的服务中,哪个由李四负责”,解析成:

  • 起始实体:订单服务
  • 第一步关系:依赖
  • 路径终点实体:服务
  • 约束条件:负责人等于李四

这一步输出的JSON最好保持简单。我试过非常复杂的意图理解框架,最后发现把精力放在实体对齐上,收益最大。

解析完成后,要做实体链接。知识库里同一个实体可能有不同叫法,“订单服务”也可能写作“订单中心”“Order Service”。你需要一份别名表,把所有叫法映射到规范实体。还没有别名表的时候,可以用文本相似度做模糊匹配,但别放松阈值,否则会把“订单查询服务”跟“订单服务”错当成同一个实体,后面全是错路。

3.2 检索后融合:向量召回和图谱路径合并时做的事

两条检索路径的结果,必须在一个框架下合并,不然会出现同一份证据被重复加权。

我用的合并策略分三步。

第一步,给每个图谱路径打分。路径分数来自三块:起点实体与问句实体的匹配度、关系类型与问句目标关系的匹配度、路径深度。深度1的路径肯定比深度3的路径优先级高。

第二步,给每个向量候选chunk打分。这个分数不是只取向量相似度,还要加一个“实体覆盖度”指标。具体做法是:把chunk文本做一次实体识别,看它包含几个本次问句解析出来的实体,实体越多,说明这个chunk越相关。

第三步,把路径和chunk按实体ID对齐。如果某个chunk里描述的事实,与某条图谱路径属于同一个实体关系,就把两边得分相加;如果两边互相矛盾,削弱而不是保留。

这套融合逻辑不像听起来那么复杂。你把它想成开会:文本chunk是“当事人描述”,图谱是“系统台账”。两边说法一致,采信度高;两边说法冲突,你就要警惕。

3.3 生成前组织:结构化证据怎么变成大模型看得懂的上下文

图谱路径是结构化数据,比如(订单服务, 依赖, 支付服务)。你不能直接把这段二进制一样的三元组丢给大模型,效果会很差。要让大模型真正用上图谱,必须把路径转成自然语言。

我常用的模板是:

“根据知识库结构化关系记录:服务组件A依赖服务组件B。该关系来自内部关系图谱,可信度为X。”

其中可信度来自实体对齐时的置信度。置信度低的关系,在生成提示词里要明确标注“候选证据”而不是“确认事实”。

多条路径组织进上下文时,建议控制在3到5条,再多大模型也会“看不过来”。而且不要把路径一股脑堆在文本chunk前面,要给每一条标注来源类型,比如“来自文本检索”或“来自关系图谱”。这样大模型才知道哪些是语义推测,哪些是关系事实。

4. 效果评测与关键参数调节

4.1 设立三类评测问题,别只用单点事实题

很多团队做RAG评测,收集100个问句,答案都是“某某是什么”,这远远不够。知识图谱增强最明显的场景是多跳关系问答,你不测这类问题,就看不到图谱的价值。

我建议把评测集分成三类:

  • 单点事实:A模块的负责人是谁
  • 单关系查询:B模块依赖哪些服务
  • 多跳路径:服务C依赖的服务中,哪一个属于李四团队

每类至少30到50条,总计一百条以上才有统计意义。评测时分别记录召回准确率和生成准确率。生成准确率按答案是否完全正确判断,部分正确或半对半错,都算错。

4.2 几个直接影响效果的参数

实体匹配阈值。实体链接时别名相似度阈值太高,真实实体会被漏掉;阈值太低,错链会传染整个路径。一般经验是先卡在0.80到0.85,再按评测集微调。

图谱查询深度。深度设为1,很多多跳问题答不了;深度设为3,查询时间和噪音都会增加。我一般默认允许深度2,只有在评测集里确认多跳问题占比高时,才放开到深度3。

证据条数。生成上下文里的证据总量,我通常控制在文本chunk 3到5个,图谱路径3到5条。如果你发现大模型开始“自由发挥”,多半是证据太多太杂,而不是提示词写得不清楚。

冲突仲裁策略。当向量chunk说“A负责订单服务”,图谱说“A是B模块的负责人”,你到底听谁的?我建议以图谱为主、文本为辅。因为图谱是在文档入库时专门抽出来的,它经过了一次结构化校验,噪音通常低于直接切块的文本。

4.3 实测效果:图谱增强后的变化体现在哪里

我们用内部知识库做过一组实验,选了一批单点事实和多跳关系问题,答案全部提前人工标注好。纯向量检索方案,多跳问题的准确率在65%左右;加入图谱作为辅助召回和证据校验后,多跳问题准确率提升到80%左右,单点事实问题虽然提升幅度小一些,但生成答案时的语气明显更确定,不再出现“可能是张三”这种含糊表达。

最值得关注的变化是“证据一致性”。没有图谱时,大模型经常把两处不同文档的冲突说法揉进一个答案;有图谱校验后,这种前后矛盾的输出明显减少了。回答每一步都有明确来源,排查效率也高了很多。

5. 常见问题与排查实录

5.1 实体识别不准,图谱越抽越歪

错误场景:新入库一份文档,系统从里面抽出“订单服务中心”,但图里已经存在“订单服务”这个实体。两者其实指同一个业务模块,却没被id对齐,导致后来所有查询都沿着两个实体分支走。

排查思路:不要只靠NER模型输出,要在实体入库前做一次“中心实体匹配”。先把待入库实体做向量化,和图谱中现有实体计算相似度,同时用领域同义词表兜底。相似度超过阈值,就挂到已有实体ID下;没超过,再创建新实体。

我当时踩坑的教训是:刚上线时为了效率,跳过了这一步,图谱两周内膨胀出大量重复实体,查询准确率反而下降了。

5.2 图谱查询出现多跳爆炸

错误场景:问“A服务影响范围”,系统从A服务出发,把所有能走到三跳以内的节点全部拉出来,返回了上百条路径。提示词根本放不下,大模型看到大量弱相关证据后开始编造。

排查思路:对路径做限制。一是限制关系类型,只扩展“依赖”和“调用”这类强关系,跳过“文档提及”这种弱关系;二是调整路径评分,每扩展一跳分数打折,深度超过阈值就停止扩展;三是给最终进入提示词的路径设硬上限。

更实用的办法是“先窄后宽”:先只取起点直接相连的一跳关系,如果这些关系里没有目标类型,再向外扩一跳。多数问答其实在一跳内就能解决。

5.3 新增文档没有触发图谱更新

错误场景:知识库更新了一版接口文档,接口名字变了,但图谱还保留旧接口。用户问新接口的具体信息,图谱路径指向旧实体,生成出来的答案带着上一代接口的参数。

排查思路:建立入库同步任务。文档一旦写入知识库,就放入待处理队列,由抽取服务消费。抽完之后,对比图谱中是否有同来源文档ID的旧三元组,有则先失效再插入新三元组。

这个环节没有太多捷径,拼的是工程化程度。每次发布更新前手动触发一次全量重建,或者依靠定时任务定期做一致性校验,至少要占一项。

5.4 大模型把图谱证据使用得“太自由”

错误场景:图谱里只有一条路径(A模块, 负责人, 张三),大模型却把张三的信息扩展成“张三具有多年架构经验,适合负责A模块”。乍看没什么问题,但实际上答案超出了证据范围。

排查思路:提示词里要强约束,明确写“只基于给定证据回答,禁止补充未出现的信息”。同时可以把每条证据的来源标记清楚,并提醒“来自关系图谱的事实之间没有逻辑外推空间”。

这个毛病在大模型参数比较大的场景特别常见,你不约束,它总想展示语言能力。

6. 我的实操体会

走完这一轮,我最大的感受是:知识图谱增强RAG,不是锦上添花,而是一条补短板的路径。纯向量RAG在泛化问题上表现不错,但在精确事实和关系链上确实不稳。图谱把它能管的管住,语义泛化留给向量和大模型,各干各的,系统才整体可控。

最后分享一个小技巧:别想着一次性把图谱建得又大又全。先建好核心实体和关系,跑通检索和融合流程,再逐步补充。一个小而准的图谱,比一个庞大但错乱的图谱有用得多。哪怕你的知识库很小,几百个实体、几百条关系,已经能让RAG的行为发生明显变化。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进