协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
nhi-cql-generator【开发与工程】Generate CQL Library and FHIR ValueSet artifacts for Taiwan NHI (National Health Insurance) reimbursement rules from rule text. Use when user asks to create CQL/ValueSet for 健保給付規則, 藥品審查, NHI CQL, 健保代碼, or payment審查. This skill applies patterns learned from 50+ GT examples.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Generate CQL Library (.cql) and FHIR ValueSet (.json) files from Taiwan NHI reimbursement rules text.
This skill generates standardized clinical decision support artifacts for Taiwan's National Health Insurance system:
Critical: All patterns are learned from 50+ GT examples. Do NOT deviate from documented patterns.
Every generated CQL file depends on two shared libraries located in .github/skills/nhi-cql-generator/assets/:
FHIRHelpers.cql (version 4.0.1)
CDSConnectCommonsForFHIRv401.cql (version 2.1.0, aliased as C3F)
C3F.Confirmed(Conditions) - Filter confirmed conditionsC3F.Verified(Observations) - Filter verified observationsC3F.MostRecent(Observations) - Get most recent observationC3F.QuantityValue(Observation) - Extract quantity valueC3F.ObservationLookBack(Observations, LookBack) - Observations in time windowC3F.MostRecentProcedure(Procedures) - Get most recent procedureC3F.ActiveCondition(Conditions) - Filter active conditionsC3F.PeriodToInterval(Period) - Convert FHIR Period to CQL IntervalC3F. prefix (e.g., C3F.Confirmed([Condition: "code"]))IMPORTANT: When generating CQL artifacts, you MUST copy these two files to the output directory alongside the generated .cql files. Without them, the CQL will fail with error:
Could not load source for library CDSConnectCommonsForFHIRv401, version 2.1.0, namespace uri null.
library "[規則編號]" version '0.0.1'
'0.0.1'library "[規則編號]" version '0.0.1'
using FHIR version '4.0.1'
include "FHIRHelpers" version '4.0.1' called FHIRHelpers
include "CDSConnectCommonsForFHIRv401" version '2.1.0' called C3F
Never change: FHIR version, FHIRHelpers version, C3F version, or aliases.
After include statements, before codesystem declarations:
valueset "[中文名稱] valueset": '[ValueSet URL]'
URL Pattern: https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.[sequential-number]
Examples:
valueset "17022B 呼氣一氧化氮監測(FeNO) valueset": 'https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.6'valueset "糖尿病合併慢性腎病變 valueset": 'https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.46'Use ONLY these codesystem URLs:
codesystem "ICD-10-CM": 'http://hl7.org/fhir/sid/icd-10-cm'
codesystem "ICD-10-PCS": 'http://www.cms.gov/Medicare/Coding/ICD10'
codesystem "LOINC": 'http://loinc.org'
codesystem "SCT": 'http://snomed.info/sct'
codesystem "TWMedicalServicePayment": 'https://twcore.mohw.gov.tw/ig/twcore/CodeSystem/medical-service-payment-tw'
codesystem "CONDVERSTATUS": 'http://terminology.hl7.org/CodeSystem/condition-ver-status'
codesystem "CONDCLINSTATUS": 'http://terminology.hl7.org/CodeSystem/condition-clinical'
codesystem "ActCode": 'http://terminology.hl7.org/CodeSystem/v3-ActCode'
context Patient
Always use Patient context.
Standard structure observed in all examples:
define "[適應症或條件名稱]":
exists ( C3F.Confirmed([Condition: "[valueset名稱]"]) )
define "MeetsInclusionCriteria":
"[主要適應症]"
define "InPopulation":
"MeetsInclusionCriteria"
define "Recommendation":
if "InPopulation" then '健保給付[點數]點'
else null
define "Rationale":
null
define "Links":
null
define "Suggestions":
null
define "Errors":
null
Direct Usage - Use C3F library functions directly without redefining them:
// ✅ Correct: Use C3F functions directly
define "適應症":
exists ( C3F.Confirmed([Condition: "適應症 valueset"]) )
define "Recent Observations":
C3F.ObservationLookBack([Observation: "code"], 1 year)
define "Lab Value":
C3F.QuantityValue( C3F.MostRecent([Observation: "code"]) )
Common C3F Functions:
C3F.Confirmed(Conditions) - Filters to confirmed conditions (verificationStatus = 'confirmed')C3F.Verified(Observations) - Filters to verified observations (status = 'final', 'corrected', or 'amended')C3F.ObservationLookBack(Observations, LookBack) - Observations within time windowC3F.MostRecent(Observations) - Most recent observation by effective dateC3F.QuantityValue(Observation) - Extract quantity value from observationC3F.MostRecentProcedure(Procedures) - Most recent procedureC3F.ActiveCondition(Conditions) - Active conditions (clinicalStatus = 'active', no abatement)C3F.PeriodToInterval(Period) - Convert FHIR Period to CQL IntervalNote: GT examples redundantly redefine these functions at the end of each file, but this is unnecessary duplication. Simply use C3F.FunctionName() directly.
exists ( C3F.Confirmed([Condition: "[valueset名稱]"]) )
exists ( C3F.Confirmed([Condition: "valueset1"])
union C3F.Confirmed([Condition: "valueset2"])
)
"condition1" and "condition2"
Count(C3F.ObservationLookBack([Observation: "[code]"], 1 year)) < 3
AgeInYears() >= 6 and AgeInYears() < 13
[Encounter: "[valueset]"] E
where ( E.serviceType ~ "[specialty code]" )
C3F.ProcedureLookBack([Procedure: "[code]"], 6 months)
define "Recommendation":
if "condition1" then if "condition2" then '說明A'
else '說明B'
else if "condition3" then '說明C'
else null
For rules with multiple適應症 with different頻率:
define "subpopulation 1":
if "InPopulation" is not true then null
else [specific condition for subpop1]
define "subpopulation 2":
if "InPopulation" is not true then null
else [specific condition for subpop2]
define "Recommendation":
if "subpopulation 1" then '健保給付[點數]點,[規範1]'
else if "subpopulation 2" then '健保給付[點數]點,[規範2]'
else null
When rules mention "脊髓損傷或病變", include ALL three categories:
| Category | ICD-10-CM | Examples |
|---|---|---|
| Trauma | S14., S24., S34., S44. | Spinal cord injury |
| Disease | G95., G82. | Spinal cord disorders, paraplegia |
| Degeneration | M43.3, M47.1, M50.0, M51.0 | Spondylolisthesis, myelopathy, disc disorders |
Critical: "病變" includes both functional disease (G-codes) AND degenerative structural changes (M-codes).
For "腦中風後遺症" or "中風導致之肢體偏癱":
| Scope | ICD-10-CM | Purpose |
|---|---|---|
| General stroke | I69.* | All stroke sequelae |
| Hemiplegia types | I69.33-I69.35 | Dominant/non-dominant/unspecified side |
| Specific sequelae | I69.3, I69.33, I69.34, I69.35, I69.39 | Cerebral infarction aftereffects |
Pattern: Use I69.* for broad inclusion, add specific I69.3X codes when hemiplegia is explicitly mentioned.
For "周邊神經病變":
For laboratory or measurement constraints:
define "低密度脂蛋白膽固醇異常":
exists (
[Observation: "LOINC code 18262-6"] O
where O.value as Quantity > 100 'mg/dL'
and O.status = 'final'
)
For rules specifying age ranges with measurement timing:
define "符合年齡條件":
( AgeInYears() >= 10 and AgeInYears() <= 17 )
or ( AgeInYears() >= 6 and AgeInYears() < 10 and "已測量過基線值" )
'...' for all strings// sparingly, written in Chinese when explaining業務邏輯define "氣喘":, define "懷孕糖尿病人、妊娠糖尿病人":)"variable" not "variable" = truePattern: [數字]{2,5}[字母] (e.g., 08111B, 12216C, 69041B, 83102K)
適應症 (Indications):
禁忌症 (Contraindications):
支付規範 (Payment Rules):
點數 (Points):
專科限制 (Specialty):
From text patterns like:
CRITICAL: Always filter by the specific NHI procedure code using TWMedicalServicePayment codesystem. NEVER use bare [Procedure] (this would match ALL procedures, not the one being regulated).
codesystem "TWMedicalServicePayment": 'https://twcore.mohw.gov.tw/ig/twcore/CodeSystem/medical-service-payment-tw'
code "[代號] 健保給付代碼": '[代號]' from "TWMedicalServicePayment" display '[項目名稱]'
// ✅ Correct: counts only THIS specific procedure
define "頻率符合":
Count(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 1 year)) < N
// ❌ WRONG: counts ALL procedures in the patient's history
define "頻率符合":
Count(C3F.ProcedureLookBack([Procedure], 1 year)) < N
| Rule Text | CQL Encoding |
|---|---|
| "一年限申報三次" | Count(ObservationLookBack([Observation: ...], 1 year)) < 3 |
| "每四年限申報一次" | Count(ObservationLookBack([Observation: ...], 4 years)) < 1 |
| "每人以申報一次為原則" | Track across patient lifetime |
| "每次就診...每次治療應間隔至少一週" | Date diff checking |
| "一年限申報三次" | Count(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 1 year)) < 3 |
| "每四年限申報一次" | Count(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 4 years)) < 1 |
| "每人以申報一次為原則" | not exists(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 100 years)) |
| "每週最多N次" | Count(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 7 days)) < N |
| "每月限一次" | Count(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 30 days)) < 1 |
When the rule says "限住院期間申報一次" or "同一就診申報一次", use Encounter reference instead of a time window. A time window (e.g., "1 year") would incorrectly block re-admission for a new event.
// 取得目前就醫 Encounter(最近一次進行中或完成的)
define "目前住院Encounter":
Last(
[Encounter] E
where E.status = 'in-progress' or E.status = 'finished'
sort by start of period
)
// 本次住院期間尚未申報過本項目
define "本次住院未申報過":
"目前住院Encounter" is not null
and not exists(
[Procedure: "[代號] 健保給付代碼"] P
where P.encounter.reference = 'Encounter/' + "目前住院Encounter".id
)
When rule says "同一治療期間至多N次", the ideal boundary is EpisodeOfCare. As a pragmatic approximation, use Encounter-reference for the per-visit part, and ProcedureLookBack with a reasonable lookback window for the total-count part:
define "本次就診未申報":
"目前住院Encounter" is not null
and not exists(
[Procedure: "[代號] 健保給付代碼"] P
where P.encounter.reference = 'Encounter/' + "目前住院Encounter".id
)
// 治療期間總次數:以 1 年作為最長治療期間的保守估計
define "治療期間總次數符合":
Count(C3F.ProcedureLookBack([Procedure: "[代號] 健保給付代碼"], 1 year)) < N
Note: The ideal approach for "同一治療期間" is to use
EpisodeOfCare, but this requires the FHIR server to populateEpisodeOfCareresources. The time-window approximation is the practical fallback.
Critical: Distinguish between pre-authorization criteria (事前資格檢查) and post-payment rules (事後支付規則).
These MUST be encoded as executable logic in MeetsInclusionCriteria:
Pattern: Contraindication ("禁忌症:...")
When a rule lists contraindications, create a separate contraindication ValueSet and check for absence:
// 1. Declare a separate contraindication valueset
valueset "[代號] 禁忌症": 'https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.[n]'
// 2. Define the contraindication check
define "有禁忌症":
exists ( C3F.Confirmed([Condition: "[代號] 禁忌症"]) )
// 3. Incorporate into MeetsInclusionCriteria
define "MeetsInclusionCriteria":
"Has Indication" and not "有禁忌症"
// 4. Recommendation branches (always check indication first → then contraindication)
define "Recommendation":
if not "Has Indication" then null
else if "有禁忌症" then '不得申報。病患符合禁忌症:[詳細原因]。'
else '健保給付。...'
Important: The contraindication JSON file uses the same structure as the indication ValueSet. Give it a distinct OID number (e.g., indication = .9, contraindication = .200).
Pattern: Specialist Restriction ("限...專科醫師執行")
codesystem "SCT": 'http://snomed.info/sct'
code "Specialist code": '[SNOMED-code]' from "SCT" display '[specialist-type]'
define "performer_id":
Last(Split(First("RecentProcedure".performer).actor.reference, '/'))
define "Performer_ref":
[Practitioner] P where P.id = "performer_id"
define "Qualified_Practitioner":
exists ( "Performer_ref".qualification q
where q.code ~ "Specialist code" )
define "MeetsInclusionCriteria":
"[適應症]" and "Qualified_Practitioner"
Examples:
Pattern: Procedure Type Restriction
When rule specifies specific surgical procedures:
codesystem "ICD-10-PCS": 'http://www.cms.gov/Medicare/Coding/ICD10'
code "Procedure1 code": '[code]' from "ICD-10-PCS" display '[description]'
code "Procedure2 code": '[code]' from "ICD-10-PCS" display '[description]'
define "ApplicableProcedure":
C3F.MostRecentProcedure ( [Procedure: "Procedure1 code"]
union [Procedure: "Procedure2 code"]
)
define "MeetsInclusionCriteria":
"[適應症]" and exists("ApplicableProcedure")
Pattern: Encounter Specialty Check
For rules limiting to specific clinical departments:
code "Department specialty code": '[SNOMED-code]' from "SCT" display '[specialty]'
define "SpecialtyEncounter":
[Encounter] E where E.serviceType ~ "Department specialty code"
define "encounter check":
exists("SpecialtyEncounter")
define "Recommendation":
if "InPopulation" is not true then null
else if "encounter check" then '給付'
else '非[科別]申報,不予給付'
These should appear in Recommendation string but NOT in MeetsInclusionCriteria:
Examples:
Pattern:
define "Recommendation":
if "InPopulation" then '健保給付[點數]點,[描述],一般材料費得另加計百分之五十'
else null
| Phrase Pattern | Type | Action |
|---|---|---|
| 限...專科醫師執行 | Pre-auth | ✅ Convert to Practitioner.qualification check |
| 限...科別申報 | Pre-auth | ✅ Convert to Encounter.serviceType check |
| 限特定手術類型 | Pre-auth | ✅ Convert to Procedure code check |
| 材料費加計...% | Post-payment | ❌ Text only in Recommendation |
| 應事先申請 | Administrative | ❌ Text only in Recommendation |
| 需檢附報告 | Administrative | ❌ Text only in Recommendation |
Critical Rule: If the regulation affects whether payment is approved, it's pre-authorization → convert to CQL. If it only affects how much or when to pay, it's post-payment → text only.
Based on GT examples, use this complete structure:
{
"resourceType": "ValueSet",
"id": "2.16.840.1.113762.1.4.1287.[number]",
"meta": {
"versionId": "1",
"lastUpdated": "[timestamp]",
"profile": [
"http://hl7.org/fhir/StructureDefinition/shareablevalueset",
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-valueset-cqfm",
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/publishable-valueset-cqfm"
]
},
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/valueset-author",
"valueContactDetail": {
"name": "Industrial Technology Research Institute Author"
}
},
{
"url": "http://hl7.org/fhir/StructureDefinition/resource-lastReviewDate",
"valueDate": "[YYYY-MM-DD]"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/valueset-effectiveDate",
"valueDate": "[YYYY-MM-DD]"
}
],
"url": "http://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.[number]",
"identifier": [
{
"system": "urn:ietf:rfc:3986",
"value": "urn:oid:2.16.840.1.113762.1.4.1287.[number]"
}
],
"version": "[YYYYMMDD]",
"name": "[EnglishCamelCaseName]",
"title": "[English title or Chinese title]",
"status": "active",
"date": "[timestamp]",
"publisher": "Industrial Technology Research Institute Steward",
"description": "[規則編號,可多個用逗號分隔如 63007B, 63008B]",
"jurisdiction": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
}
]
}
],
"purpose": "(Clinical Focus: ...),(Data Element Scope: ...),(Inclusion Criteria: ...),(Exclusion Criteria: ...)",
"compose": {
"include": [
{
"system": "[CodeSystem URL]",
"version": "2025",
"filter": [
{
"property": "concept",
"op": "[operator]",
"value": "[code]"
}
]
}
]
}
}
Pattern: 2.16.840.1.113762.1.4.1287.[sequential-number]
The ID is the full OID, not a custom name. File name matches the ID:
2.16.840.1.113762.1.4.1287.6.json"2.16.840.1.113762.1.4.1287.6"Critical: Use http:// (NOT https://) in url field:
"url": "http://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.[number]"
In CQL: Use https:// in valueset declarations:
valueset "name": 'https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.[number]'
Use filter instead of explicit concept array:
Common operators:
"op": "descendent-of" - Include code and all descendants (most common)"op": "is-a" - Include code and descendants"op": "=" - Exact match single code"op": "in" - Multiple codes (comma-separated in value)Examples:
// All J45.* asthma codes
{"property": "concept", "op": "descendent-of", "value": "J45"}
// Specific codes
{"property": "concept", "op": "=", "value": "C79.81"}
// Multiple specific codes
{"property": "concept", "op": "in", "value": "E10.22,E13.22,E08.22"}
Choose operator based on code specificity and clinical intent:
| Operator | Use When | ICD-10 Code Pattern | Example |
|---|---|---|---|
= | Exact leaf code needed | 6-7 digits (e.g., G62.9, I69.398) | Single specific diagnosis |
is-a | Mid-level category | 4-5 digits (e.g., J45.5, E11.4) | Specific subtype with few children |
descendent-of | Broad family inclusion | 3-4 digits (e.g., J45, I69, G62) | All variants needed |
in | Multiple unrelated codes | Mixed | Enumerate disparate codes |
Code Granularity Guidelines:
Real Examples from GT:
// G62.9 (7 digits, leaf code) - use exact match
{"property": "concept", "op": "=", "value": "G62.9"}
// E11.4 (5 digits, mid-level) - use is-a
{"property": "concept", "op": "is-a", "value": "E11.4"}
// I69 (3 digits, broad family) - use descendent-of
{"property": "concept", "op": "descendent-of", "value": "I69"}
// Multiple unrelated codes - use in
{"property": "concept", "op": "in", "value": "M43.3,M47.1,M50.0,M51.0"}
Decision Logic:
=is-adescendent-ofinStandard 4-part structure (observed in all GT examples):
(Clinical Focus: [臨床焦點]),(Data Element Scope: [資料元素範圍]),(Inclusion Criteria: [納入條件]),(Exclusion Criteria: [排除條件,若無則寫 No])
Critical: ValueSet.url in JSON must exactly match the valueset declaration URL in CQL (except http vs https).
http://hl7.org/fhir/sid/icd-10-cm"2025"http://www.cms.gov/Medicare/Coding/ICD10 or https://twcore.mohw.gov.tw/ig/twcore/CodeSystem/medical-service-payment-twhttp://loinc.orghttp://snomed.info/sctFrom GT examples, one ValueSet may support multiple rules:
"description": "63007B, 63008B" - Modified radical mastectomy (single/bilateral)"description": "13030B,13031B,13032B" - Different H. pylori testsWhen rules share indications, use the same ValueSet ID.
Ask user for:
Extract and identify:
For each distinct code set identified:
2.16.840.1.113762.1.4.1287.[number].jsonhttp:// (NOT https)descendent-of for code families (e.g., J45 for all asthma)in for multiple specific codes= for single specific codesImportant:
2.16.840.1.113762.1.4.1287.6.json)http://https://Following exact structure:
Checklist:
Deliver to user:
[規則編號].cql (plain text)2.16.840.1.113762.1.4.1287.[number].json files.github/skills/nhi-cql-generator/assets/ to output directory:
FHIRHelpers.cqlCDSConnectCommonsForFHIRv401.cql| CQL ValueSet Declaration | ValueSet File | URL (CQL uses https, JSON uses http) |
|--------------------------|---------------|---------------------------------------|
| [中文名稱] valueset | 2.16.840.1.113762.1.4.1287.[N].json | https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.[N] |
Critical: The CQL file will NOT execute without the dependency libraries in the same directory. Always include them in the output.
Input:
Output:
08111B.cql - With Von Willebrand disease codes2.16.840.1.113762.1.4.1287.1.json - ICD-10-CM codes with filter for VWD codesInput:
Output:
17022B.cql - With age check, frequency check, and pediatric specialty check2.16.840.1.113762.1.4.1287.6.json - Uses descendent-of filter for J45 (asthma)Input:
Output:
12216C.cql - With subpopulation 1 and 2, different frequency rules2.16.840.1.113762.1.4.1287.[N].json - HIV codes with appropriate filters2.16.840.1.113762.1.4.1287.[N+1].json - Dialysis dependency codesActivate when user mentions:
In ValueSet JSON (url field):
"url": "http://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.6"
Uses http:// (no 's')
In CQL (valueset declaration):
valueset "name": 'https://example.org/fhir/ValueSet/2.16.840.1.113762.1.4.1287.6'
Uses https:// (with 's')
This is intentional and follows GT examples exactly.
2.16.840.1.113762.1.4.1287.6.json17022B-indications.json