InterX - 知识图谱增强
上一篇聊了多通道检索系统如何从上万个 chunk 里精准找到相关内容。但有一个根本性的问题它解决不了:跨章节推理。
用户问"空调制冷效果差",首跳检索命中的是"常见原因"章节的 chunk。但解决方案在"维护保养"章节——两个章节之间没有任何字面重叠,BM25 和 Dense 向量都找不到它们的关联。
知识图谱就是为了解决这个问题而引入的。它不是传统意义上需要人工标注的知识图谱,而是一种"Agentic RAG 冷启动"的证据驱动建图方法。
核心思路:用 QA 证据种子,而不是 LLM 盲猜
传统知识图谱构建有两种路径:
- 人工标注:成本高,40 本手册、上万个 chunk pair,根本标不完
- LLM 盲猜:对所有 chunk pair 调用 LLM 判断是否有关系,成本爆炸(假设 5000 个 chunk,pair 数量是 5000×4999/2 ≈ 1250 万),而且 LLM 对不相关的 pair 大概率回答"无关",噪声巨大
我们的做法完全不同:利用 350 条 QA 评估数据中的证据引用作为种子。
这些 QA 数据是比赛提供的,每条数据标注了"正确答案引用了手册的哪些行"。这些引用天然就是 chunk 之间的关系——如果两个 chunk 同时被一条 QA 引用,它们大概率在语义上相关;如果一个 QA 的证据分散在不同章节,那这些章节之间就存在跨章节关联。
基于这个洞察,我们设计了五阶段流水线。
五阶段建图流水线
Phase 1: 解析证据引用 → evidence_resolved.json
从 350 条 QA 中提取证据引用,解析为结构化数据
产出: 1699 条引用,94.5% 解析率
Phase 2: 行号到 Chunk 映射 → evidence_mapped.json
将手册行号映射到具体的 chunk ID
方法: 二分查找 + 区间重叠检测
Phase 3: 构建图结构 + CO_EVIDENCE 边 → Kùzu graph.db
同一条 QA 引用的 chunk 之间建立 CO_EVIDENCE 边
权重: 0.5(证据共现,非直接语义关系)
Phase 4: LLM 语义边提取 → semantic_edges.json
对跨章节的 chunk pair,调用 LLM 判断是否存在语义关系
499 个 batch,仅 2 次失败
严格 YES/NO 判定,不允许模糊回答
Phase 5: 写入 SEMANTIC 边 → 完整图谱
LLM 确认的语义关系写入图数据库
权重: 1.0(直接语义关系)最终产出两类边:
| 边类型 | 数量 | 权重 | 来源 | LLM 成本 |
|---|---|---|---|---|
| CO_EVIDENCE | 48,508 条 | 0.5 | QA 证据共现统计 | 零 |
| SEMANTIC | 3,084 条 | 1.0 | LLM 确认的语义关系 | 按 batch 计费 |
48,508 条 CO_EVIDENCE 边是纯脚本构建的——没有任何 LLM 调用。这是整个设计最精妙的地方:我们用 QA 证据引用作为"免费"的关系信号,只对真正需要判断的跨章节 pair 才调用 LLM。
两级置信度图扩展
图建好了,怎么用?
检索阶段,首跳命中的 chunk 作为种子节点,然后在图上做 BFS 扩展。两种边的权重不同:
- SEMANTIC 边(权重 1.0):LLM 确认的语义关系,置信度高
- CO_EVIDENCE 边(权重 0.5):证据共现统计,置信度中等
扩展时的评分公式:
score = edge_weight × hop_decay × seed_score
其中 hop_decay = 1.0 / (1.0 + 0.3 × hops)这个公式很直觉:
edge_weight:边的类型决定基础分,SEMANTIC 边给 1.0,CO_EVIDENCE 边给 0.5hop_decay:跳数衰减,每多跳一跳分数衰减。第一跳衰减到 0.77,第二跳衰减到 0.63,越远越弱seed_score:种子节点自身的检索分数,高分种子扩展出来的节点也更可信
三者相乘,确保了:近的、强关联的、高分种子扩展出来的 chunk 得分高;远的、弱关联的、低分种子扩展出来的 chunk 得分低。
跨章节证据发现:一个真实案例
回到开头的问题:"空调制冷效果差怎么办?"
首跳检索
│ 命中: "常见原因"章节的 chunk
│ 内容: "制冷效果差可能原因包括滤网堵塞、制冷剂不足……"
│
▼ 图扩展(BFS)
│ 通过 CO_EVIDENCE 边 → 发现"维护保养"章节的 chunk
│ 通过 SEMANTIC 边 → 发现"故障排查"章节的 chunk
│
▼ 扩展结果
新增 8 个跨章节 chunk
包含: 滤网清洁步骤、制冷剂补充方法、专业维修建议没有知识图谱,系统只能返回"常见原因"章节的内容——告诉用户原因,但不告诉用户怎么办。有了图扩展,系统自动发现了"维护保养"章节的解决方案,补全了跨章节的证据链。
这就是知识图谱的核心价值:它不是给系统增加了"知识",而是增加了"关联"。手册里的知识本来就在那里,只是分散在不同章节。图谱把它们连起来了。
工程健壮性:不只是算法对了就行
算法思路对了,工程上如果不够健壮,一样跑不起来。我们做了几个关键的工程设计:
每手册独立 Kùzu 数据库
40 本手册,如果全部塞进一个图数据库,内存占用会很大,而且一本手册重建会影响其他手册。所以我们给每本手册建了独立的 Kùzu 数据库——需要重建某本手册时,只动那一个库,其他库完全不受影响。同时也规避了内存泄漏的风险。
增量构建
图数据库本身记录了哪些 chunk pair 已经处理过。Phase 3 写入 CO_EVIDENCE 边时,会先查询是否已存在;Phase 4 提取 SEMANTIC 边时,也会跳过已有边的 pair。不需要额外的状态文件,图数据库本身就是状态。
断点续传
Phase 4 是最耗时的阶段(499 个 batch 的 LLM 调用)。如果跑到一半网络断了或者进程被 kill,不需要从头开始。我们每 20 个 batch 周期性保存进度,--resume 参数可以从上次中断的地方继续。
并发 LLM 提取
单线程串行调用 499 个 batch 太慢了。我们用 ThreadPoolExecutor(4) 并发提取,提速 10-20 倍。同时设置了 pair 数量上限 3000 + 可重现采样(seed=42),保证结果可复现。
同 BigChunk 内 pair 跳过
如果两个 chunk 在同一个 BigChunk(900 tokens 的大块)内,它们已经在同一个 section 里了,层次关系已经通过 chunk 聚合体现,不需要 LLM 再判断一遍。这个优化大幅减少了需要处理的 pair 数量。
检索阶段完全零 LLM
最后强调一个关键设计点:知识图谱的检索阶段完全没有 LLM 调用。
图扩展是纯 BFS + 权重计算,确定性算法,延迟可预测。LLM 调用只发生在离线建图阶段(Phase 4),而且是一次性的。图建好之后,检索时的扩展操作就是遍历邻居节点、计算分数、排序——没有任何随机性,也没有任何在线成本。
这也是为什么我们选择 Kùzu 作为图数据库:它是嵌入式的,不需要独立部署,查询性能足够好,而且和 Python 生态集成得很好。
效果总结
| 指标 | 数值 |
|---|---|
| CO_EVIDENCE 边 | 48,508 条(零 LLM 成本) |
| SEMANTIC 边 | 3,084 条(LLM 提取) |
| 构建时间 | 约 7.5 小时(一次性离线成本) |
| 检索阶段 LLM 调用 | 0 次 |
| 每次检索扩展 chunk | 约 8 个(两个 RAG 案例均值) |
知识图谱在 InterX 中扮演的角色,不是"智能大脑",而是一个"关联索引"——它把手册中分散在不同章节的关联信息预先连接起来,让检索时能跨越 chunk 边界找到完整的证据链。这种"离线建图、在线查图"的架构,既保证了检索质量,又控制了在线成本。