跳转到内容

临床试验协议:从自然语言到条件树

“未接受过既往靶向治疗,除非辅助治疗在复发前 12 个月以上完成。”协调员读这句话时,会自然追问:unless 修饰哪一段?12 个月从哪一个日期开始算?模型若直接给“符合/不符合”,这些决定都被藏进了一个不可检查的答案。

这类句子不是一个标签,而是一棵包含逻辑、时间、例外和医学概念的条件树。直接问模型“患者是否符合”会把解析错误与患者证据错误混在一起,无法定位。

ClinicalTrials.gov API v2 提供结构化 study record 和 OpenAPI 描述,并按工作日刷新;客户端应记录 dataTimestamp 和协议版本。S06S07 但 eligibilityCriteria 仍包含大量自然语言。TrialGPT 将匹配拆到 criterion 级,是重要进步;生产系统还需先把每条 criterion 变成版本冻结、可人工校验的结构。S30S31

痛点包括:列表层级丢失、AND/OR 范围模糊、否定嵌套、时间窗参照点不清、单位省略、不同版本协议变化。PDF/OCR 还可能把项目符号和上下标读错。

第一阶段是忠实切分:保留章节、列表、原文、页码/字段路径和版本,不做医学推断。第二阶段是语义结构化:抽取 inclusion/exclusion、operator、children、concept、value/unit、temporal constraint、exception 和 required evidence。

解析器只提出草案;研究协调员在 UI 中对照原文修改并“冻结”。匹配任务永远引用冻结版本。协议更新时生成 diff,不覆盖旧树。

一个条件树节点示例:all(age >= 18, diagnosis == NSCLC, any(EGFR_exon19, EGFR_L858R), not(prior_targeted_therapy within lifetime))。这里 any 的作用域必须来自原文结构,不能由模型想当然补齐。

方案 强项 主要缺口
ClinicalTrials.gov API 官方、版本和结构入口 S06 标准文本仍需语义解析
TrialGPT Matching criterion 级解释 S30 需本地协议版本与人工冻结
纯正则 数字、单位、简单年龄稳定 嵌套逻辑和例外脆弱
LLM structured output 泛化强 必须 schema、原文定位和人审
Terminal window
python3 labs/run_lab.py --lab 09

实验解析一个受控的小型语法:age>=18 AND (egfr=ex19 OR egfr=l858r),生成条件树并对 3 个合成病例求值。它不是通用协议 NLP;其价值是展示解析与求值分离、原文与 AST 同时保留。

先对照原句看一次 AST,再把括号或 OR 的位置改掉,最后比较哪一类病例的结果变化。实验重点不是让语法覆盖更多文本,而是直观看到“作用域错一层,资格就可能反过来”。

考虑合成标准:“Age ≥ 18 years; histologically confirmed advanced NSCLC with EGFR exon 19 deletion or L858R; no prior EGFR-targeted therapy, except adjuvant therapy completed >12 months before recurrence.” 这里至少有年龄、疾病、分期、组织学确认、两种变异、既往用药、治疗意图、完成到复发的时间差和例外。

忠实切分阶段只恢复列表和原文跨度。语义阶段生成顶层 all:年龄条件、诊断条件、any(ex19,L858R) 和包含例外的既往治疗条件。每个叶节点声明 Evidence requirement。解析器若不知道“advanced”映射哪些 stage,不自行补充,写 concept_status=needs_review。如果复发日期缺失,例外无法求值,最终是 unknown。

这比把整句嵌入向量库多做了很多工作,却换来关键能力:协调员可以逐节点修改,verifier 可以逐节点测试,协议版本更新时可以显示 AST diff,错误能定位到解析、取证还是求值。

第一层文档摄取处理 HTML/API/PDF,保留页码、标题和列表层级;第二层 criterion segmenter 区分 inclusion/exclusion 并生成稳定 id;第三层模型/规则把单条 criterion 转成 schema;第四层 semantic validator 检查单位、算子、逻辑作用域、否定、时间参照和术语。人审冻结后才进入 matching。

模型 structured output 适合第三层,但必须让它返回 source span。对于年龄、数值、单位等可确定部分,规则可与模型并行抽取并做差异检查。两者不一致时不自动选择模型。对每种错误建立 taxonomy:split、polarity、scope、concept、numeric、temporal、exception、hallucinated。评测不是只看整条 JSON 是否完全一致,而是分字段并特别关注会改变资格判断的严重错误。

ClinicalTrials.gov 数据会更新,S06 研究协议也可能 amendment。ProtocolVersion 包含来源、NCT id、抓取时间、内容 hash 和状态;每条 TrialCriterion id 绑定版本。新版本进入 diff 队列:新增/删除/修改、逻辑变化、数值变化、文本排版变化。人审决定新版本何时生效,进行中的任务仍能重放旧版本。

若只是 API 格式变化,migration adapter 负责兼容;若 eligibility 语义变化,必须重新评估受影响任务。把两者混为“更新了一次数据”会让历史审计失真。官方迁移指南正说明客户端不能假定接口永久不变。S07

协议是数据,不是指令。解析 prompt 将原文置于带来源的 data block,输出只能匹配 schema;document 中出现“ignore previous instructions”也只能成为文本。更重要的是解析 Agent 没有患者读取和写工具,即便被影响也无法扩大权限。chapter 22 的红队场景会把这条不变量变成测试。

人工审核界面左侧显示原文和定位,右侧显示条件树;点击叶节点高亮对应 span。审核者可改算子、概念、时间窗,所有修改以 patch 事件记录。冻结按钮展示版本、未解决 warning 和受影响 criteria,避免在模糊字段仍存在时误发布。机器 confidence 只作排序,不隐藏低置信结果。

章末练习:把本章示例中的 or 改成“EGFR exon 19 deletion or L858R with no prior targeted therapy unless…”,画出两种可能作用域并说明各自会影响哪些患者。只要原文无法唯一决定,答案就应是 protocol_ambiguous + 人工确认,而不是选择更常见的解释。

  • 模型输出 JSON 通过 schema 不表示逻辑作用域正确。
  • 不能用当前协议重算历史判断而不保留旧版本。
  • OCR 置信度低、逻辑歧义和未知术语必须进入人工队列。
  • 系统只做候选筛选,不把结构化树视为具有法律效力的协议替代物。

完整的来源类型、用途与边界见教材来源注册表

  • S06 ClinicalTrials.gov API
  • S07 API Migration Guide
  • S30 TrialGPT paper
  • S31 TrialGPT code