Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"email": "z1326434525@gmail.com"
},
"metadata": {
"version": "0.2.0-pilot",
"version": "0.7.0-pilot",
"description": "Medcius — 面向一线临床医生的 Agent 插件,为宿主 Agent 提供受约束的临床技能、只读工具、PHI 防护、证据追溯与审计。"
},
"plugins": [
Expand Down
21 changes: 21 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.git
.github
tests
docs
experimental
node_modules
staging
out
deploy
*.md
!README.md
.claude-plugin
.codebuddy
.cursor
.posix
.rules
.trae
.claude
agents
**/*.log
**/data.sqlite*
8 changes: 8 additions & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# gitleaks 历史/合成豁免清单(每行一条指纹;# 开头为注释)
#
# 1) tests/test-security.mjs:61 — AES-256-GCM 加解密回环测试的显式密钥。
# 值为 "0123456789abcdef" 重复 4 遍的确定性合成模式,不是真实凭据;
# 引入于 1828a2e(feat: release Phase 1 architecture)。历史提交无法重写,
# 故按 gitleaks 官方方式登记 fingerprint 豁免;该行本身也已改为低熵合成值,
# 未来不再触发 generic-api-key 规则。
1828a2ed5d1a09b482ba8418ec2343c89ba4153c:tests/test-security.mjs:generic-api-key:61
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Medcius 前置机生产镜像(缺口六:运行时产品形态)
# 零第三方依赖(node:http / better-sqlite3 均为本地源码或内建),镜像只含运行所需文件。
# 安全基线:非 root 运行、固定版本基镜像、健康检查、数据/密钥全部经挂载注入(不进镜像层)。
FROM node:22.14.0-alpine3.21

RUN addgroup -S medcius && adduser -S medcius -G medcius \
&& mkdir -p /opt/medcius/data /opt/medcius/backups \
&& chown -R medcius:medcius /opt/medcius

WORKDIR /opt/medcius/app

# 只拷贝运行所需(零第三方依赖,无需 npm install);测试/文档/合规文书/实验区不进生产镜像
COPY scripts ./scripts
COPY plugins ./plugins

ENV NODE_ENV=production \
NODE_NO_WARNINGS=1 \
PORT=8080 \
HOST=0.0.0.0 \
CLAUDE_MEDCIUS_DATA=/opt/medcius/data

USER medcius
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"

CMD ["node", "scripts/serve.mjs"]
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Medcius 为 Codex、Trae、WorkBuddy/CodeBuddy 或医院自建 Agent 增加受

Medcius 不是一套独立临床软件或平台,也不是一个能够自主诊疗的“临床智能体”。它不负责替代宿主 Agent 的对话与编排,不拥有患者主数据,不独立决定下一步临床行动,也不绕过医生执行 EHR 写回。

当前版本为 **`0.2.0-pilot` 工程试点版**。已有代码和合成验证不能替代真实 EHR 验收、临床事实准确性、人因效率、安全性或监管证据。
当前版本为 **`0.7.0-pilot` 工程试点版**。已有代码和合成验证不能替代真实 EHR 验收、临床事实准确性、人因效率、安全性或监管证据。

## 定位

Expand Down Expand Up @@ -102,12 +102,22 @@ Medcius Agent Plugin
| `skills/clinical-note-extract` | 带原文 span 和断言状态的病历事实抽取 |
| `skills/doc-extract` | 文档与附件提取 |
| `lib/hospital-agent-adapter.mjs` | 面向 Codex、Trae、WorkBuddy 与医院自建 Agent 的宿主无关适配内核 |
| `lib/clinician-directory-auth.mjs` | 医院目录身份适配(LDAP/AD/统一身份插槽 + 确定性角色映射 + 失败锁定 + 会话吊销,无隐式特权) |
| `lib/ca-signature-adapter.mjs` | CA 电子签名适配层(内置 ECDSA P-256 + 医院 CA SDK 插槽;签名记录可验签、防篡改、零 PHI) |
| `lib/patient-evolution-engine.mjs` | 查房前患者变化整理引擎 |
| `lib/shift-handover-engine.mjs` | 临床交接班 SBAR 结构化整理引擎 |
| `lib/consult-preparation-engine.mjs` | 专科会诊前资料包整理引擎 |
| `lib/discharge-readiness-engine.mjs` | 出院准备度与资料完整性核对引擎 |
| `lib/patient-affordability-context.mjs` | 来源绑定的患者费用负担、覆盖/估算与援助转介状态;不计算自付额或自动改药 |
| `lib/nhsa-record-quality-engine.mjs` | 病案首页/医保结算清单要素质量确定性核对:必填要素缺口、住院天数与费用代数一致性、离院方式值域、性别/年龄-诊断章节冲突;不做 DRG/DIP 分组、不改编码、不判定医保违规 |
| `lib/settlement-from-note.mjs` | 出院记录 → 结算清单栏 + 编码六字段出处 + 清单机检 + 病案要素质量核对;不做分组器 |
| `contracts/patient-financial-access-record.v1.schema.json` | 费用负担与可获得性输入记录的机器可检查契约 |
| `contracts/china-record-quality-report.v1.schema.json` | 病案要素质量核对报告的机器可检查契约 |
| `contracts/drg-dip-reconciliation.v1.schema.json` | DRG/DIP 要素质量×医院分组器对账契约(不做分组器) |
| `packs/official-sources.json` + `scripts/fetch-official-corpus.mjs` + `scripts/corpus-freshness.mjs` | 官方语料供应链:来源登记表、拉取/暂存校验(永不自动导入)、新鲜度 SLA 监控 |
| `plugins/medcius/scripts/gen-classification-pack.mjs` + `scripts/qms-internal-audit.mjs` | 分类界定材料包就绪门(R04→R05)与可执行 QMS 内审(R09 落地) |
| `Dockerfile`/`docker-compose.yml`/`deploy/` + `scripts/deploy.mjs` + `scripts/resident-probe.mjs` | 运行时产品形态:容器化交付、安装/升级/回滚(sha256 清单)、常驻探针与 Prometheus 指标 |
| `lib/llm-inference-config.mjs` | LLM 推理路径配置管理:A/B 档校验(C 档全托管拒绝)、extract-only 客户端(D1 结构性限制)、模型/提示词版本出处 |
| `lib/idp-jwks-verifier.mjs` | 企业级 IdP / OIDC / JWKS 动态公钥验签与多租户隔离中间件 |
| `lib/mtls-gateway-guard.mjs` | 院内前置机 mTLS 双向认证守卫与零信任只读安全信封 |
| `lib/clinical-skill-catalog.mjs` + `rule-packs/catalogs/` | 临床技能目录全生命周期治理引擎(专家审批、哈希签名、一键熔断与回滚) |
Expand All @@ -116,15 +126,16 @@ Medcius Agent Plugin
| `lib/high-risk-followup-tracker.mjs` | 高风险检查检验从开立、采集、结果到医生确认的阶段追踪;不自动处置 |
| `lib/specialty-rule-pack.mjs` + `rule-packs/` | 专科病区规则包加载、版本哈希、审批元数据与生产环境失败关闭 |
| `lib/read-only-hospital-data-bridge.mjs` | 院内异构接口只读桥;逐源绑定租户、患者、就诊和来源哈希 |
| `lib/connectors/` | 真实系统 FHIR R4CDA 文档通道只读连接器及 PHI 出口守卫 |
| `lib/connectors/` | 真实系统四条只读接入路径连接器(P1 FHIR R4、P2 CDA 文档、P3 视图库/中间库、P4 HL7 v2 消息订阅)及 PHI 出口守卫 |
| `evals/shadow-mode/` | 真实世界多病区连续病例影子研究(Shadow Study)协议引擎与 Wilson CI 统计 |
| `evals/real-world-noise/` | 真实病历脏数据鲁棒性基准(噪声模型 + 确定性下限)与真实脱敏病历接入量具(fail-closed) |
| `evals/time-motion/` | 临床医生 Time-Motion 与人因认知负荷(NASA-TLX)自动化统计分析器 |
| `evals/physician-annotation/` | 独立医生双盲标注、Kappa 一致性评测与仲裁体系 |
| `servers/fhir` | SMART on FHIR R4 连接器;Codex、Trae、WorkBuddy 适配入口强制只读 |
| `servers/documents` | 本地文档提取与来源处理 |
| `servers/phiguard` | PHI 扫描、脱敏与假名化支持 |
| `servers/audit` | 本地防篡改检测用哈希链审计 |
| `servers/api` | 参考侧边栏、REST 与 CDS Hooks 适配 |
| `servers/api` | 参考侧边栏、医生端内网工作台(`/workstation`,治理阶梯感知)、REST 与 CDS Hooks 适配 |

部分上游遗留能力仍需从正式插件包中继续拆分。处方、编码、临床试验、管理驾驶舱和多 Agent 模块不得因为存在于仓库中就被视为 Medcius 核心能力。

Expand Down
89 changes: 89 additions & 0 deletions deploy/DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 前置机部署与运维手册(DEPLOYMENT · 缺口六落地)

> **用途**:把 Medcius 从"git 仓库 + node 进程"变成可安装、可升级、可回滚、可监控的院内交付物。与 `docs/ops/PRODUCTIZATION-OPERATIONS.md`(策略层)配套,本文是执行层。所有命令已在 CI 中做确定性子集验证(第 45 门)。

## 1. 前置机规格基线

| 项 | 沙箱评估 | 生产建议 |
|---|---|---|
| OS | 任意可跑 Node ≥20 | 麒麟/openEuler/Ubuntu LTS(院方基线) |
| CPU/内存 | 2C4G | 4C8G(含本地 LLM 推理时按模型另计 GPU) |
| 磁盘 | 10G | 系统 20G + 数据 50G+(审计链按事件量评估) |
| 网络 | 单机 | 医院内网区 → 前置机区(DMZ);医生工作站 → 前置机 8443/TLS;前置机 → HIS 只读视图/FHIR |
| 运行身份 | 任意 | 专用 `medcius` 系统用户(非 root) |

## 2. 三种部署形态

| 形态 | 命令 | 适用 |
|---|---|---|
| 容器(推荐) | `docker compose --profile hospital up -d` | 有容器平台的院区;`deploy/medcius.env` + 卷挂载注入密钥/数据/TLS/规则包 |
| systemd 裸机 | `deploy.mjs install --target /opt/medcius` + `deploy/systemd/medcius.service` | 无容器平台 |
| 开发沙箱 | `docker compose --profile sandbox up` 或 `node scripts/serve.mjs` | 工程/演示(demo 病区可用) |

## 3. 安装 / 升级 / 回滚(`scripts/deploy.mjs`)

```bash
# 巡检(node 版本、env、数据目录、源码版本)
node scripts/deploy.mjs status

# 安装(确定性布局:releases/<时间戳> + data/ + backups/ + medcius.env;env 存在则拒绝覆盖)
node scripts/deploy.mjs install --target /opt/medcius

# 升级 = 先备份 data/(sha256 清单)→ 新版本 release → 切换 current
node scripts/deploy.mjs upgrade --target /opt/medcius

# 回滚 = 校验备份清单 sha256 → 恢复 data/(当前数据先做安全备份)
node scripts/deploy.mjs rollback --target /opt/medcius --to <backup-ts>

# 一切变更支持 --dry-run(只打印计划,不落盘)
```

升级铁律:升级前 `backup` 自动执行;审计链(append-only)只进备份、永不重建;回滚必须通过 manifest sha256 校验,损坏即中止。

## 4. 常驻探针与监控对接(落地运维手册 §6)

```bash
# 单次巡检(CI 第 45 门使用 --once;P1/P2 活跃时 exit 2)
node scripts/resident-probe.mjs --once --metrics-out /opt/medcius/metrics/probe.prom

# 常驻(systemd timer 或 compose 内另起 sidecar)
node scripts/resident-probe.mjs --interval 60 --state-file /opt/medcius/data/probe-state.json --metrics-out /opt/medcius/metrics/probe.prom
```

告警规则(确定性子集,`evaluateProbeRules` 可单测):
- `audit_chain_broken` **P1**:审计链 verify 任何一次失败即 P1;
- `health_endpoint_down` 连续 ≥3 周期 P2,累计 ≥30 分钟升 P1;
- `production_corpus_missing` **P2**:official 语料为 0(H01 将阻断真实流程);
- `probe_latency_breach` **P3**:/health 延迟超预算。

Prometheus 指标(`medcius_probe_*`)写入 `--metrics-out`,由院方监控平台(Prometheus file_sd / exporter 汇聚)采集;告警通知走院方既有通道。

## 5. LLM 推理路径(缺口六:从"口头约定"到"被校验的配置")

```javascript
import { validateLlmConfig, createLlmInferenceClient } from "./plugins/medcius/lib/llm-inference-config.mjs";

const v = validateLlmConfig({
topology: "A", // A 全本地 / B 混合;C 全托管在校验层直接拒绝
model_id: "qwen2.5-14b-instruct", model_version: "2026-08",
prompt_pack_version: "medcius-extract-v3", endpoint: "http://127.0.0.1:11434/v1",
capacity: { max_concurrency: 8, latency_budget_ms_p95: 8000 },
});
// v.ok === false 时拒绝启动相关工作流(fail-closed)

const llm = createLlmInferenceClient({ config, transport: myOpenAICompatibleTransport });
await llm.extract({ text }); // 只有 extract() —— 对象上不存在 decide()/adjudicate()(D1 结构性限制)
```

- B 档必须声明 `desensitization_attestation: true` 与 `provider_registration_ref`(R20 备案核验引用);
- 每次抽取返回 `{model_id, model_version, prompt_pack_version, config_digest, latency_ms}`,审计记录可回答"这条抽取出自哪个模型/提示词版本";
- 并发预算满 → `LLM_CONCURRENCY_BUDGET_EXCEEDED`(fail-closed,不排队堆积);超时 → `LLM_TIMEOUT`(不降级、不补造)。

## 6. 升级检查单(每次升级逐项打勾)

- [ ] `node scripts/deploy.mjs status` 全 PASS
- [ ] `node scripts/run-all-checks.mjs` 全绿(44+ 门)
- [ ] 升级前 `upgrade` 已自动备份(manifest sha256 校验通过)
- [ ] `medcius.env` 密钥未变化或经密钥系统轮换(轮换需治理窗口,见运维手册 §7)
- [ ] `resident-probe --once` 无 P1/P2
- [ ] 治理阶梯未变化(变化须治理委员会决议 + 证据签名,见 governance-mode.mjs)
46 changes: 46 additions & 0 deletions deploy/env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# ============================================================================
# Medcius 前置机环境模板(deploy/medcius.env)
# 复制为 medcius.env 后由密钥管理系统填充;**任何真实密钥不得提交进 git**。
# 变量语义见 docs/ops/PRODUCTIZATION-OPERATIONS.md §4/§3.2。
# ============================================================================

# --- 运行档位 / 治理 ---
NODE_ENV=production
MEDCIUS_PROFILE=production
# 发布治理阶梯(D6):生产禁止直接设 advisory_mode / certified_writeback(硬性报错)
MEDCIUS_GOVERNANCE_STAGE=retrospective_study

# --- 服务监听 ---
PORT=8443
HOST=0.0.0.0
MEDCIUS_ALLOWED_ORIGINS=https://workstation.hospital.internal

# --- 密钥(全部从密钥管理系统注入;禁止默认值上生产) ---
MEDCIUS_JWT_SECRET=
CLAUDE_MEDCIUS_PHI_SALT=
CLAUDE_MEDCIUS_ENCRYPTION_KEY=

# --- TLS(生产强制;server.mjs 拒绝无 TLS 的生产启动) ---
MEDCIUS_TLS_KEY=/opt/medcius/tls/medcius.key
MEDCIUS_TLS_CERT=/opt/medcius/tls/medcius.crt

# --- 数据目录(审计链 append-only + 语料库) ---
CLAUDE_MEDCIUS_DATA=/opt/medcius/data

# --- 院内规则包(具名/版本化,生产缺包时失败关闭) ---
MEDCIUS_RULE_PACK_DIR=/opt/medcius/rules

# --- LLM 推理路径(缺口六:validateLlmConfig 校验;C 档全托管直接拒绝) ---
# topology: A=全本地(目标态) | B=混合(仅脱敏抽取文本出域,判定链留本地)
MEDCIUS_LLM_TOPOLOGY=A
MEDCIUS_LLM_ENDPOINT=http://127.0.0.1:11434/v1
MEDCIUS_LLM_MODEL_ID=
MEDCIUS_LLM_MODEL_VERSION=
MEDCIUS_LLM_PROMPT_PACK_VERSION=
# B 档必填(核验留档引用 R20 备案记录)
# MEDCIUS_LLM_DESENSITIZATION_ATTESTATION=true
# MEDCIUS_LLM_PROVIDER_REGISTRATION_REF=R20:xxx

# --- 常驻探针(缺口六) ---
MEDCIUS_PROBE_INTERVAL_SEC=60
MEDCIUS_PROBE_LATENCY_BUDGET_MS=2000
29 changes: 29 additions & 0 deletions deploy/systemd/medcius.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[Unit]
Description=Medcius 前置机服务 (Doctor Workstation + REST/CDS Hooks + Workstation UI)
Documentation=file:///opt/medcius/current/deploy/DEPLOYMENT.md
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=medcius
Group=medcius
WorkingDirectory=/opt/medcius/current
EnvironmentFile=/opt/medcius/medcius.env
ExecStart=/usr/bin/node scripts/serve.mjs
Restart=on-failure
RestartSec=5

# —— 加固基线(fail-closed 部署纪律)——
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/medcius/data /opt/medcius/backups
RestrictSUIDSGID=true
ProtectKernelTunables=true
ProtectControlGroups=true
MemoryDenyWriteExecute=false

[Install]
WantedBy=multi-user.target
50 changes: 50 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Medcius 前置机编排(缺口六)
# 用法:
# 沙箱评估(无 TLS、无真实数据): docker compose up medcius-sandbox
# 医院生产(TLS + 密钥注入): docker compose --profile hospital up medcius
# 纪律:密钥与数据只经挂载/env 注入,不进镜像层;C 档全托管 LLM 拓扑在配置校验层直接拒绝。

services:
medcius-sandbox:
image: medcius:0.7.0-pilot
build: .
profiles: ["sandbox"]
ports:
- "8080:8080"
environment:
NODE_ENV: development
MEDCIUS_PROFILE: demo
MEDCIUS_ALLOW_ANONYMOUS: "true"
MEDCIUS_LLM_TOPOLOGY: "A"
MEDCIUS_LLM_ENDPOINT: "http://host.docker.internal:11434/v1"
MEDCIUS_LLM_MODEL_ID: "local-model"
MEDCIUS_LLM_MODEL_VERSION: "dev"
MEDCIUS_LLM_PROMPT_PACK_VERSION: "dev"
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8080/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 30s
timeout: 5s
retries: 3

medcius:
image: medcius:0.7.0-pilot
build: .
profiles: ["hospital"]
restart: unless-stopped
ports:
- "8443:8443"
env_file:
- ./deploy/medcius.env # 由 deploy/medcius.env.template 复制后经密钥管理系统填充
volumes:
- /opt/medcius/data:/opt/medcius/data # 审计链 + 语料库(append-only,院内留存)
- /opt/medcius/backups:/opt/medcius/backups
- /opt/medcius/tls:/opt/medcius/tls:ro # 院方签发证书(TLS 强制)
- /opt/medcius/rules:/opt/medcius/rules:ro # 院内审批规则包(具名/版本化)
healthcheck:
test: ["CMD", "node", "-e", "fetch('https://127.0.0.1:8443/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 30s
timeout: 5s
start-period: 20s
retries: 3
# 加固声明:容器内非 root(见 Dockerfile USER);只读文件系统可按需追加
# read_only: true(数据目录已外置挂载)
Loading
Loading