[开源] 合理用药规则改了三年,临床药师还在等排期:基于内容寻址与状态机的院内前置审核落地工具

发布时间:2026/8/3 3:56:30
[开源] 合理用药规则改了三年,临床药师还在等排期:基于内容寻址与状态机的院内前置审核落地工具 本项目把合理用药规则的编辑、版本化、签核、发布、审计五件事从信息科手里拿回临床药师桌上。院里常见的尴尬是药师周二例会临时拍板肝功能不全患者禁用头孢哌酮真正落到 HIS 前置审核里要等到下周五因为 SQL 存储过程攥在信息科手里改一条规则得走排期、测试、上线流程。我们想让药师在 Web 配置台里用结构化表单改规则、拖拽表达式、保存草稿、提交签核、一键发布全部在自己的浏览器里完成交付形态是 Web 配置台 离线 CLI JSON-LD 自检包 PDF 报告技术栈是 Fastify TypeScript PostgreSQL React 18模拟适配器用 FHIR MedicationRequest 子集跑通端到端。角色与触发时刻院里这套活儿绕不开四个人临床药师、主任药师、信息科、审计员。临床药师平时在配置台用结构化表单写规则、保存为 draft主任药师在签核页看 AI 预审提示、人工 approve 或驳回信息科负责适配器注册、强制下线、上线阻断审计员在飞检月导出 JSON-LD 自检包交给飞行检查组。典型的周二例会故事线是这样走的上午例会决定加一条肝功能不全患者禁用头孢哌酮的规则药师进入配置台、选好 category 与 severity、拖拽表达式构造器拼出patient.hepatic_function in [B,C]、保存为 draft、提交 testing下午主任药师打开签核页、看到 AI 预审提示与现有肝损规则存在 severity 冲突、调整后批准 active周三信息科一键发布30 秒内所有在线适配器同步拉到新规则集HIS 前置审核立刻生效次月飞检审计员导出 JSON-LD 自检包交给飞检组离线校验包里含规则全版本链、全部签核记录、全部触发拦截记录飞检组不用连任何生产数据库就能验自洽。核心机制拆开看五条主线支撑上面这套流程我们一项一项拆开。模块职责技术要点DSL 强类型药师拖拽式构造规则结构化、可校验、防幻觉JSONLogic 受限子集 强类型 AST 深度类型推导内容寻址规则体规范化后算 SHA-256存不可变版本对象JSON canonicalize SHA-256 hexGit-like 版本化分支 / tag / diff / 3-way merge / 一键回滚不依赖真实 git纯 Postgres JSONB 父指针链表工作流状态机draft → testing → active → retired加签核工单状态转换守卫 HMAC 签名 AI 预审 stub审计 append-only JSON-LD飞检时一键导出可校验包链式 HMAC schema.org 锚定 PDF 报告表达式构造器是药师每天打交道最多的组件。它在白名单内做嵌套式拖拽操作符只能在/!/////in/!in/and/or/not//-/cat/coalesce里挑变量只能在 14 条 drug / dose / department / patient 路径里挑函数只能在 10 条白名单函数如has_allergy/creatinine_clearance/is_liver_compromised里挑。除了literal节点的字面值输入框没有任何让药师自由键入 op/var/function 名的控件前端有专门的测试断言锁定这个不变量。底部 Lint 面板防抖 300ms 调后端/v1/dsl/validate客户端先做白名单静态校验通过后再请求后端后端不可达时降级为本地校验并提示DSL_BACKEND_UNREACHABLE。版本化与回滚30 秒的故事线上线 5 分钟发现肝功能不全 头孢哌酮 → block规则误伤了孕妇科室需要立刻回滚到 v2仅 block 肝功能 C配置台不可达时仍可走 CLI 兜底整个过程分四步# 步骤 1查版本树30 秒内确定目标 curl -sS http://localhost:4010/v1/versioning/list/R0001 | jq .[] | {version_hash, state, message, author, created_at} # → 当前 active v3 (肝损 B C block)目标 v2 (肝损 C only block) # 步骤 2diff 确认回滚内容 curl -sS -X POST http://localhost:4010/v1/versioning/diff \ -H Content-Type: application/json \ -d {a_hash: v3-hash, b_hash: v2-hash} | jq # → {added:[], removed:[when.0.in.0 B], changed:[then.message_template]} # 步骤 3一键回滚 curl -sS -X POST http://localhost:4010/v1/versioning/rollback \ -H Content-Type: application/json \ -d { rule_id: R0001, target_hash: v2-hash, actor: info-001, reason: v3 误伤孕妇科室B 级患者包含部分正常孕检样本 } | jq # → {new_version_hash: v4-hash, new_state: active, # retired_hashes: [v3-hash], audit_event_id: audit-id} # 步骤 4验证拦截解除 curl -sS -X POST http://localhost:4011/fhir/MedicationRequest \ -H Content-Type: application/fhirjson \ -d eval/fixtures/orders/hepatic-b.json # → verdictpassv3 时为 block回滚后为 pass两个关键不变量必须守住回滚不删任何历史v1/v2/v3 仍存在state 标 retired回滚产生全新版本 v4不修改 v3 的 content_address。审计链同时记录rule.version.rolled_back与旧 activerule.version.retired飞检组可双向追溯。适配器降级上线不能假成功适配器在 HIS / EMR / 医保前置审核节点是最终消费者。规则集上线前必须确认至少一个 active 适配器在线否则就是假上线规则在 testing 阶段签核通过却因为下游都收不到而形同虚设。衍生状态由心跳新鲜度决定online是心跳 ≤ 5 分钟、degraded是 5–30 分钟、offline是 30 分钟或失联、disabled是信息科主动停用。阈值定义在backend/src/modules/adapter/types.ts。当前 active 规则集中如果没有任何 online 适配器的last_pulled_version命中配置台顶部显示红色 banner当前 N 条规则未下发到任何在线适配器但不阻断发布信息科可继续推进 release 但必须看到 banner。真正阻断的是 testing → active 这道守卫任何 active 适配器均处于 offline或注册表为空时状态机抛出WorkflowError({ code: ADAPTER_OFFLINE_BLOCKS_PUBLISH, http: 409, details: { online_count, offline_count, disabled_count, unpushed_rules } })上线按钮无法点击。恢复路径是信息科联系厂商恢复心跳 → 调用POST /v1/adapters/:id/heartbeat重新拉取版本 → 配置台unpushed_rules清零且 online_count ≥ 1 后自动解锁。REST 端点速查路径用途GET /v1/adapters列表含derived_statusGET /v1/adapters/:id单条含derived_statusGET /v1/adapters/status聚合total / online / degraded / offline / disabled / unpushed_rules / by_protocolPOST /v1/adapters/:id/heartbeatmock-adapter 主动调用POST /v1/adapters/:id/enable信息科启用 / 禁用GET /v1/rules/activemock-adapter hot-reload 端点30s TTL凭证管理守住一条底线credentials_ref字段只接受路径/URI 引用vault://his/app-secret/env:HIS_TOKEN绝不存明文。明文走 HashiCorp Vault / 云 KMS / 院方 KSMbin/rxpc 与 mock-adapter 启动时读process.env但不写入任何 audit_event.payload。飞检自检包离线可验次月飞检时审计员向飞行检查组或医保稽核组提交一份规则决策全过程的可校验包。JSON-LD 自检包是唯一权威交付飞检组离线校验自洽即可不依赖任何在跑服务。# 步骤 1导出 JSON-LD 自检包最近 30 天全量 curl -sS -X POST http://localhost:4010/v1/audit/export-bundle \ -H Content-Type: application/json \ -d {from_ts: 2026-06-01T00:00:00Z, to_ts: 2026-06-30T23:59:59Z} \ audit-export-202606.json # 步骤 2离线自检无需联网 ./bin/rxpc verify-bundle ./audit-export-202606.json # → {ok: true}manifest hash HMAC 内部 prev_hash 链均一致 # 步骤 3同步出具 PDF 报告线下归档 curl -sS -X POST http://localhost:4010/v1/audit/export-pdf \ -H Content-Type: application/json \ -d {rule_id: uuid-1, from_ts: ..., to_ts: ..., generator: audit-001} \ --output audit-report-202606.pdf # PDF 含 5 章节Chain Verification / Rule Definition / Rule Versions / # Signoff Tickets / Action Counts不可抵赖的根基是每条audit_event的payload_sha256由 SHA-256 算得signature由 HMAC-SHA256(secret) 算得任何字段修改都会被检测到。包内含规则定义 / 版本链 / 签核工单 / 审计事件 4 类节点谁、在何时、基于什么依据、做了什么变更在包里自描述无需查询生产数据库。飞检组如怀疑本系统有缺陷可同时运行eval/bench/baseline.sql与本系统的eval/bench/eval.ts做对照precision/recall 写入eval/bench/report.json作为质量佐证。快速上手cp .env.example .env docker compose up -d npm run migrate bash eval/demo.sh # 端到端 demo 60s 跑通 8 步 npm run bench # 召回率 / 精确率 benchmark npm run coverage # 覆盖率 mutation npm run bench:perf # 性能 benchmarkper-rule p95 5ms open http://localhost:4012/ # Web 配置台 ./bin/rxpc validate test/fixtures/dsl/valid-rule.json # 离线 CLI 校验端口分配postgres 5432、backend (Fastify) 4010、frontend (Vite) 4012、mock-adapter (FHIR) 4011。OpenAPI UI 在http://localhost:4010/docsOpenAPI JSON 在http://localhost:4010/docs/json。性能 benchmark 跑完后业务吞吐的折算很直观per-rule p95 实测 0.001 ms预算 5 ms快 5000 倍end-to-end p95 0.01 ms实测吞吐 100,000 医嘱/秒。信科主任问能不能扛住早高峰 1 万医嘱/小时时1 万/小时 ≈ 3 医嘱/秒余裕 3 万倍。样例数据底座在data/samples/30 药品 / 10 科室 / 50 规则 / 200 医嘱seed42 确定性生成覆盖 5 类规则抗菌药分级、溶媒禁忌、单次剂量上限、特殊人群、医保限制。所有patient_hashHMAC-SHA256(seed|salt).slice(0,16)无姓名 / 住院号 / 真实批号。规则来源字段映射到美康 / 大通 / 卫宁 / 国家版知识库的接口约定本样例不复制任何真实知识库原文真实接入需按院方采购合同处理凭证。边界与范围产品不重做合理用药知识库内容美康 / 大通 / 卫宁 / 国家版均视为外部数据源不内置与真实 HIS / EMR / 医保前置审核的厂商 SDKmock-adapter用于自包含端到端真实接入需 HIS 厂商按其协议自行实现。LLM 仅 stubLLM_PROVIDERstub|openai|claude可拔插AI 预审提示在 MVP 阶段是占位实现。许可 MIT。项目地址 https://github.com/nexorin9/rx-policy-station