数据合同:统一 Evidence 与资格判定
两个 Agent 都写下“EGFR 阳性”。一个只给了模型总结,另一个给了报告版本、样本时间和原文位置。页面上是同一句话,工程上是两种对象–前者没法复核,后者才能进入资格判断。
一个团队的“证据”如果有时是向量库文本、有时是 FHIR JSON、有时又是模型自然语言,系统就无法可靠地合并、引用和审计。合同要做的事,是让不同模态和不同 Agent 在同一组可验证语义上协作。
为什么 schema 比 prompt 更基础
Section titled “为什么 schema 比 prompt 更基础”FHIR 已经把临床信息拆成资源,DICOM 规范了影像对象,OMOP 统一了观察性分析表。S01S04S05 这些标准存在本身就说明,医疗数据靠一段自然语言提示统一不了。生成式系统常犯的错是只约束模型最终输出的 JSON,不约束来源定位、版本和缺失语义,结果 JSON 语法完全正确,事实却追溯不回去。
四个公共合同
Section titled “四个公共合同”Evidence
Section titled “Evidence”至少包含 evidence_id, patient_id, source_type, source_uri, observed_at, retrieved_at, source_version, locator, normalized_fact, confidence, limitations, content_hash。locator 对 FHIR 可以是资源 id 或 path,对 DICOM 是 Study/Series/SOP UID 和区域,对 PDF 是页码与字符范围。
TrialCriterion
Section titled “TrialCriterion”保存原文、inclusion/exclusion、标准化概念、逻辑树、时间窗、数值和单位、否定、例外、所需模态、协议版本和人工审核状态。原文永远不被规范化字段覆盖。
CriterionAssessment
Section titled “CriterionAssessment”结论只有 met / not_met / unknown / conflict,并带 evidence_ids、规则或模型版本、机器理由、验证器决策和人工签署。unknown 是缺证据,conflict 是证据彼此矛盾,两者不能混成一个“低置信度”。
ToolManifest 与 TaskPackage
Section titled “ToolManifest 与 TaskPackage”ToolManifest 声明 typed input/output、read/write、风险、患者 scope、副作用、幂等键、超时重试、预算和审批策略。TaskPackage 封装初始环境、工具清单、任务指令、隐藏 verifier、成功与安全条件。
代表方案对比
Section titled “代表方案对比”| 方案 | 强项 | 对本项目的不足 |
|---|---|---|
| FHIR Resource/Profile | 医疗交换语义和扩展机制 | 不描述 Agent 轨迹与 verifier S01 |
| OpenAPI/JSON Schema | 接口和字段验证 | 不知道 PHI、临床时间窗和来源血缘 |
| MCP Tool | 工具发现与调用协议 | 需外加患者 scope、副作用和审批 S24S25 |
| MedAgentBench task | 环境与任务成功 | 仍需本地业务合同和版本治理 S32 |
python3 labs/run_lab.py --lab 04实验用标准库验证一条 Evidence 是否有稳定 id、来源版本、定位、哈希和患者范围,然后检测 CriterionAssessment 引用的 evidence id 是否真实存在。把 patient_id 改成另一个人,验证器会拦住跨患者的证据拼接。
第二次运行不要再看一遍成功,只改 patient_id 就好。验证器如果仍然接受结果,说明它验的只是 JSON 形状,没有验“这份事实到底属于谁”。
从原始 FHIR 到可审计判断
Section titled “从原始 FHIR 到可审计判断”假设 FHIR Observation Observation/anc-17/_history/3 表示某次 ANC。Evidence 里不能只留 value=1.7,还要保存编码系统与 code、原始 valueQuantity、UCUM unit、effectiveDateTime、status、FHIR server、resource version 和查询时间。规范化事实可以写成 anc_g_per_l=1.7,但 source locator 仍指向原值–这样换算器升级时,团队可以重算规范化事实而不丢原始依据。
对应的 TrialCriterion 保存协议原文“ANC ≥ 1.5 × 10^9/L within 14 days prior to enrollment”,结构化成数值算子、单位、时间窗和参照事件。CriterionAssessment 引用该 Evidence,记录求值器版本与 met。参照事件还没确定的话,判断应该是 unknown,不能拿当前日期顶上。
这个例子里其实同时出现了三种版本:来源资源版本、协议版本和算法版本。只用一个 version 字段一定会产生歧义,建议分成 source_version、contract_version、transform_version、decision_version,审计记录里写清用的是哪个组合。
Schema 演进策略
Section titled “Schema 演进策略”合同一旦被任务、数据库、网站和评测共同使用,随手改字段的代价就很高。新增可选字段通常向后兼容;改变枚举意义、删除字段或更换单位语义属于破坏性变更。用语义版本,并在消息里带 schema_version。读取端先同时支持新旧两版,跑迁移与双读比较,再停止旧写入;历史审计仍保留原版本的解析器。
四态枚举也别随手扩成十几个近义状态。要表达“等待实验室结果”,可以在 status=unknown 下加 reason_code=missing_lab 和工作流 next_action,不要把临床判断状态和任务状态混成一个枚举。confidence 同理,它是模型或抽取器的辅助属性,不应该决定 status。
ToolManifest 的真实作用
Section titled “ToolManifest 的真实作用”拿 read_fhir_observations 举例:input schema 要求 patient id、code、date range,manifest 声明 read-only、risk=L2、需要 patient scope、最大返回条数、timeout、重试条件和日志脱敏字段。create_screening_draft 是内部可逆写入,risk=L3,要求 approval id 和 idempotency key。contact_patient 根本不注册。模型调不了一个没有暴露的工具,这比在提示词里写“不要联系患者”可靠得多。
TaskPackage 把这些工具在固定环境里组合起来。隐藏 verifier 不只看最终 status,还检查调用参数有没有扩大日期或患者范围、Evidence ids 能不能解析、写入次数是不是一次、超时后有没有重复提交。合同因此把开发、测试、安全和产品串在了一起,不只是一份 API 文档。
语义验证与形状验证
Section titled “语义验证与形状验证”JSON Schema 能发现 patient_id 缺失,发现不了 Evidence 指向另一个患者;能限制 status 枚举,判断不了入排标准的否定方向是不是解释反了。生产验证分三层:形状验证,引用/权限/版本这类跨对象不变量,领域规则和人工复核。模型输出要依次通过,任何一层失败都返回结构化错误,不给模型用自然语言“解释过去”的机会。
合同评审清单
Section titled “合同评审清单”评审时逐字段问下去:它的来源是谁?是否包含 PHI?能否从别的字段推导?版本如何变化?失败或未知怎样表达?日志是否需要它?谁能看?保留多久?会不会被模型任意生成?一个字段如果这些问题都答不上来,它多半只是为了 Demo 方便加进来的数据债务。
- schema validation 只能验证形状,不能证明医学事实正确。
- 置信度若未校准只是一个数字,不能替代四态和人工审核。
- content hash 能发现内容变化,不能证明来源可信。
- 合同版本升级必须有迁移和双读期;直接改字段会让历史轨迹不可重放。
完整的来源类型、用途与边界见教材来源注册表。
资料与延伸阅读
Section titled “资料与延伸阅读”- HL7 FHIR R4:先熟悉资源、引用和 profile,再为 Agent 补上来源血缘与任务语义。
- DICOMweb 与 OMOP CDM:分别理解影像对象和观察性分析数据的合同边界。
- MCP Specification:工具协议解决“怎么调用”,本章的 ToolManifest 额外约束“谁能调用、会产生什么副作用”。
- MedAgentBench:可作为任务包和环境验证的研究参考。