核心命題: 本體不是資料庫:它約束「可以說什麼」,不是「存了什麼」。 本文角度: 生產級取捨:OWL Profile 選型、SHACL 約束、身份解析、雙時間與 provenance、Agent 工具契約。
1. 進階版定位
入門版已講清楚 Class、Instance、Relation、Axiom、TBox/ABox、12 個設計維度與八步流程。進階版不再重複基礎,而是處理生產系統一定會遇到的問題:
- 表達力與推理成本怎樣取捨?
- OWL 與 SHACL 怎樣分工?
- 本體怎樣模組化、版本化、規模化?
- 多租戶、時間、身份解析、provenance 怎樣做?
- Agent 怎樣安全地使用本體,而不是把整個 ontology 塞進 prompt?
- 怎樣量化本體是否真的有用?
2. 形式化基礎:描述邏輯、OWL 2 與開放世界
2.1 Description Logic 的最小直覺
描述邏輯(DL)把領域拆成三種東西:
- Concept(概念):Account、JournalEntry
- Role(角色/關係):debits、posts
- Individual(個體):Account#1000、JE#20261021-001
DL 的價值是它有一套模型論語義,可以精確定義「什麼叫推論正確」。例如:
- CashAccount 是 AssetAccount 的子類,Account#1000 是 CashAccount,則 Account#1000 一定是 AssetAccount。
- 每張 JournalEntry 至少有一條 JournalLine;某張分錄沒有任何行,則模型不一致。
這就是 TBox(概念層)與 ABox(實例層)的分工。
2.2 OWL 2 的四個 Profile
OWL 2 不是單一語言,而是一組 profile。選錯 profile,通常不是「功能不夠」,而是「推理太慢」或「語義太弱」。
| Profile | 表達力 | 推理複雜度 | 適合 |
|---|---|---|---|
| OWL 2 EL | 中等(存在量化、交集) | 多項式時間 | 大規模分類、企業主數據 |
| OWL 2 QL | 較弱(查詢改寫友好) | 低 | 本體式資料存取、關聯式資料庫 |
| OWL 2 RL | 規則化(規則引擎) | 多項式時間 | 即時推理、資料整合 |
| OWL 2 DL | 高(SROIQ) | 高 | 複雜語義、醫療/學術本體 |
實務建議:
- 企業主數據、會計科目分類:先用 EL。
- 需要把本體映射到 SQL/關聯式查詢:用 QL。
- 需要規則引擎、即時推論:用 RL。
- 只有真正需要複雜公理(基數、逆關係、複雜角色)時才用 DL。
2.3 開放世界與唯一名稱假設
OWL 預設開放世界(OWA):沒有說過的,不代表不存在。 SQL/SHACL 通常是封閉世界(CWA):查不到,就是不存在。
還有一個常見陷阱:唯一名稱假設(UNA)。OWL 不預設不同 URI 一定是不同個體;如果你需要「兩個 ID 不同就是不同實體」,要在業務層或 SHACL 明確處理。
結論:
- 語義推理用 OWL(OWA)。
- 資料完整性用 SHACL/SQL(CWA)。
- 不要把兩者混為一談。
3. 約束工程:SHACL 進階
3.1 SHACL 的三層
- Node Shape:對某類節點做整體約束。
- Property Shape:對某個屬性的值做約束(datatype、minCount、pattern、in)。
- SPARQL Constraint:跨屬性、跨行的複雜約束。
3.2 借貸平衡:用 SPARQL Constraint
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <https://example.com/accounting#> .
ex:JournalEntryShape a sh:NodeShape ;
sh:targetClass ex:JournalEntry ;
sh:sparql [
sh:message "Debit total must equal credit total" ;
sh:severity sh:Violation ;
sh:select """
SELECT $this
WHERE {
$this ex:totalDebit ?d ;
ex:totalCredit ?c .
FILTER (?d != ?c)
}
"""
] .
這種約束比在 prompt 裡寫「請確保借貸平衡」可靠得多,因為它是可執行、可重跑、可審計的。
3.3 pySHACL 驗證
from pyshacl import validate
from rdflib import Graph
data = Graph().parse("data.ttl", format="turtle")
shapes = Graph().parse("shapes.ttl", format="turtle")
conforms, report_graph, report_text = validate(
data, shacl_graph=shapes, inference="rdfs", abort_on_first=False
)
print(conforms)
print(report_text)
3.4 SHACL 的邊界
- SHACL 不負責推論;它只驗證資料是否符合 shapes。
- SHACL 的 SPARQL constraint 功能強大,但效能取決於查詢與索引。
- 錯誤報告要能落到業務物件與欄位,否則使用者看不懂。
4. 查詢與推理:SPARQL、Materialization、Incremental
4.1 SPARQL 的三種形態
- SELECT:取表格資料(報表、對賬)。
- ASK:是否存在符合條件的資料(權限、校驗)。
- CONSTRUCT:把查詢結果生成新的 RDF 圖(ETL、推理)。
例子:查某期間的借方合計。
SELECT ?account (SUM(?debit) AS ?totalDebit)
WHERE {
?entry a ex:JournalEntry ;
ex:inPeriod ?period ;
ex:posts ?line .
?line ex:debits ?account ;
ex:amount ?debit .
?period ex:code "2026-10" .
}
GROUP BY ?account
ORDER BY DESC(?totalDebit)
4.2 推理策略
| 策略 | 做法 | 優點 | 缺點 |
|---|---|---|---|
| Materialization | 預先把推論結果寫入圖 | 查詢快 | 寫入慢、儲存大 |
| Query Rewriting | 查詢時改寫成 SQL/SPARQL | 儲存小 | 查詢複雜 |
| Incremental | 只重算受影響部分 | 平衡 | 實作難 |
| Rule Engine | 用 OWL RL/業務規則 | 可解釋 | 規則爆炸 |
實務建議:
- 讀多寫少:materialization + 增量更新。
- 寫多讀少:query rewriting 或規則引擎。
- Agent 場景:只取子圖,局部推理,避免全局 materialization。
4.3 推理的邊界
推理可以幫你分類、檢查一致性、推導間接關係;但不能判斷資料真實性、處理模糊語言、替代業務規則、保證即時性。
5. 模組化與本體模式
5.1 四層模組
- Core Ontology:Entity、Party、Event、Time、Document 等通用概念。
- Domain Modules:Accounting、Banking、Tax、HR;每個模組獨立版本。
- Application Profiles:給 Agent/報表用的子集+JSON Schema+SHACL。
- Mapping Layer:DB/API/PDF/Excel/Bank 的映射。
好處:
- 每個模組可以獨立測試、獨立版本。
- Agent 只加載需要的 application profile,不會被全局 ontology 淹沒。
- 不同客戶可以共享 core,替換 domain/application。
5.2 本體模式(ODP)
- Role Pattern:Party 可以同時是 Customer、Supplier、Employee;用 Role 而不是多個子類。
- Event Pattern:Event → Actor → Object → Time;適合交易、審計、日誌。
- Time Pattern:ValidFrom/ValidTo;不要覆蓋歷史。
- Provenance Pattern:Entity → Activity → Agent(W3C PROV)。
- N-ary Relation:用中間節點表示三方以上的關係,例如「合約—物業—租客—期間」。
5.3 版本與 import
- 每個模組有 semantic version(1.2.0)。
- 用 owl:imports 組合,但不要讓 import 形成循環。
- 變更要記錄:改了什麼、為何改、影響哪些 CQ。
- 向後兼容:新增可選屬性可以;刪除/改名要有 migration。
6. 身份解析:本體落地的關鍵
同一個供應商可能有多個名稱、多個 ID、多個銀行帳戶。沒有 identity resolution,本體只會把重複實體越建越多。
6.1 管線
- Ingest:多來源(DB、發票、銀行、Excel)。
- Normalize:去除空格、全半角、公司後綴(Ltd/Limited/有限公司)。
- Blocking:用規則縮小候選(稅號前 6 位、名稱首字、地址郵區)。
- Scoring:名稱相似度、稅號、地址、銀行帳戶、embedding。
- Merge/Review:高分自動合併;中分人工確認;低分不合併。
- Canonical ID:產生唯一 ID,保留 aliases 與 merge history。
6.2 評分公式(示例)
score = 0.35 * name_sim
+ 0.25 * tax_id_match
+ 0.20 * address_sim
+ 0.15 * bank_account_match
+ 0.05 * embedding_sim
門檻示例:
- score >= 0.92:自動合併
- 0.75 ≤ score < 0.92:人工確認
- score < 0.75:不合併
6.3 合併與拆分
- 合併要保留 merge_history:誰合的、何時、依據。
- 拆分要能回復:哪個 alias 被移到哪個新實體。
- 所有合併/拆分都要寫 audit log。
7. 時間、版本與 Provenance
7.1 Bitemporal
- Valid Time:這個事實何時在現實世界有效(例如租金合約 2026-01-01 至 2026-12-31)。
- Transaction Time:這個事實何時被系統記錄(例如 2026-10-21 匯入)。
兩者分開,才能回答:
- 「2026-06-30 當時,我們認為這份合約的有效期是什麼?」
- 「我們在 2026-10-21 才發現地址改了,之前記錄的是什麼?」
7.2 PROV-O
每個事實要能回答:
- Entity:哪份文件/哪個資料列?
- Activity:哪個提取/轉換過程?
- Agent:哪個 Agent/人確認?
- Timestamp:何時?
這對會計、審計、合規尤其重要。
7.3 Event Sourcing
與其只存「當前狀態」,不如存「事件流」:
- DocumentReceived
- ExtractionCompleted
- JournalDrafted
- JournalApproved
- JournalPosted
- JournalReversed
每個事件都可重放,能重建任何時間點的狀態。
8. 多租戶、安全與治理
8.1 隔離模型
- TBox 可以共享。
- ABox 必須按 tenant 隔離:每個 tenant 一個 named graph/schema/namespace。
- 查詢層強制 tenant filter;不要只靠應用層。
- 權限要落到概念/屬性/實例級別。
8.2 安全
- PII 欄位標記與遮罩。
- 審計日誌 append-only。
- 匯出/匯入要有權限與期限。
- Agent 的工具調用要經過授權與參數驗證。
8.3 治理
- Ontology owner:誰有權改?
- Change advisory:誰審?
- 版本與 deprecation policy。
- 定期質量審計:CQ 覆蓋、SHACL 通過率、實體解析 F1。
9. 規模與效能
9.1 儲存選型
| 類型 | 優點 | 缺點 | 適合 |
|---|---|---|---|
| RDF Store(GraphDB/Stardog/Jena) | 標準、推理、SPARQL | 運維成本 | 語義層、資料整合 |
| Property Graph(Neo4j) | 查詢快、生態好 | 語義較弱 | 關係分析、推薦 |
| Relational + JSON | 成熟、事務強 | 圖查詢較弱 | 交易系統、MVP |
9.2 效能技巧
- 只取子圖:Agent 不需要整個 ontology。
- 索引:entity ID、type、relation、tenant。
- 分區:按 tenant/時間/domain。
- 緩存:concept lookup、subgraph、validation result。
- 避免全局 materialization;用增量或查詢改寫。
9.3 基準測試
- 查詢 P50/P95/P99
- 驗證吞吐量(shapes/sec)
- 推理時間
- 圖規模(triples/nodes/edges)
- 更新延遲
10. Agent 進階整合:工具契約,不是 Prompt 堆砌
10.1 三個工具+一個閘門
- search_concepts(query):按名稱/同義詞找概念。
- get_subgraph(concept_id, depth, profile):取相關子圖。
- validate_payload(schema_id, payload):用 JSON Schema/SHACL 驗證。
- Pre-send gate:發送前檢查 ledger/權限/重複。
10.2 多 Agent 共享
- 所有 Agent 用同一套 concept ID、relation、constraint。
- 共享 ontology service,不要各自維護一份。
- 共享 audit log,讓每個決策可追溯。
10.3 幻覺控制
- 強制工具參數 schema 驗證。
- 重要結論要求引用 ontology path/source。
- 低信心 → 例外,不自動執行。
- 不把 ontology 全文塞進 prompt;用子圖+例子。
10.4 RAG vs Ontology
| 需求 | RAG | Ontology |
|---|---|---|
| 找相關文件 | 強 | 弱 |
| 實體解析 | 弱 | 強 |
| 關係推理 | 弱 | 強 |
| 參數驗證 | 弱 | 強 |
| 可審計 | 中 | 強 |
最佳組合:RAG 找資料,本體約束語義,Agent 做決策。
11. 評估框架
11.1 離線指標
- CQ 覆蓋率:能回答的 CQ/總 CQ
- SHACL 通過率:合規實例/總實例
- 一致性:unsatisfiable class 數、邏輯衝突數
- 實體解析 F1:precision/recall
- 查詢延遲:P50/P95
- 維護成本:變更 lead time、回歸缺陷數
11.2 線上指標
- Agent 任務成功率
- 人工例外率
- 平均處理時間
- 使用者修正率
- 審計可追溯率
11.3 漂移監控
- 概念使用率下降 → 可能過時
- 新實體類型大量出現 → 需要擴展
- SHACL 失敗率上升 → 資料源或規則變了
- 實體解析 merge 錯誤上升 → 需要調整門檻
12. 高級案例:AutoLedger 會計本體 v1.0
12.1 核心公理
- 每張 JournalEntry 的借方合計 = 貸方合計(本位幣)。
- JournalLine.account 必須存在且 is_active。
- JournalEntry.date 必須落在 open period。
- 外幣分錄必須有 fx_rate > 0,並存原幣與本位幣。
- 每個 JournalLine 必須有 source_document 或 source_ref。
12.2 OWL 片段
Prefix(:=<https://example.com/accounting#>)
Ontology(<https://example.com/accounting>
SubClassOf(:CashAccount :AssetAccount)
SubClassOf(:AssetAccount :Account)
ObjectPropertyDomain(:debits :JournalLine)
ObjectPropertyRange(:debits :Account)
FunctionalObjectProperty(:hasPeriod)
)
12.3 SPARQL 對賬查詢
SELECT ?bankTx ?journalLine
WHERE {
?bankTx a ex:BankTransaction ;
ex:amount ?amt ;
ex:date ?date .
?journalLine a ex:JournalLine ;
ex:amount ?amt ;
ex:date ?date .
FILTER NOT EXISTS { ?bankTx ex:matchedTo ?journalLine }
}
12.4 事件流
DocumentReceived -> ExtractionCompleted -> JournalDrafted
-> JournalApproved -> JournalPosted -> JournalReconciled
每個事件都記錄 actor、timestamp、payload hash;可重放、可審計。
13. 反模式與修復
| 反模式 | 症狀 | 根因 | 修復 |
|---|---|---|---|
| 巨型本體 | 沒人敢改 | 沒有模組化 | 拆 core/domain/application |
| Schema 當本體 | 只有表,沒有語義 | 把 DB schema 抄過來 | 加 CQ、關係、約束 |
| 沒有 CQ | 建模發散 | 為建模而建模 | 先寫 20 條 CQ |
| 用 OWL 做驗證 | 推理慢、結果不確定 | 混淆 OWA/CWA | 用 SHACL/JSON Schema |
| 沒有身份解析 | 同一實體多個 ID | 缺 blocking/scoring | 建 identity pipeline |
| 沒有時間 | 覆蓋歷史 | 只存當前值 | bitemporal/event sourcing |
| 沒有 provenance | 無法審計 | 只存結果 | PROV-O/source ref |
| 沒有版本 | 語義漂移 | 無 owner | semantic version+change log |
| 整個本體塞 prompt | context 爆炸 | 把 ontology 當文件 | 子圖+工具 |
| 多 Agent 各自維護 | 結論矛盾 | 沒有共享 service | 統一 ontology service |
14. 90 天進階落地計劃
14.1 第 1–4 週:形式化與 CQ
- 選 OWL profile(EL/QL/RL)
- 寫 30 條 CQ
- 建 core+accounting 模組
- 用 Protégé 檢查一致性
14.2 第 5–8 週:約束與身份
- 建 SHACL shapes(必填、值域、跨行)
- 建 identity resolution pipeline
- 加 provenance
- 用 pySHACL 接入 CI
14.3 第 9–12 週:Agent 與評估
- 三個 ontology tools
- pre-send gate+audit
- 評估 CQ 覆蓋、SHACL 通過、Agent 成功率
- 版本 v1.0+runbook
15. 練習題(Lab)
15.1 Lab 1:寫 CQ
為「銀行對賬」寫 5 條 CQ,並說明每條需要哪些概念/關係。
參考答案:
- 哪些銀行交易未匹配?→ BankTransaction、matchedTo
- 哪些分錄有多個銀行交易?→ JournalLine、BankTransaction
- 某月未匹配金額合計?→ amount、date、status
- 同一參考號出現兩次?→ reference、count
- 哪些交易跨期?→ date、Period
15.2 Lab 2:選 OWL Profile
你的系統需要大規模科目分類、查詢快、偶爾推理。選哪個?
參考答案:OWL 2 EL;如果主要是查詢改寫,選 QL;如果需要規則引擎,選 RL。
15.3 Lab 3:寫 SHACL
寫一個 shape,要求 JournalLine.amount > 0 且 account_code 必填。
參考答案:
ex:JournalLineShape a sh:NodeShape ;
sh:targetClass ex:JournalLine ;
sh:property [
sh:path ex:amount ;
sh:datatype xsd:decimal ;
sh:minExclusive 0
] ;
sh:property [
sh:path ex:accountCode ;
sh:minCount 1 ;
sh:datatype xsd:string
] .
15.4 Lab 4:SPARQL
查 2026-10 期間每個科目的借方合計。
參考答案:見第 3.1 節查詢。
15.5 Lab 5:Identity Resolution
兩個供應商名稱相似但稅號不同,應否合併?
參考答案:不應自動合併;稅號是強標識,應進入人工確認,並保留兩個 canonical ID。
16. 結語:本體是生產系統的語義層
進階本體設計的核心不是「畫得更複雜」,而是:
- 用最弱的表達力滿足 CQ
- 用 SHACL 做可執行驗證
- 用模組化控制複雜度
- 用 identity/time/provenance 保證可信
- 用工具契約讓 Agent 安全使用
- 用指標證明它真的有用
三句話總結:
- 表達力是成本,不是榮耀。
- 約束要可執行,不要只寫在 prompt。
- 本體要能被維護,才有資格談「精通」。
17. 附錄:標準、工具與閱讀
- 標準:RDF 1.1、RDFS、OWL 2(EL/QL/RL/DL)、SHACL、SKOS、JSON-LD、SPARQL 1.1、PROV-O
- 方法論:Ontology 101、NeOn、METHONTOLOGY、Competency Questions、Ontology Design Patterns
- 工具:Protégé、TopBraid Composer、GraphDB、Stardog、Apache Jena、pySHACL、Neo4j
- 延伸:schema.org、BFO、SUMO、Palantir Ontology、W3C Data on the Web Best Practices
進階不是把模型變大,而是把模型變得更可計算、更可驗證、更可運維。
下一步
選一條你真實會問的 CQ,然後:
- 用最弱的 OWL Profile 建模,先確認推理機能跑;
- 為它寫 SHACL Shape,用 pySHACL 跑一次
Conformance; - 量三個數字:SHACL 通過率、P95 查詢延遲、Agent 任務成功率;
- 把結果寫成一段 5 行的驗收記錄,貼回本體倉的 README。
本體不是寫完就算,是要能被驗收。