知识图谱增强大模型这件事,我断断续续折腾了一年多。最早是把三元组硬塞进提示词里,后来自己搭子图召回链路,再后来把图查询封装成工具让模型自己调用,几条路都走过一遍,踩的坑比写的代码多。这篇不谈概念普及,只谈比较:同一个业务问题摆在这儿,四条主流路线各自的账怎么算、什么条件下该选哪条、选完之后会卡在哪里。如果你正在纠结“我们手上已经有一张图,怎么让 LLM 真正用起来”,或者在犹豫“要不要为了图谱去动模型权重”,下面这些内容基本能帮你把决策提前做完,少走几个月弯路。
先说清楚这篇适合谁看。手上已经有一张可用的知识图谱、或者正准备建图的工程同学,可以直接从第三节开始看落地细节;做技术选型的负责人,建议重点看第二节的成本账和第七节的实测对比,那里有每条路线的隐性开销。完全没接触过图数据的朋友也不用担心,关键名词我会补一句大白话解释,比如“子图”就是从整张图里切一小块相关的出来,“实体链接”就是把用户嘴里说的那个词对上图里的那个节点,不影响往下读。
1. 先把“增强”这件事定义清楚
1.1 三类真实痛点决定了路线分叉
很多人一上来就问“哪个方案效果最好”,这个问题没法回答,因为“增强”要解决的痛点根本不是同一个。我见过的需求大概能归成三类。第一类是事实型长尾:模型不知道某个具体型号的设备配什么规格的备件,这类知识在参数里压根没存过,只能外部去查。第二类是推理链断裂:答案不在某一条边上,得顺着两三条关系走一遍才能拼出来,比如“这台设备的上游供应商,最近一次变更记录是谁签的”。第三类是可追溯:答案对不对不只是准确率问题,还得能指出这个结论是从哪条记录推出来的,审计场景里这一条往往是硬指标。
这三类痛点对应的方法完全不一样。第一类用检索式最划算,捞一小块子图塞进上下文就解决了,改图即生效。第二类要看跳数,两跳以内检索式还能扛,三跳以上或者需要动态决定下一步查什么,就得考虑把查询能力交给模型自己调度。第三类反而是最容易被忽略的——很多人做完增强发现答案对了,但拿不出证据,最后还得回头补引用链路,等于返工。我的建议是:在选型之前,先把你的问题按这三类打个比例,哪类占大头,路线基本就定了。
还有一类隐性痛点值得单独提一句:术语和表述风格不一致。同一个概念,业务方说“跳闸”,图里写的是“保护动作”,模型回答时用的又是第三套说法。这种问题检索式解决不了,因为知识本身没缺,缺的是语言对齐,通常得靠指令微调那一路。判断方法很简单:如果你发现检索出来的内容是对的,但模型组织出来的话术让业务方读着别扭,那就不是召回问题,是表述问题。
1.2 四条路线的一句话画像
为了后面比较方便,我先把四条路线各自的样子描述清楚,用最直白的话。
检索式:推理的时候去图里捞一小块相关的子图,序列化成文本,跟问题一起塞进上下文。模型本身一行代码不改,改的是喂给它的东西。优点是见效快、可解释、改图即生效;缺点是每次请求都多花 token,召回质量直接决定上限。
训练式:把图里的结构信息或者嵌入向量,通过对比学习、指令微调、继续预训练等方式写进模型参数。优点是推理时不增加额外开销,语言风格和术语体系也能一起对齐;缺点是训练成本高、更新慢、图一变就得重训,而且很容易训出“记住了但说不清为什么”的效果。
Agent 式:不预先捞子图,而是把图查询能力封装成几个函数交给模型,让它自己决定查什么、查几次。优点是灵活,聚合、计数、条件筛选这类问题表现明显更好;缺点是延迟高、稳定性差、调试困难,模型偶尔会写出跑不通的查询。
协同式:反过来用,让 LLM 帮你建图、补边、做实体消歧,把图本身的质量提上去,再叠加前面三种增强。它不是一个独立的推理方案,更像是一个前置的放大器。很多团队做完检索式发现效果上不去,回头一看是图里本身缺边、重名实体没合并,这时候该做的就是这一路。
这四条不是互斥的。我现在的项目里就是协同式建图 + 检索式兜底 + Agent 处理聚合类问题,三层叠着用。但叠加是有代价的,复杂度上去了,排查问题的时候链路特别长。所以下面第二节我先把单条路线的账算清楚,再谈怎么组合。
2. 横向对比:把成本、延迟、可控性摊在一张表上
2.1 一张表看清关键差异
这张表是我自己项目里实际用过的对比口径,不是拍脑袋写的。里面“典型失败模式”这一列尤其值得看,因为它决定了你后期花多少时间在救火上。
| 对比维度 | 检索式(子图注入) | 训练式(权重注入) | Agent 式(工具调用) | 协同式(LLM 反哺建图) |
|---|---|---|---|---|
| 知识注入时机 | 推理时 | 训练时 | 推理时按需 | 建图时 |
| 图更新后生效速度 | 立即 | 需重训,数天到数周 | 立即(改工具描述即可) | 立即 |
| 单次请求额外 token | 1500 到 3000 | 基本为 0 | 2000 到 4000(多轮累计) | 不涉及 |
| 单次请求额外延迟 | 50 到 150 ms | 0 | 200 到 800 ms(多轮加图查询) | 不涉及 |
| 首次建设成本 | 低到中 | 高 | 中 | 中到高 |
| 长期维护成本 | 低 | 高 | 中(schema 变动要改) | 中(需要抽检) |
| 可解释与可溯源 | 强,能指到具体边 | 弱,说不清来源 | 中,取决于是否记录查询 | 不涉及 |
| 对模型能力依赖 | 中 | 高(要会训) | 高(要会调工具) | 中 |
| 典型失败模式 | 召回错、子图太大稀释重点 | 训完记不住、更新不及时 | 查询写错、死循环、空结果 | 引入噪声边、污染图 |
| 适合的问题类型 | 单跳事实、两跳关系 | 术语对齐、风格统一 | 聚合、计数、条件筛选 | 图质量差、覆盖不全 |
这张表里我最想强调两行:图更新后生效速度和可解释性。前者决定你的运维节奏,如果你的图每天都在变(比如设备台账、人员组织架构这种高频变动的领域),训练式基本可以直接排除,因为训练永远追不上变化。后者决定你能不能过合规那一关,金融、医疗、内部审计这类场景,答对了但说不出出处,等于没答。
2.2 延迟和成本的账要算到 token 上
很多方案对比文章只讲效果不讲钱,实际做选型的时候,token 成本往往是最后一根稻草。我按自己的项目算过一笔账,基准是:问题平均 60 字,系统指令 200 字,输出 300 字。
不做任何增强的话,一次请求大概 400 到 500 token。做检索式增强,假设召回 40 个节点、60 条边,序列化之后平均每条边 25 到 30 个字,加上节点描述,大约 1800 字,折合 2500 token 左右,加上原有的,一次请求 3000 token 出头。也就是说成本直接翻了大概 6 倍。这个倍数在实际业务里是要命的,如果每天有十万次调用,账单差距非常直观。
Agent 式看起来更贵,但要看场景。简单的单跳问题它只需要一轮工具调用,累计下来 2000 token 左右,反而比检索式便宜;难一点的聚合问题要三轮,累计 3500 到 4000,加上每次图查询 60 到 200 毫秒的耗时,P95 延迟会明显拉高。所以 Agent 式的正确用法不是全量替换,而是做路由:简单问题走检索式,判到聚合或条件筛选类才放给 Agent。做这个路由本身不复杂,用一个小模型或者规则判断问题里有没有“多少”“哪些同时满足”“列出所有”这类信号词,准确率就够用了。
训练式的账最容易被算错。表面上看推理时零额外开销,但隐性成本在别处:标注数据的成本、训练算力、每次图更新后的重训周期、以及模型版本管理带来的评测成本。我见过一个团队为了让模型学会用领域术语回答,训了三轮,效果确实好,但后来图结构大改,又得重训一遍,前后投入的人力远超预期。这笔账要不要算,取决于你的图更新频率——一年更新一次和一周更新一次,结论完全相反。
提示:算成本的时候别忘了失败重试。检索式召回错了,用户重问一次,成本直接翻倍。召回质量差的方案,账面 token 便宜,实际总成本可能更高。
2.3 可维护性是被低估的选型维度
效果和成本大家都算得清,可维护性经常被忽略,但它往往是项目半年后是死是活的分水岭。我按改动类型拆一下。
改图结构(加了一种新关系、新实体类型):检索式基本无感,只要序列化模板能兼容;Agent 式要改工具描述和查询模板,还得重跑一轮回归测试,因为模型对新 schema 的理解可能变了;训练式最惨,得重新构造数据、重训、重测。
改序列化格式(比如从三元组平铺改成自然语言段落):检索式要重跑评测,因为模型对格式的敏感度很高,我实测过同一个子图换两种写法,准确率能差 8 个百分点;Agent 式基本无感;训练式如果格式写在训练数据里,那就得重训。
改业务口径(比如“有效设备”的定义变了):这是最麻烦的一种。检索式可以通过在指令里补充定义解决;训练式必须重训,因为口径写进参数了;Agent 式可以在工具描述里加过滤条件,改动量中等。
综合下来我的判断是:图更新频率高于每月一次、或者业务口径经常调整的场景,优先选检索式或 Agent 式,慎重考虑训练式。反过来,如果你的图非常稳定(比如行业标准、法规条款这类几年才动一次的知识),而且对术语一致性要求极高,训练式的性价比就出来了。
3. 检索式落地:子图召回这条链路怎么搭
3.1 三层漏斗:实体链接、邻域扩展、预算截断
检索式听起来简单——“查一下相关的塞进去”,实际上手会发现 80% 的效果损失发生在第一步。我把这条链路拆成三层漏斗,每一层都有明确的职责。
第一层,实体链接。用户问“三号泵上次检修是什么时候”,你得先把“三号泵”对上图里的节点。这一步是整条链路最容易崩的地方。我踩过的坑包括:别名没覆盖(图里叫“3#给水泵”,用户说“三号泵”)、同名不同实体(三个车间各有一台“一号机”)、以及简称和全称混用。我的做法是建一张别名字段,把常见叫法、拼音、历史名称全塞进去,然后用混合检索:别名精确匹配优先,匹配不上走向量召回取前 10 个候选,再把候选丢给一个小模型做消歧,让它根据上下文(比如用户前面提到过哪个车间)选一个。这一层做不好,后面全是白费。
第二层,邻域扩展。种子节点定下来之后,往外扩一跳还是两跳,是个要调参的事。我一开始图省事扩两跳,结果子图动辄两三百个节点,序列化之后上万 token,成本和效果双输。后来改成:默认一跳,只有当问题里出现明显的链式信号(“的上游”“的负责人所在部门”这种)才扩两跳,并且扩展时做剪枝——按边的类型白名单过滤(只保留业务相关的几种关系)、按时间衰减(太老的边降权)、按节点度数限制(超级节点比如“公司”这种只保留少量连接)。
# 邻域扩展的核心参数,这些值是我调了两周才定下来的 EXPAND_CONFIG = { "max_hop": 1, # 默认一跳 "max_hop_chain": 2, # 检测到链式信号才放两跳 "max_nodes": 40, # 硬上限,超了就按分数砍 "max_edges": 60, "edge_type_whitelist": ["belongs_to", "supplier_of", "maintained_by", "located_in"], "super_node_degree": 200, # 度数超过这个值的节点只保留 top5 连接 "time_decay_days": 730, # 两年以上的边降权 0.5 }第三层,预算截断。前面两层产出的子图往往还是超预算,得做排序再截断。我的排序分三个因子:与问题的语义相关度(用向量相似度算)、节点中心性(度数越高越可能是关键实体)、路径长度(离种子越近越重要)。按加权分排序,砍到 token 预算以内。这里有个细节值得说:截断的时候不要把强相关的内容放在最前面,也不要放在最后面。模型对长上下文中间部分的注意力会衰减,我实测把最关键的三条边放在序列化的最后、次关键的放最前,效果比按相关度排序好一些。这个结论不一定通用,但建议你在自己的场景里做个 A/B 验证。
3.2 序列化格式的四种写法与取舍
子图捞出来了,怎么变成文本喂给模型,这件事的影响比很多人想的大。我试过四种写法。
| 写法 | 示例 | token 开销 | 模型理解度 | 溯源便利性 |
|---|---|---|---|---|
| 三元组平铺 | (三号泵, 属于, 一号车间) | 中 | 中 | 强,每行对应一条边 |
| 路径式 | 三号泵 -[属于]-> 一号车间 -[负责人]-> 张某 | 低 | 高(关系链清晰) | 中,多跳路径整体引用 |
| 自然语言段落 | 三号泵位于一号车间,该车间负责人是张某 | 高 | 最高(贴近训练分布) | 弱,难对应到具体边 |
| 结构化嵌套 | {"设备":"三号泵","车间":{"名":"一号","负责人":"张某"}} | 中 | 中低(部分模型不敏感) | 中 |
我最后选的是混合写法:主体用路径式,把关键的多跳关系表达清楚;末尾再补一个简短的证据清单,用三元组平铺列出所有被引用的边,并要求模型在回答里标注序号。这样既保证了理解度,又解决了溯源问题。实测下来比纯自然语言段落省 30% 左右的 token,溯源效果好很多。
注意:序列化格式一旦定下来,就不要轻易改。改一次要重跑全量评测,因为模型对格式的敏感度超出预期,同一个子图换种写法准确率能掉好几个点。
3.3 社区摘要 vs 路径摘要:什么场景选哪个
这两年图检索有一个明显的分支:一个是围绕具体节点捞邻域子图(路径摘要),另一个是预先对图做社区划分、给每个社区生成摘要,检索时把摘要塞进上下文(业界常说的社区摘要式做法,GraphRAG 属于这一类)。两者解决的问题不一样,选错了会很别扭。
路径摘要擅长局部事实:某个实体有什么属性、跟谁有关系。它不擅长全局性问题,比如“这张图里主要有哪些类型的风险”“从整体上看设备故障集中在哪几个环节”。这类问题的答案散布在整张图上,你捞任何一个局部子图都不对。
社区摘要正好补这一块。做法是先跑社区发现算法(比如 Louvain 这类),把图切成若干个簇,然后用 LLM 给每个簇写一段摘要,检索时先定位到相关社区,再把摘要塞进去。代价是预处理成本很高:一次全量摘要生成可能几十万 token,图一变就得增量更新,而且社区划分的结果会漂移。
我的建议是:如果你的问题里全局性、归纳性的占 20% 以上,值得做社区摘要;如果九成问题都是围绕具体实体的,那别折腾,老老实实做邻域子图,社区摘要的投入产出比很低。
4. 训练式:什么条件下值得动权重
4.1 三档投入:嵌入对齐、指令微调、继续预训练
训练式不是只有“微调”一条路,投入从小到大能分三档,收益和风险差得很远。
第一档,嵌入对齐。用知识图谱嵌入模型(TransE、RotatE 这一类)把实体和关系学成向量,然后在检索阶段用这些向量做相似度计算,或者在输入里拼接少量嵌入特征。这一档几乎不改模型本身,主要提升的是召回质量。成本低、风险小,我建议任何做检索式的项目都顺手做一下,尤其是别名多、同义词多的领域,效果立竿见影。
第二档,指令微调。构造“问题 + 子图 → 答案”的样本对,做有监督微调。这是最常见的一档,主要价值在于对齐表述风格和提升子图利用率。我实测过一个对比:同样质量的子图,微调过的模型能多利用 15% 到 20% 的有效信息,因为它更懂该看哪里。成本中等,几千到上万条样本就能出效果,风险是容易过拟合到样本的表述方式上。
第三档,继续预训练注入。把图谱信息通过知识掩码、实体链接替换等方式融进预训练语料,让模型在底层就带上领域知识。这一档我试过一次,结论是除非你的领域语料极其稀缺、且图谱质量极高,否则不划算。训练成本是一方面,更要命的是更新困难——图一变,之前训的东西就过时了,而你很难定位到底哪部分参数过期了。
4.2 数据构造的坑:负样本与关系方向
训练式最大的隐性成本不在训练,在数据构造。我在这上面栽过几次,说几个具体的。
负样本太容易。一开始我用随机替换尾实体的方式造负样本,模型很快就学到 99% 准确率,但上线效果很差。原因很简单:随机替换出来的负样本太假了,比如“三号泵属于火星”这种,模型一眼就能看出来,学不到细粒度的区分能力。后来改成难负样本:同关系下类型正确但实例错误(“三号泵属于二号车间”)、类型相近但关系错误(“三号泵维护了张某”),训练难度上去了,效果才真实。难负样本的比例我一般控制在 1:2 到 1:3,全用难的反而会让模型过度保守。
关系方向搞反。这个坑特别隐蔽。图谱里“A 供应 B”和“B 由 A 供应”是同一条边的两种表达,序列化的时候如果没统一方向,模型学出来的东西就是乱的。我的做法是在数据构造阶段强制统一方向,并且在序列化模板里给每条边标注箭头,比如A -[供应]-> B,让方向显式可见。
标签泄漏。这个问题最致命,因为它会让你的评测结果虚高,直到上线才发现。典型表现是:构造样本时把答案实体名不小心写进了 prompt 里,或者问题和图谱里的描述文本几乎一模一样(相当于把原文当问题)。做构造的时候一定要留一份改写过的评测集,问题和图谱描述之间不能有长串字面重合,否则测出来的分数没有意义。
提示:训练式方案一定要做“不训练”的对照组。我见过好几次这样的案例:微调后效果提升了,但仔细一看,提升来自换了更好的检索配置,跟训练没关系。对照组不做,钱就白花了。
5. Agent 式:把图查询变成模型手里的工具
5.1 什么信号说明你该上 Agent
Agent 式不是更高级的方案,它只是更适合某类问题。判断信号我总结成三条。
第一条,问题需要先看结果才知道下一步查什么。比如“找出所有近半年故障超过三次的设备,再查它们的供应商有没有重合”,这种问题的查询路径没法预先确定,必须边走边看。检索式做不到这一点,因为它是一次性把子图塞进去,后续没法追问。
第二条,涉及聚合、计数、排序。“有几台”“最多的是哪个”“排前三的”这类,靠语义相似度召回子图很容易漏,因为答案不在某一块子图里,得遍历。把聚合查询封装成工具让模型调用,准确率能上一个台阶。
第三条,条件筛选复杂且有明确过滤语义。比如“只查 2023 年之后、状态为在用的、且属于华东区域的设备”,这种问题用向量检索做不好,因为它是结构化的过滤条件,用图查询语言表达最直接。
反过来说,如果你的问题九成都是“某某是什么”“某某和某某什么关系”,那别上 Agent,纯属给自己找麻烦。多轮调用带来的延迟、调试难度、以及模型偶尔抽风写出跑不通的查询,这些代价只有在问题复杂度确实需要的时候才值。
5.2 稳定性设计:跳数上限、超时、空结果回退、schema 精简
Agent 式最大的问题是不稳定。同样的输入,今天答对明天答错,排查起来特别痛苦。我后来总结了几条强制约束,加上之后稳定性明显好转。
跳数上限。硬性限制最多三轮工具调用,超过就强制收敛,让它基于已有信息作答并说明信息不足。不加这个限制,模型会在信息不足的时候反复查,有时候能查七八轮,延迟直接爆炸。
查询超时和行数上限。图查询一定要有超时(我设的 5 秒)和返回行数上限(我设的 200 行)。不加的话,一条没有过滤条件的查询可能把整张图拉出来,服务直接挂掉。同时工具本身要设成只读,这一点是底线。
空结果回退。查询返回空的时候,不要直接把“没查到”丢给模型,它会开始瞎猜。我的做法是返回一个提示,告诉它“没查到结果,可能的相近实体有:某某、某某”,让它有机会修正。这个改动让空结果场景的二次成功率提升了不少。
schema 精简。工具描述里塞太多东西,prompt 会爆,而且模型反而抓不住重点。我现在只暴露 8 到 12 种最常用的关系类型,工具描述控制在 300 token 以内,其余的在需要时通过第二层工具动态加载。实测下来,schema 越精简,查询写对的比例越高。
5.3 查询语言生成 vs 模板填充
让模型直接写图查询语言(比如 Cypher 这类),自由度最高但成功率不稳;做成模板让模型填参数,成功率高但覆盖不了长尾。我两种都试过,最后用的是混合策略。
| 策略 | 覆盖率 | 一次成功率 | 调试难度 | 适用场景 |
|---|---|---|---|---|
| 模型裸写查询 | 高 | 60% 到 75% | 高 | 探索性、一次性问题 |
| 模板参数填充 | 中(限模板覆盖) | 92% 以上 | 低 | 高频固定问法 |
| 混合:先匹配模板,失败再放开 | 高 | 85% 左右 | 中 | 生产环境推荐 |
具体做法是:维护 10 到 20 个高频查询模板(按实体查属性、按关系查邻居、按条件筛列表等),先做意图匹配,匹配上就填参数;匹配不上再让模型自由生成,但生成的查询要走一遍语法校验和干跑(先EXPLAIN一下,不实际执行),校验不过就打回让它重写,最多两次。这套下来,生产环境的一次成功率能稳在 85% 以上,剩下的走人工兜底。
6. 协同式:让 LLM 反哺图谱
6.1 建图阶段的四个高价值环节
协同式这条线经常被当成“建图工具”,其实它对增强效果的影响比想象中大。我梳理了四个高价值环节。
实体抽取。从非结构化文本(工单、报告、说明书)里抽实体和关系,构建初始图谱。这一环 LLM 的表现已经相当可用,但要注意长文本的切分策略——切得太碎会丢跨段关系,切得太长会漏抽。我的经验是按段落切,相邻段落保留 20% 重叠,跨段关系靠后期合并补。
实体消歧。这是提升图质量性价比最高的一环。同一台设备在不同文档里有三四种写法很常见,消歧做不好,图里就是一堆孤岛。做法是用向量相似度先粗筛候选,再让 LLM 结合上下文判断是不是同一个实体,判断时要求它给出理由,方便抽检。
关系抽取与校验。抽出来的关系要过一遍类型校验,确保符合预定义的 schema。我遇到过抽出来“设备-负责-人”结果方向反了的情况,后来加了 schema 约束和方向校验才好。
缺失关系补全。这一环收益大风险也大。用 LLM 根据已有信息推断可能缺失的边,能显著提升图谱的连通性,但推断出来的东西不能直接当事实用。我的处理是给每一条推断边打上“推断”标记和置信度,默认不参与检索,只有置信度超过阈值且经过抽检确认的才转正。
6.2 自动补边的风险控制
补边这件事我踩过坑,说一下具体控制手段。
置信度阈值 + 抽检比例。推断边按置信度分层,高置信度的抽检 10%,中置信度的抽检 30%,低置信度的直接不进图。抽检由业务方做,虽然费人,但比错了强。
来源标记。每条边都要记录来源(原文抽取、规则推断、模型推断)和时间戳。检索的时候可以根据场景决定要不要用推断边——审计场景一律不用,探索性问答可以用但要标注。
隔离区。推断出来的边先放隔离区,不直接进主图,避免污染。这个设计看起来多余,实际救过我好几次——有一次模型批量推断出了一批“设备属于车间”的边,方向全对但车间归属全错,幸亏在隔离区被抽检发现了。
版本回滚。每批补边操作都要可回滚。这个不用多解释,做过数据的人都知道有多重要。
7. 我的一套最小对比实验
7.1 数据与任务设计
为了搞清楚几条路线在自己场景下的真实差距,我搭了一套最小对比实验。数据用的是某运维团队内部的设备故障图谱,脱敏后大约 3 万个实体、12 万条边,涵盖设备、车间、人员、供应商、故障记录五类节点。实验规模不大,但足够看出趋势。
任务设计成四类,每类 100 题,全部人工标注标准答案:单跳事实题(“某型号泵的额定功率是多少”)、两跳关系题(“某设备的供应商负责人是谁”)、聚合计数题(“近半年故障超过三次的设备有几台”)、条件筛选题(“列出所有 2023 年后投产且状态异常的华东设备”)。四类题的难度梯度是我们刻意设计的,用来观察不同路线在不同复杂度上的表现差异。
对照组是不做任何增强的基线,同一批模型、同一套提示词,只是不给图谱信息。这个对照组非常重要,我见过太多项目连基线都没测就宣布方案有效。
7.2 指标与打分口径
指标选了五个:答案准确率、事实一致性(回答里每个事实陈述是否能在子图里找到依据)、引用可追溯率(能不能指到具体的边)、P95 延迟、单次请求 token 成本。
准确率的打分我用了 LLM 裁判加人工复核的方式,裁判占比 100%,人工抽 20% 复核。裁判的提示词里有一个关键设计要求:必须先列出证据再给判断,不让它直接输出“对/错”。这个小改动让裁判和人工的一致率从 70% 多提升到 88% 左右。原因很直白,让它先找证据,它就没法偷懒凭感觉判。
延迟测量统一用同一台机器、同一个网络环境,每类题跑三遍取 P95。token 成本按实际消耗统计,包括重试的消耗。
7.3 实测结论与踩坑记录
结果和我预期有出入,几个结论值得说。
检索式在两跳以内的题上表现最好,单跳准确率 91%,两跳 84%,明显高于基线(基线分别是 62% 和 41%)。但到聚合题就掉得厉害,只有 58%,因为答案散布在整张图上,捞局部子图根本不够。
Agent 式在聚合和条件筛选上反超,聚合题 82%,筛选 88%,但延迟是检索式的 3 倍多,P95 从 900 毫秒涨到 3.1 秒。这个延迟在交互场景是要命的,所以最后我做了路由,只把聚合类问题放给它。
训练式在术语一致性上表现最好,人工评分里“表述是否专业”这一项高出其他方案一截,但准确率提升有限(两跳题从 84% 提到 87%)。投入产出比我个人觉得一般,除非你的场景对表述规范有硬要求。
协同式的效果体现在召回率上,补完实体消歧和缺失边之后,检索式的两跳准确率从 84% 提到 89%,但代价是引入了大约 3% 的噪声边,抽检才发现。所以补边一定要走隔离区和抽检,这个教训是实打实花时间换来的。
几个踩过的坑也记一下。第一个坑是评测集泄漏:一开始我构造的问题直接从图谱描述里改写,字面重合度很高,测出来检索式准确率 95%,高兴了两天,换成业务方真实提问方式(口语化、带错别字、省略主语)之后掉到 76%。第二个坑是子图顺序:同样的子图,把关键边放在中间位置,准确率比放在首尾低 5 个点左右,这个现象在长上下文里更明显。第三个坑是 LLM 裁判偏爱长答案,回答越详细它越容易判对,后来我在裁判提示词里明确要求“冗余信息不加分,错误信息扣分”才纠过来。
8. 常见问题与排查速查表
8.1 召回侧
| 症状 | 可能原因 | 验证方法 | 处理方式 |
|---|---|---|---|
| 答案明显在图里但答不出来 | 实体链接没对上 | 打印第一层候选列表看种子对不对 | 补别名、加向量召回、加消歧模型 |
| 召回的子图跟问题无关 | 种子选错或扩展过度 | 抽样看子图内容 | 收紧边类型白名单,限制跳数 |
| 简单问题答错、复杂问题反而对 | 子图太大稀释了重点 | 统计子图节点数与准确率的关系 | 降低 max_nodes,提高排序权重 |
| 同名实体张冠李戴 | 消歧缺失 | 查同名节点数量 | 引入上下文消歧,加限定属性 |
8.2 生成侧
| 症状 | 可能原因 | 验证方法 | 处理方式 |
|---|---|---|---|
| 回答里出现子图没有的信息 | 模型自行补全 | 做事实一致性检查 | 提示词里加“仅依据给定证据作答” |
| 答案对但读者看不懂 | 术语未对齐 | 人工评分 | 指令里加术语表,或走微调 |
| 引用编号错乱 | 序列化格式混乱 | 检查编号与证据清单是否一致 | 固定序列化模板,编号与边一一对应 |
| 长上下文里漏掉关键信息 | 注意力衰减 | 把关键边换位置做 A/B | 关键信息放首尾,控制总长度 |
8.3 工程侧
| 症状 | 可能原因 | 验证方法 | 处理方式 |
|---|---|---|---|
| 延迟忽高忽低 | 图查询未设超时 | 看慢查询日志 | 加超时和行数上限 |
| 成本超预期 | 重试次数多 | 统计重试率 | 提升一次成功率,加缓存 |
| Agent 反复查询不收敛 | 无跳数上限 | 看调用轮次分布 | 硬性限制三轮,超限强制收敛 |
| 更新图后效果变差 | 序列化模板不兼容新结构 | 对比更新前后的子图样例 | 模板做向后兼容,更新前跑回归 |
这套速查表我打印出来贴在工位上过,实际排查的时候按“症状”那一列从上往下找,基本能定位到方向。有一点要提醒:别一次改多个变量。我早期排查问题喜欢同时调三个参数,结果效果变好了也不知道是哪个起的作用,后来强制自己一次只动一个,虽然慢,但结论可靠。
最后分享一个我个人觉得最值钱的习惯:每次做方案对比,都留一份“最笨的基线”——不做任何增强、直接问模型。这份基线看起来没用,但它是唯一能告诉你“这套复杂链路到底值不值”的参照物。我做过三次方案重构,两次都是因为发现复杂方案比基线只好了一点点,而成本翻了好几倍。真正拉开差距的,往往不是选了哪条路线,而是把实体链接、子图剪枝、序列化格式这几个不起眼的环节抠到位。