[控场AI]
· 9 分钟阅读· 4,958 字

当LLM遇见知识图谱:如何让数据库诚实地说“我不知道”

当LLM遇见知识图谱:如何让数据库诚实地说“我不知道”

Helio用知识图谱做企业知识代理,核心结论是定义“正确”比优化模型更难也更重要。

Helio 是一个基于 FalkorDB 知识图谱的企业知识代理,用 7 种节点、14 种边模拟 120 人金融科技组织,回答“谁了解支付服务、宕机影响什么、决策在哪定”。文章的核心不在检索,而在如何确信 LLM 输出正确。团队指出 LLM 自我评判是循环论证,幻觉与正确答案在结构上无法区分,因此在实现前先手写 20 条标准答案键,其中 5 条刻意无解,把“我不知道”设为有评分的特性。文章还拆解了数量与时效混淆、SUPERSEDES 边证明过时、验证门 AND/ANY_OF 误判、SSE 全量快照、未闭合 div 导致静默删除等一系列“看起来正确、实际错误”的坑,并给出先写答案键、指标坏了别修模型、让破坏性操作大声失败等建议。

企业知识是天然图谱:服务依赖服务,团队拥有服务,决策覆盖决策。但大多数企业搜索仍停留在“找文档”层面。Helio 是一个基于知识图谱的企业知识代理,它试图回答“谁了解支付服务?它宕机了会影响什么?这个决策在哪里定的?答案有多新?”——一次遍历,路径可见。

Graph Hacks - Thestrals

但真正棘手的问题不在检索,而在于你如何确信一个 LLM 生成的结果是正确的。本文拆解了 Helio 团队踩过的坑,核心结论是:在 AI 系统里,定义“正确”比优化模型更难,也更重要。

检索的信任危机:循环验证陷阱

Helio 团队用 FalkorDB 构建了一个包含 7 种节点类型、14 种边类型的合成金融科技知识图谱,模拟 120 人在支付迁移中的组织。然后他们把 LLM 接到图谱上,通过工具调用循环工作。

检索部分很快跑通了。紧接着一个理所当然的问题浮出水面:你怎么知道它没有胡编?

常规做法是让 LLM 自我评判——问模型“这个答案对吗?”。但作者明确指出:这是循环论证。一个幻觉的所有权链路和一个正确的所有权链路在结构上完全相同:都是基于格式良好的边生成的流畅文本,都提到服务、团队、日期和文档。当评判模型被问“这看起来对吗?”,它看到的证据在两种情况下一模一样。

失败在构造上就是不可见的。没有任何信号会提前警告你。

LLM-as-a-Judge(模型即评判者) 是当前评估生成式AI系统的主流范式:用另一个(通常更强的)LLM对候选答案打分或判断对错。它的吸引力在于成本低、可扩展,且能处理开放式输出。但其根本缺陷在于评判者与被评判者共享同一套语言先验——两者都会把“格式良好、术语一致、逻辑通顺”误认为“事实正确”。在知识图谱检索场景中,这一点尤其致命:无论答案是否真的对应图中的边,只要文本读起来像那么回事,评判模型就倾向于给高分。学界因此提出“参照锚定评估”(reference-anchored evaluation),即先固定人工标注的标准答案,再让自动评分对齐该答案,而非让模型自由裁量。Helio 的 GROUND_TRUTH.md 正是这一思路的工程化落地:把“正确”外置为不可被模型漂移污染的固定文件。

先写答案键:把“正确”定义在实现之前

团队做了一个当时觉得反直觉的决定:在生成任何一行数据之前,先在 GROUND_TRUTH.md 中手写 20 条关于公司的事实。

这颠倒了通常的顺序。多数人先建系统,再凭肉眼判断它是否能工作。而 Helio 先锁定“正确”的含义,生成器必须满足这个答案键,而不是反过来。

更关键的是:这 20 个问题中有 5 个在图谱中没有答案。这是有意设计的。例如:

"谁拥有 config-service?"
→ "图谱中不存在 OWNED_BY 边。没有团队拥有它。"

这个回答就是产品本身。不是道歉,不是含糊,不是猜测——因为缺失的边是一个真实的、正确的答案,而不是数据的缺口。在这里发明一个所有者的代理已经失败了,即使句子读起来完美无缺。团队把这一点设为显式的评分类别,而不是脚注。

这个框架改变的不只是测试,还有优化目标。一旦“拒绝回答”成为与“正确回答”同权重的评分行为,提示词就不再关于“如何更有用”,而开始关于“如何更可验证”。

图谱才能抓到的两类错误

数量与时间的混淆陷阱

Priya Sharma 在 payments-gateway 上有最多的提交——14 次。她最后一次提交是 2026-04-15,也就是她离开公司的那天。

一个 ORDER BY count DESC 会返回 Priya。这个查询在数量上技术正确,但作为答案完全错误。任何把“谁了解这个”压缩成一个数字的系统,都会自信地产生这种错误,而且输出里没有任何可疑迹象。

Helio 的解法是返回两个独立信号并拒绝混合:

平台团队拥有支付服务。Ravi Menon 最近仍在处理它(2026-09-28)。Priya Sharma 提交最多(14 次),但她的最后接触是 2026-04-15,被标记为过时。去问 Ravi Menon。

修复不是靠更聪明的模型,而是不在本应有两个事实的地方只放一个数字。数量和时效是两条独立边的独立属性,合并它们是建模错误,不是智能问题。

带引用的矛盾

一份三月文档和一个三月 Slack 线程都说“单一供应商,Stripe”。而决策 D-19(2026-05-06)说“双供应商,Adyen 主,Stripe 备”。

两个来源都已过时,图谱可以证明这一点——因为 SUPERSEDES 是一条边,不是推理。这是嵌入向量做不到的事:嵌入可以告诉你这些文档相似,但无法告诉你其中一份已经被一个你能叫出名字的决策正式废弃。引用让过时变得可操作;没有这条边,“这个文档过时了”会让读者无法判断它到底推翻了哪个现行决策。

SUPERSEDES 边属于知识图谱中的时序/版本关系建模。它与普通的“相关”边有本质区别:相关边只表达语义接近,而 SUPERSEDES 表达“B 正式取代了 A”,从而让系统能回答“当前有效的是什么”而非仅仅“有哪些相似文档”。在向量检索中,两份内容相近的文档会得到几乎相同的嵌入,系统无法区分哪份已失效;而在图谱中,一条 SUPERSEDES 边就把“过时”从模糊判断变成了可遍历、可引用、可审计的事实。这也是为什么带引用的矛盾检测必须依赖图结构:引用需要指向一个具名的、可定位的取代者(如决策 D-19),否则“这份文档过时了”只是一句无法验证的断言。

最值钱的 bug:自己的验证门在说谎

项目进行到中途,他们的验证门报告四个必须失败的测试问题全部错误。而事实上,它们都是正确且完整的拒绝回答:

Q16 "图谱中不存在 config-service 的 OWNED_BY 边……"
     → 匹配了 8 种措辞中的 1 种
Q17 "图谱中不存在摘要提及定价的决策。"
     → 匹配了 10 种中的 1 种
Q20 "图谱中不存在 dispute-service 的 SLA 条目。"
     → 匹配了 7 种中的 1 种

每一个语义上都是完美的拒绝。每一个都被判失败。

原因是验证门要求“不”的八种拼写必须出现在同一个句子里。按 AND 逻辑读,它要求一句话说八遍“不不不不不不不”——没有一个正确答案会这么做。

他们发布了一个恰好朝浪费时间方向倒退的门。 它被优化为看起来严谨,同时精确惩罚了他们声称想要的行为。

更值得记录的是他们首先尝试的修复:扩大词汇表。结果通过率下降了。扩大 AND 列表严格来说会更难——每个新增词都是另一个合取项。

修复的是匹配规则,不是词汇表。

拒绝有很多拼写方式,没有一个答案包含全部。所以必须失败的用例采用 ANY_OF,而肯定性用例保持 AND——因为要证明你理解哪个决策是现行的,“D-19”和“取代”都必须出现,但“没有所有者”只需要一种诚实的表达。

这个教训可以推广到项目之外:当一个指标朝错误方向移动时,指标本身通常就是 bug。 他们差点去“修复”模型,而模型一直是好的。

验证门(verification gate) 是 CI/CD 中阻止不合格产物进入下一环节的自动化检查。在 AI 系统中,它通常表现为一组“必须通过/必须失败”的断言测试。设计验证门时最容易犯的错误是混淆两类逻辑:肯定性用例要求关键要素全部出现(AND),而否定性/拒绝用例只要求任一合理措辞出现即可(ANY_OF)。若对拒绝用例误用 AND,门就会要求一句话同时包含所有同义词,从而系统性判错正确输出。更隐蔽的是,这种错误会让指标朝“更严格”的方向移动,看起来像是质量提升,实则惩罚了系统应有的诚实行为。因此验证门本身也需要被测试——用已知正确的样本反向校验门是否会误杀,是防止“门在说谎”的必要手段。

其他被踩碎的假设

SSE 每一帧发送完整快照,不是增量。 处理器必须替换而不是追加。追加只在一帧内看起来正确,这正是它能通过随意测试、然后在生产环境复制整个答案轨迹的原因。协议假设就是代码。

六个图谱事实代理在结构上无法触及。 不是“答错了”,而是无法到达。Cypher 正确,遍历正确,但返回行的形状没有提供模型会走的路径。一个正确的查询藏在一个不可触及的表面后面,仍然不可触及。

工具结果触发了 413。 一个 6.5k token 的工具结果大到直接被拒绝。修复是返回轨迹而不是原始行——这反而成了更好的产品设计,因为轨迹正是人想看到的东西。

代理结果每次运行不稳定。 模型是采样的,单次扫描说明不了什么。任何从一次运行得出的结论——包括“修复生效了”——几乎毫无价值。这就是为什么标准答案集是固定且手写的:它不会随模型漂移。

前端与删除的教训

网站起步于另一个产品的逆向工程捕获,意味着继承了整个代码库——包括加载的 12 个第三方脚本,其中一个从他们的域名拉取,还有一个资源节省脚本在解析中途删除 DOM 节点,导致 hydration 不匹配。全部移除后:0 控制台错误。

真正硬核的教训来自删除操作。捕获的 HTML 有未闭合的 <div>,这意味着显而易见的做法——数 <div> 和 </div> 直到深度归零——会静默地越过元素的真实结尾。那一次删掉了页脚和四个完整章节。切割看起来平衡,没有任何东西报错。这是最糟糕的 bug 类型:失败不可见,直到你发现某个章节消失了。

修复是停止信任结构,开始检查它:用真正的 HTML 解析器推导范围,并且在哨兵计数(</main>、<footer> 等)存活之前拒绝写入。一个不会静默失败的删除,比一个通常正确的删除更有价值。

CSS 里也有对称的教训:他们试图通过分层捕获的样式表让工具类赢,按钮上完美工作,但把页面每个章节砍半,因为标记使用了两个样式表都拥有的通用类名。真正的机制一直是加载顺序。

Hydration 不匹配是服务端渲染(SSR)框架中的一类经典故障:服务器生成的 HTML 与客户端首次渲染的虚拟 DOM 不一致,框架在“接管”页面时检测到差异,轻则警告、重则丢弃并重渲染整棵子树。常见诱因包括第三方脚本在解析途中修改 DOM、浏览器扩展注入节点、以及依赖时间或随机数的渲染逻辑。文中“资源节省脚本在解析中途删除 DOM 节点”正是典型触发场景——服务器输出的结构与客户端预期结构对不上。移除这类脚本后控制台归零,说明 hydration 的稳定性高度依赖“服务端与客户端看到同一棵 DOM 树”这一隐含契约,任何破坏该契约的优化都会以难以定位的方式失败。

给后来者的几条建议

先写答案键。 当正确性在实现存在之前被定义,下游一切都会变便宜。

把“我不知道”当作有评分特性。 如果拒绝不被测量,模型就会因为猜测而得到奖励。

永远不要让一个数字代表两个事实。 数量/时效陷阱不是 AI 失败,而是多年前在 LLM 出现之前做出的数据建模选择。

指标坏了就别修模型。 他们的验证门一天内判错了四个正确答案,扩大词汇让情况更糟。模型一直是正确的。

让破坏性操作大声失败。 未闭合 div 和分层 CSS 的 bug 有相同根因:看起来正确,实际错误。两者都是通过问“我需要检查什么才能知道这是安全的”发现的,而不是靠看得更仔细。

所有数据均为合成。

#知识图谱#LLM验证#FalkorDB#企业搜索#AI代理#幻觉检测#Graph RAG
分享:

相关推荐