P3 · 宠物疾病三元组推理系统
P3 · 宠物疾病三元组推理系统
多范式推理实战营 · 项目 3/6
副标题:第三种范式——前向链规则 + SPARQL 查询一体化
作者:森林瀑布 |博客:senlinpubu.top |最后更新:2026-06-22
一、引言:为什么需要第三种范式?
P1 用 OWL 描述逻辑(HermiT Tableau 算法),P2 用 Horn 子句逻辑(Prolog SLD 归结)。两者都是"纯推理机"——推理和查询是分开的两步。
但企业实践中,知识往往存储在数据库里,需要通过标准查询语言访问。P3 展示的正是这种范式:
把推理内置到查询管线里——SPARQL 查询时自动包含推理结果。
这是 Jena Fuseki 的核心价值:三元组数据库 + 前向链规则引擎 + SPARQL 查询一体化。
二、技术架构:前向链 + SPARQL 一体化
┌─────────────────────────────────────────────────┐ │ 输入:病例(症状列表) │ │ 例:["发热", "呕吐", "腹泻"] │ └──────────────────────┬──────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ Step 1:SPARQL UPDATE(断言症状三元组) │ │ INSERT DATA { :case :has :发热, :has :呕吐 } │ └──────────────────────┬──────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ Step 2:Jena 前向链自动触发(数据驱动) │ │ 规则2 → :case :suspected :d001, :d003, ... │ │ 规则3 → :case :excluded :d002, :d003, ... │ │ 规则4 → :case :diagnosed :d001, :d004 │ │ (预计算所有推断结果,存入图) │ └──────────────────────┬──────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ Step 3:SPARQL 查询(自动包含推理结果) │ │ SELECT ?d WHERE { :case :diagnosed ?d } │ │ → 直接返回预计算的推断结果 │ └──────────────────────┬──────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ Step 4:清理临时三元组(DELETE WHERE) │ └─────────────────────────────────────────────────┘2.1 前向链 vs 反向链
| P2 Prolog(反向链) | P3 Jena(前向链) | |
|---|---|---|
| 触发方式 | 用户提问触发 | 数据变更触发 |
| 计算时机 | 查询时按需计算 | 数据加载时预计算 |
| 计算范围 | 只算被问到的 | 算所有可能的 |
| 查询速度 | 慢(实时计算) | 快(读预计算结果) |
| 写入速度 | 快(不计算) | 慢(触发规则链) |
| 类比 | 按需计算 | 物化视图 |
工程取舍:P3 适合"读多写少"的场景——知识库不常变,但查询频繁。每次写入触发前向链预计算,后续查询直接读结果。
2.2 SPARQL 查询推理一体化
P3 最独特的能力——SPARQL 查询自动包含推理结果:
-- :diagnosed 不是手工断言的,是 Jena 规则推断的 -- 但 SPARQL 查询时它和手工断言的三元组完全一样 SELECT ?disease ?label WHERE { :case_temp :diagnosed ?disease . ?disease rdfs:label ?label . }对比 P1 和 P2:
| P1(HermiT) | P2(Prolog) | P3(Jena) | |
|---|---|---|---|
| 推理与查询 | 分开:先sync_reasoner(),再cls.instances() | 一体:prolog.query()同时推理 | 一体:SPARQL 查询自动含推理结果 |
| 查询语言 | owlready2 API | Prolog 语法 | SPARQL(W3C 标准) |
P3 的 SPARQL 是 W3C 标准查询语言,意味着任何 SPARQL 客户端都能查询推理结果——跨语言、跨平台互操作。
2.3 知识表示:RDF 三元组
# 疾病个体(注意:P3 中疾病是个体,不是类) :d001 rdf:type :疾病 ; rdfs:label "猫瘟" ; :necessary :发热, :呕吐, :腹泻 ; :nos :咳嗽, :流鼻涕 . # 症状个体 :发热 rdf:type :症状 ; rdfs:label "发热" .对比三种知识表示:
| P1 OWL | P2 Prolog | P3 RDF | |
|---|---|---|---|
| 疾病 | 类(Class D001) | 谓词disease(d001,...) | 资源:d001 rdf:type :疾病 |
| 症状 | 个体(Individual) | 原子'发热' | 资源:发热 rdf:type :症状 |
| 关联 | equivalent_to公理 | necessary/2事实 | :necessary三元组 |
| 格式 | OWL/RDF XML | .pl 文本 | Turtle(.ttl) |
2.4 Jena 自定义规则
# 前向链规则(数据加载时自动执行) [rule_suspected: (?case :has ?symptom), (?disease :necessary ?symptom), (?disease rdf:type :疾病) -> (?case :suspected ?disease) ] [rule_diagnosed: (?case :suspected ?disease), noValue(?case :excluded ?disease) -> (?case :diagnosed ?disease) ]noValue是 Jena 的 CWA 否定——图中没有excluded三元组就认为不存在。对比 P1 的 OWA(未断言 ≠ 不存在)和 P2 的\+(negation as failure)。
三、运行示例
输入与 P1/P2 相同的病例:
case={"pet_type":"cat","symptoms":["发热","呕吐","腹泻"],"breed":"英短","age":2}输出:
────────────────────────────────────────────────── 📋 诊断结果(Jena 前向链 + SPARQL) ────────────────────────────────────────────────── 1. 猫瘟 置信度:1.00 ██████████ ✅确诊 2. 犬细小病毒 置信度:0.67 ██████ ⚠️疑似 ────────────────────────────────────────────────── 🔗 传递闭包(Jena 前向链预计算): 猫感冒 → 猫瘟 犬感冒 → 犬细小三种范式结果对比
| 疾病 | P1(HermiT) | P2(Prolog) | P3(Jena) |
|---|---|---|---|
| 猫瘟 | 0.99(SWRL 补充) | 1.00 ✅确诊 | 1.00 ✅确诊 |
| 犬细小 | 0.77(SWRL 补充) | 0.67 ⚠️疑似 | 0.67 ⚠️疑似 |
| 推理方式 | OWL 分类 + SWRL | Prolog SLD 归结 | Jena 前向链 + SPARQL |
| 否定语义 | OWA | CWA(\+) | CWA(noValue) |
四、与 LLM 方案对比
| 维度 | LLM 方案 | P3 Jena 方案 |
|---|---|---|
| 输入 | 自然语言 | symptoms: ["发热", "呕吐", "腹泻"] |
| 输出 | 「可能是猫瘟,建议就医」 | 猫瘟(1.00 ✅确诊) |
| 推理链 | ❌ 黑盒 | ✅ 前向链规则可追溯 |
| 查询语言 | 自然语言 | SPARQL(W3C 标准) |
| 数据持久化 | ❌ | ✅ 三元组数据库 |
| 跨系统互操作 | ❌ | ✅ SPARQL 端点(HTTP API) |
| 耗时 | 2-5 秒 | <0.1 秒(预计算) |
五、P1 / P2 / P3:什么时候用哪个?
| 场景特征 | 推荐范式 | 原因 |
|---|---|---|
| 需要分类推理、一致性检查 | P1 OWL | Prolog/Jena 不做 satisfiability |
| 开放世界更合理(医疗、法律) | P1 OWL | “没记录” ≠ “没有” |
| 需要递归推理 | P2 Prolog | OWL/Jena 递归能力弱 |
| 规则密集(350+ 条) | P2 Prolog | Prolog 规则表达力强 |
| 需要标准查询语言 | P3 Jena | SPARQL 是 W3C 标准 |
| 需要数据持久化 | P3 Jena | 三元组数据库(TDB2) |
| 读多写少(知识库不常变) | P3 Jena | 前向链预计算,查询快 |
| 需要跨系统互操作 | P3 Jena | HTTP + SPARQL 端点 |
| 需要形式化保证 | P1 OWL | Prolog/Jena 无完备性保证 |
实践中常组合使用:用 OWL 管理领域概念体系,用 Prolog 执行复杂规则,用 Jena Fuseki 提供标准化的查询服务。
教学效果:从代码到原理
本项目作为《当 LLM 不够用了》的配套实战案例,对应以下章节:
| 本书章节 | 本项目对应内容 |
|---|---|
| 第一章 本体论是什么 | RDF 三元组 vs OWL 类层次——两种知识建模范式的底层统一 |
| 第四章 本体推理的技术基础设施 | 前向链规则引擎与 SPARQL 查询一体化——推理即查询 |
| 第五章 企业级本体推理架构设计 | Jena Fuseki 三元组数据库——知识的持久化与标准化查询 |
| 第九章 打造你的第一条企业决策推理链 | P1/P2/P3 场景决策树:「什么时候用 OWL,什么时候用 Prolog,什么时候用 Jena」 |
学习路径:
- 第一周:先跑通 P1 和 P2,理解两种范式后再看 P3——体会「推理 + 查询一体化」的价值
- 第二周:掌握 SPARQL 查询语法——W3C 标准,跨语言跨平台
- 第三周:理解前向链预计算 vs 反向链实时计算——工程中的读/写权衡
- 第四周:尝试用 Jena Fuseki HTTP API 从外部程序查询推理结果
六、总结
P1/P2/P3 构成了"多范式推理实战营"的核心三部曲——同一问题,三种范式,各有取舍:
| P1 | P2 | P3 | |
|---|---|---|---|
| 逻辑 | 描述逻辑 | Horn 子句 | 前向链规则 |
| 假设 | OWA | CWA | CWA |
| 方向 | 分类(Tableau) | 反向链(SLD) | 前向链 |
| 时机 | 查询前 | 查询时 | 数据加载时 |
| 查询 | API | Prolog | SPARQL |
| 持久化 | 文件 | 内存 | 三元组数据库 |
| 完备性 | ✅ | ❌ | ❌ |
| 标准 | OWL(W3C) | Prolog(ISO) | RDF/SPARQL(W3C) |
「让 OWL 做知识治理,让 Prolog 做规则推理,让 Jena 做查询服务,让 LLM 做知识工程。」
—— 《当 LLM 不够用了——本体推理的企业决策实践》
项目链接:github.com/georgewangchn/OntologyOps/tree/main/ontologyops/examples/P3
P1 对比:P1 · OWL 本体推理
P2 对比:P2 · Prolog 规则推理
书籍全书:《当 LLM 不够用了——本体推理的企业决策实践》在线阅读