把健保給付條文,
變成可以驗證的 CQL

醫師逐條審核,CQL 引擎逐條驗證。

審核頁示意:左側是條文清單,中間是 AND/OR 判定邏輯,右側是條件 C3 的原文出處、證據契約與接受、退回、不適用三個決定。
14 天LLM 的 prompt 與回應留存可查
3 值true · false · null,缺資料不猜
4 次每條條文的修正預算,三段共用
292 + 77backend 與 frontend 測試
管線

七個步驟,每一步之間都有程式檢查

引文要在原文裡、節點定義要合法、整份 IR 要通過 validateIr。哪一塊出錯,就只修那一塊。

  1. 01匯入貼上條文,或上傳 PDF、docx,取出要分析的條文。
  2. 02拆解逐字引文 → 原子條件 → AND/OR/NOT 群組,分三段擷取。
  3. 03覆核每個條件一次小呼叫,只帶它引用的引文,對照原文並正規化數值。
  4. 04審核醫師逐條件接受、退回或標為不適用,再做整條簽核。
  5. 05產生每個條件一個 Boolean parameter,defines 組合邏輯,編譯成 ELM。
  6. 06驗證逐一覆寫 parameter,用 cql-execution 執行,和 IR 三值邏輯的結果比對。
  7. 07匯出CQL、ELM、證據契約與 fixtures,以條文或整份文件打包。
給審核者 →

每個條件,都對得回原文

醫師不必讀 CQL。畫布上是條件、群組和原文出處;有疑問的條件可以單獨重跑,改過的邏輯會讓相關簽核失效。

01 · 引文驗證

每個條件都指回原文

LLM 先抄下逐字引文,再從引文拆出條件。找不到逐字對應的引文,會標成待核,不會自動通過。

閱讀文件 →
查得到的依據比對時忽略空白與全半形;修不好的引文記成「N 筆引文待核」,分析不會因此失敗。
02 · 單條件重新分析

只重跑有疑問的條件

覺得某個條件拆錯了,可以只對它重新呼叫 LLM。結果走一般的邏輯修改流程,其他條件不受影響。

閱讀文件 →
查得到的依據被改動的條件,審核決定標為失效;沒動到的條件,決定保留。
03 · 邏輯畫布

在畫布上改邏輯

編輯、新增、移動條件,群組在 AND/OR/NOT 之間切換。一存檔,CQL 重新產生、消融驗證重跑。

閱讀文件 →
查得到的依據存檔帶 baseHash:期間有人先存,就回 409,不會互相覆蓋。
給工程與資料團隊 →

IR 是唯一來源,產生的 CQL 實際執行驗證

CQL 由 IR 產生,不手寫。LLM 的每一段輸出都先過程式檢查;邏輯一改,就重新產生、重新驗證。

04 · renderCql

CQL 由 IR 產生

每個原子條件一個 Boolean parameter,群組變成 define。不需要 FHIR retrieve 就能評估,證據由外部 adapter 填入。

讀懂產生的 CQL →
查得到的依據邏輯一存檔,CQL 重新產生、消融驗證重跑;畫面上看到的就是會匯出的。
05 · 消融驗證

每個情境都實際執行一次

cql-engine 把 CQL 編譯成 ELM,cql-execution 逐一覆寫 parameter 執行;每個結果都要和 IR 三值邏輯的預期一致,才算通過。

讀懂產生的 CQL →
查得到的依據證據不足時回 insufficient-data,而不是猜一個答案。
06 · 定點修正

只修出錯的那一塊

拆解分三段擷取:引文、條件、群組。每段都先過程式檢查,出錯的部分有專屬的修正步驟,其他部分不重跑;一條條文最多修 4 次,三段共用。

讀懂產生的 CQL →
查得到的依據截斷的 JSON 絕不自動補括號;解析失敗就把錯誤位置回丟給模型重問。
CQL 入門 →

四個部分,讀懂一份 CQL

工作台匯出的 CQL 都是同一個結構。下面是範例條文產生的邏輯,每一段旁邊都有說明。

Clause_Sample.cqlrenderCql 產出 · 節錄
library Clause_Sample version '1.0.0'

using FHIR version '4.0.1'
1

標頭

library 是這份邏輯的名稱和版本;using FHIR 宣告它用 FHIR R4 的資料模型。

parameter "C1" Boolean default null
parameter "C2" Boolean default null
parameter "C3" Boolean default null
parameter "C4" Boolean default null
2

輸入:一個條件一個參數

C1–C4 各是一個 Boolean 參數,預設是 null。證據轉接器查過病歷後填入 true 或 false,查不到就維持 null。

context Patient

define "Criterion C1":
  "C1"
// Criterion C2–C4 follow the same pattern

define "Group G1":
  "Criterion C1"
  and "Criterion C2"
  and "Group G2"

define "Group G2":
  "Criterion C3"
  or "Criterion C4"

define "Eligibility":
  "Group G1"
3

邏輯:把條件組起來

define 替一段運算取名字。and、or 的組法和審核畫面上的 G1、G2 一樣。context Patient 表示每個 define 都針對一位病人計算。

define "Eligibility Status":
  if "Eligibility" is true then 'eligible'
  else if "Eligibility" is false then 'ineligible'
  else 'insufficient-data'
4

結果:三種狀態

用 is true、is false 分出符合、不符合和資料不足。缺資料不會被算成「不符合」。

遇到 null 時true and null → nullfalse and null → falsetrue or null → true完整的 CQL 入門 →
治理

上線之後也管得住

一次簽核只對應一個版本的邏輯。誰在什麼時候改了什麼,都查得到。

  • 三種角色admin、reviewer、viewer。viewer 看得到全部頁面,但不能分析、簽核或改邏輯。
  • 修改即失效被改動的條件與整條簽核標為失效,條文狀態依剩下的決定重新推導。
  • 鎖定與退回已核准的條文由 reviewer 退回重審;已鎖定的只有 admin 能解鎖。
  • 全部留痕修改、退回、重跑都寫入 audit_log;LLM 的 prompt 與回應保留 14 天。
  • 不會互相覆蓋存檔帶 baseHash。兩個人同時編輯,後存的會收到 409,而不是蓋掉前一個人的修改。
  • 忠實度檢查反向編譯(round-trip)對照產出與原條文;邏輯一改就標為過期,待手動重跑。
快速開始

三步,在本機跑起來

完整安裝說明 →
  1. 1先換掉預設值上線前必做
    複製 .env.example,設定 JWT_SECRET,並更換所有預設密碼。
  2. 2起整套服務
    $ docker compose up -d
    $ cd frontend && npm ci
    $ npm run dev
  3. 3打開工作台
    :5173工作台(前端開發)
    :8081整套(經 nginx)
    在 管理 → LLM 設定 加入模型 profile,就能貼上第一份條文。

從一條條文開始

貼上文字,或上傳 PDF、docx。拆解、審核、驗證、匯出,都在同一個工作台完成。

  • docker compose 起整套服務
  • LLM 可接本機 Ollama 或雲端
  • 每次修改都寫入 audit_log