◈本体论工程手册
方法论

四 · 推理与校验

四类推理、复杂度、三层校验

推理与校验常被当成同一件事的两面,它们其实是两件相反的事:推理从已有事实里导出更多事实,校验从已有事实里找出不合规的那些。 一个只会让数据变多,一个只会说不。把它们装进同一个流程,是设计上的关键决定。

四类推理,复杂度相差六个数量级

类机制代表实现时间复杂度什么时候用
RDFS 蕴含固定规则:子类传递、定义域值域rdflib、Jena、OWL-RL准线性只需要类型继承与属性传递
OWL 2 RL前向链规则闭包OWL-RL(Python)、Reasonable(Rust)多项式,可增量规则形态清晰、想避免 tableau
OWL 2 ELconsequence-based,专为分类优化ELK多项式(PTime)类数量极大、只需分类
OWL 2 DLtableau,可判定但重HermiT、Openllet、Konclude最坏 2-NEXPTIME需要完整表达、能接受不可预测的耗时

这张表最实用的读法是最后两行:EL 与 DL 的差别不是「表达力多一点」,而是「能不能预测耗时」。 一个在测试数据上 200ms 完成的 DL 推理,在生产数据上可能跑 40 分钟,而且你不知道为什么。EL 的耗时是可估计的,这是它被选来跑 SNOMED(数十万类)的真正原因。

一个必须记住的事实:推理机不会报错

第十一章有实测记录,这里先把结论放前面。

同一份数据、同一批约束,用两种方式处理:

  • SHACL 校验:报出 5 条违规,判定不通过,进程退出码非零。
  • OWL-RL 推理:零报错,新增 217 条三元组,进程退出码零。

这不是 bug,是两种范式的定义差异:

  • OWL 是开放世界的:数据里没写的,是「未知」而不是「错误」。因此不存在「不合规」这个概念,只有「能推出什么」。
  • SHACL 是闭合世界的:数据里没写的,就是缺失。因此它可以判定不合规。

所以如果有人告诉你「我们用 OWL 做了数据质量校验」,正确的追问是:你用的那个推理机,在数据缺字段时会返回什么? 答案通常是「什么也不返回」,然后这个校验就被悄悄降级成了一份没人看的报告。

三层校验,缺一层就漏一类错

工程上,校验应该分三层,且三层都要有非零退出码:

层检查什么手段典型错误
语法层文件能不能解析、IRI 是否合法、前缀是否声明rdflib parse、jena riot拼写错误、编码错误、重复定义
结构层必填、类型、基数、枚举、跨字段条件SHACL Core + SPARQL 约束缺 owner、单位不一致、状态值越界
语义层类层次是否一致、有没有不可满足的类、定义域值域冲突推理机 + 一致性检查一个类既属于 A 又属于 disjoint 的 B

三层的分工很明确:语法层防手滑,结构层防业务错误,语义层防建模错误。大多数团队只做了第一层和第三层的一半——语法检查是工具自带的,一致性检查偶尔跑一次,而真正会产生业务损失的结构层校验反而缺失。

校验的四条工程要求

一、必须在写入路径上,或者至少在合并路径上。 事后扫描的价值随时间衰减。数据进入下游七个系统之后再发现问题,修复成本是写入时的百倍。

二、必须有明确的严重级别。 把所有违规都当错误,会导致规则被整体放宽;把所有违规都当警告,等于没有。实践中可用的三档:

  • 错误:阻断,必须修。
  • 警告:不阻断,但计入趋势,连续两周上升要处理。
  • 提示:只记录,供建模者参考。

三、必须允许「带理由的豁免」。 真实数据总有历史遗留。如果规则不允许例外,团队会关掉规则。可行的做法是:豁免必须是一条显式记录,含对象 ID、规则 ID、理由、到期时间。到期未修,豁免自动失效、违规重新出现。没有到期时间的豁免等于永久删除规则。

四、必须先给人看,再自动修。 自动修复的问题不是修错,而是没人知道它修了什么。至少在第一年,违规报告应该以人能读懂的形式呈现:哪个对象、哪条规则、原文怎么说、为什么不合规。工具里那些「一键全修复」的按钮,建议在团队建立直觉之前不要开。

推理该放在哪

推理有三个合理的放置位置,收益依次递减:

  1. 构建期:建模时跑一次分类,检查层次是否合理、有没有不可满足的类。这是收益最高、风险最低的用法——错了只影响建模决策。
  2. 查询期:把推理结果当查询的一部分(例如 OBDA 的查询重写)。收益高,但对性能与可预测性要求高,适合 QL 这类为下推设计的 profile。
  3. 写入期:每次写入都跑推理,把推论物化。收益中等,风险最高——推理失败会阻断写入,而推理失败的诊断信息通常对业务方毫无意义。

推断:绝大多数团队应该只做第 1 种,按需做第 2 种,避免第 3 种。 把推理放在写入路径上,等于把一个不可预测耗时的组件放进了事务里。

一个实测能说明的边界

实测里还有一条发现值得单独记:**OWL 2 RL 的规则集只包含 maxCardinality 为 0 和 1 的基数限制。** 这意味着:

  • owl:maxCardinality 1(至多一个)可以推。
  • owl:minCardinality 1(至少一个)推不出来。
  • owl:cardinality 1(恰好一个)只能推出「至多一个」那一半。

「至少一个」是业务里最常见的约束(至少一个负责人、至少一条审批记录)。它在 OWL 2 RL 里不存在,这解释了为什么那么多「用 OWL 做校验」的尝试最终都回到了 SHACL。

结论:两者不是替代关系

正确的组合是:

  • SHACL 负责「不许错」,挂在写入或合并路径上,有退出码,有严重级别,有带期限的豁免。
  • EL/RL 推理负责「帮着看懂」,在构建期跑,用来检查层次与发现意外推论。
  • 两者共用一份模型,但不要指望一份文件同时承担两种语义。同一个类,在 OWL 里是「由公理定义的集合」,在 SHACL 里是「一组必须有/必须没有的属性」——这两个意思不一样,硬塞进一个文件会得到两边都不彻底的结果。

实践中可行的做法是:模型用 LinkML 一类的中立格式写,编译出 OWL 与 SHACL 两份产物,各司其职。这也是第三章里那个「一份源、多目标编译」判断的由来。