醫療保健臨床決策支援系統開發模式 是做什麼的?
用於建構可整合至電子病歷工作流程的臨床決策支援系統之開發模式。CDSS 模組攸關病人安全——對偽陰性零容忍。
使用時機
- 實作藥物交互作用檢查
- 建構劑量驗證引擎
- 實作臨床評分系統(NEWS2、qSOFA、APACHE、GCS)
- 設計異常臨床值的警報系統
- 建構附安全檢查的藥物處方輸入
- 整合檢驗結果判讀與臨床情境
運作方式
CDSS 引擎是一個無副作用的純函式庫。輸入臨床資料,輸出警報。這使其完全可測試。
三個主要模組:
checkInteractions(newDrug, currentMeds, allergies)— 檢查新藥物與現有藥物及已知過敏的交互作用。回傳依嚴重度排序的InteractionAlert[]。使用DrugInteractionPair資料模型。validateDose(drug, dose, route, weight, age, renalFunction)— 根據體重劑量、年齡調整及腎功能調整規則驗證處方劑量。回傳DoseValidationResult。calculateNEWS2(vitals)— 國家早期預警評分 2,輸入NEWS2Input。回傳包含總分、風險等級及升級指引的NEWS2Result。
電子病歷使用者介面
↓ (使用者輸入資料)
CDSS 引擎(純函式,無副作用)
├── 藥物交互作用檢查器
├── 劑量驗證器
├── 臨床評分(NEWS2、qSOFA 等)
└── 警報分類器
↓ (回傳警報)
電子病歷使用者介面(內嵌顯示警報,若為關鍵級則封鎖)
藥物交互作用檢查
interface DrugInteractionPair {
drugA: string; // 學名
drugB: string; // 學名
severity: 'critical' | 'major' | 'minor';
mechanism: string;
clinicalEffect: string;
recommendation: string;
}
function checkInteractions(
newDrug: string,
currentMedications: string[],
allergyList: string[]
): InteractionAlert[] {
if (!newDrug) return [];
const alerts: InteractionAlert[] = [];
for (const current of currentMedications) {
const interaction = findInteraction(newDrug, current);
if (interaction) {
alerts.push({ severity: interaction.severity, pair: [newDrug, current],
message: interaction.clinicalEffect, recommendation: interaction.recommendation });
}
}
for (const allergy of allergyList) {
if (isCrossReactive(newDrug, allergy)) {
alerts.push({ severity: 'critical', pair: [newDrug, allergy],
message: `與已紀錄過敏產生交叉反應:${allergy}`,
recommendation: '未經過敏諮詢請勿處方' });
}
}
return alerts.sort((a, b) => severityOrder(a.severity) - severityOrder(b.severity));
}
交互作用配對必須是雙向的:如果藥物A與藥物B有交互作用,那麼藥物B也與藥物A有交互作用。
劑量驗證
interface DoseValidationResult {
valid: boolean;
message: string;
suggestedRange: { min: number; max: number; unit: string } | null;
factors: string[];
}
function validateDose(
drug: string,
dose: number,
route: 'oral' | 'iv' | 'im' | 'sc' | 'topical',
patientWeight?: number,
patientAge?: number,
renalFunction?: number
): DoseValidationResult {
const rules = getDoseRules(drug, route);
if (!rules) return { valid: true, message: '無可用驗證規則', suggestedRange: null, factors: [] };
const factors: string[] = [];
// 安全考量:若規則需要體重但體重遺失,則封鎖(不可通過)
if (rules.weightBased) {
if (!patientWeight || patientWeight <= 0) {
return { valid: false, message: `${drug}(mg/kg 藥物)需要體重資訊`,
suggestedRange: null, factors: ['weight_missing'] };
}
factors.push('weight');
const maxDose = rules.maxPerKg * patientWeight;
if (dose > maxDose) {
return { valid: false, message: `劑量超過 ${patientWeight}kg 體重上限`,
suggestedRange: { min: rules.minPerKg * patientWeight, max: maxDose, unit: rules.unit }, factors };
}
}
// 年齡調整(當規則定義年齡區間且提供年齡時)
if (rules.ageAdjusted && patientAge !== undefined) {
factors.push('age');
const ageMax = rules.getAgeAdjustedMax(patientAge);
if (dose > ageMax) {
return { valid: false, message: `超過 ${patientAge} 歲年齡調整上限`,
suggestedRange: { min: rules.typicalMin, max: ageMax, unit: rules.unit }, factors };
}
}
// 腎功能調整(當規則定義 eGFR 區間且提供 eGFR 時)
if (rules.renalAdjusted && renalFunction !== undefined) {
factors.push('renal');
const renalMax = rules.getRenalAdjustedMax(renalFunction);
if (dose > renalMax) {
return { valid: false, message: `eGFR ${renalFunction} 時超過腎功能調整上限`,
suggestedRange: { min: rules.typicalMin, max: renalMax, unit: rules.unit }, factors };
}
}
// 絕對上限
if (dose > rules.absoluteMax) {
return { valid: false, message: `超過絕對上限 ${rules.absoluteMax}${rules.unit}`,
suggestedRange: { min: rules.typicalMin, max: rules.absoluteMax, unit: rules.unit },
factors: [...factors, 'absolute_max'] };
}
return { valid: true, message: '在範圍內',
suggestedRange: { min: rules.typicalMin, max: rules.typicalMax, unit: rules.unit }, factors };
}
臨床評分:NEWS2
interface NEWS2Input {
respiratoryRate: number; oxygenSaturation: number; supplementalOxygen: boolean;
temperature: number; systolicBP: number; heartRate: number;
consciousness: 'alert' | 'voice' | 'pain' | 'unresponsive';
}
interface NEWS2Result {
total: number; // 0-20
risk: 'low' | 'low-medium' | 'medium' | 'high';
components: Record<string, number>;
escalation: string;
}
評分表必須完全符合皇家內科醫學會的規格。
警報嚴重度與使用者介面行為
| 嚴重度 | 使用者介面行為 | 臨床醫師必須採取的行動 |
|---|---|---|
| Critical(關鍵) | 封鎖動作。不可關閉的模態視窗。紅色。 | 必須記錄豁免理由才能繼續 |
| Major(重大) | 內嵌警告橫幅。橘色。 | 必須確認才能繼續 |
| Minor(輕微) | 內嵌資訊提示。黃色。 | 僅供知悉,無需採取行動 |
Critical(關鍵)警報絕不可自動關閉或以 Toast 通知方式實作。豁免理由必須儲存在稽核軌跡中。
測試 CDSS(對偽陰性零容忍)
describe('CDSS — 病人安全', () => {
INTERACTION_PAIRS.forEach(({ drugA, drugB, severity }) => {
it(`偵測 ${drugA} + ${drugB}(${severity})`, () => {
const alerts = checkInteractions(drugA, [drugB], []);
expect(alerts.length).toBeGreaterThan(0);
expect(alerts[0].severity).toBe(severity);
});
it(`偵測 ${drugB} + ${drugA}(反向)`, () => {
const alerts = checkInteractions(drugB, [drugA], []);
expect(alerts.length).toBeGreaterThan(0);
});
});
it('體重遺失時封鎖 mg/kg 藥物', () => {
const result = validateDose('gentamicin', 300, 'iv');
expect(result.valid).toBe(false);
expect(result.factors).toContain('weight_missing');
});
it('能穩定處理格式錯誤的藥物資料', () => {
expect(() => checkInteractions('', [], [])).not.toThrow();
});
});
通過標準:100%。任何遺漏的交互作用都是一次病人安全事件。
反模式
- 讓 CDSS 檢查可選或可跳過,而無需記錄理由
- 將交互作用檢查實作為 Toast 通知
- 對藥物或臨床資料使用
any型別 - 硬編碼交互作用配對,而非使用可維護的資料結構
- 在 CDSS 引擎中默默捕捉錯誤(必須大聲呈現失敗)
- 在缺乏體重資訊時跳過體重劑量驗證(必須封鎖,不可通過)
範例
範例 1:藥物交互作用檢查
const alerts = checkInteractions('warfarin', ['aspirin', 'metformin'], ['penicillin']);
// [{ severity: 'critical', pair: ['warfarin', 'aspirin'],
// message: '增加出血風險', recommendation: '避免合併使用' }]
範例 2:劑量驗證
const ok = validateDose('paracetamol', 1000, 'oral', 70, 45);
// { valid: true, suggestedRange: { min: 500, max: 4000, unit: 'mg' } }
const bad = validateDose('paracetamol', 5000, 'oral', 70, 45);
// { valid: false, message: '超過絕對上限 4000mg' }
const noWeight = validateDose('gentamicin', 300, 'iv');
// { valid: false, factors: ['weight_missing'] }
範例 3:NEWS2 評分
const result = calculateNEWS2({
respiratoryRate: 24, oxygenSaturation: 93, supplementalOxygen: true,
temperature: 38.5, systolicBP: 100, heartRate: 110, consciousness: 'voice'
});
// { total: 13, risk: 'high', escalation: '緊急臨床評估。考慮加護病房。' }