跳转到内容

试验召回与排序:先找全,再排准

如果搜索结果第一名标题里同时出现 “EGFR” 和 “NSCLC”,却已经停止招募、地点也不匹配,把它交给下一步模型只会浪费预算。反过来,过早把位置缺失的试验删掉,也可能把需要人工核对的候选永久丢掉。

从十万级试验记录中直接让 LLM 阅读全部内容既昂贵又不可靠。合理链路是先用高召回检索缩小候选,再用较贵的逐标准判断和排序精排。TrialGPT 正是 retrieval-matching-ranking 三段式。S30

传统检索依赖关键词和 BM25,精确、可解释,但同义词和描述变化会漏召回;向量检索能捕捉语义,却可能把“研究主题相似”误当“患者符合”。TREC Clinical Trials 推动了患者描述到试验的检索评测,但离实时站点状态和本地流程仍有距离。S12

医疗试验还有硬过滤:疾病、年龄、地点、招募状态、日期、干预类型。把这些交给向量相似度会浪费候选预算。反过来,过早用结构化条件严筛,又会因数据缺失漏掉相关试验。

  1. 用官方 API 拉取并记录版本、状态、地点和更新时间。S06
  2. 硬过滤只处理高置信度字段;未知不立即排除。
  3. 关键词/BM25 与向量召回并行,采用 reciprocal rank fusion 合并。
  4. 轻量 cross-encoder 或规则重排。
  5. 对 top-k 执行逐标准匹配,再生成 trial-level 排序。

召回阶段看 Recall@k,排序阶段看 nDCG@k/MRR,最终还看“候选是否真的可行动”。测试集按时间切分,避免同版本近重复泄漏。

方案 优点 失败模式
关键词/BM25 可解释、专名强 同义词、长语义弱
Dense retrieval 语义召回 相似不等于合格
Hybrid + RRF 互补、无需统一分数 索引和调参更复杂
TrialGPT Retrieval 研究基线与完整链路 S30S31 仍需本地数据/成本复现
Terminal window
python3 labs/run_lab.py --lab 10

实验在 5 条合成试验上分别计算词项分数与概念重叠,用 RRF 合并并计算 Recall@3。删掉疾病硬过滤,观察非肺癌但文本相似的试验如何进入前排。

先保留混合召回的候选清单和排名,再删掉一个硬过滤,最后只比较“哪个错误候选为什么冒出来”。不要从这个五条数据集推导真实 Recall,也不要把能跑通的例子写成临床检索效果。

一个患者怎样从数万条记录到 20 个候选

Section titled “一个患者怎样从数万条记录到 20 个候选”

患者 Evidence 显示晚期 NSCLC、EGFR exon 19 deletion、所在城市和年龄。Query Builder 不把整份病历交给搜索,而是生成最小检索表示:疾病标准概念、关键变异、可选治疗线、年龄和地点。首先从 ClinicalTrials.gov snapshot 过滤已过期记录和明确不可能的研究类型,但招募状态或地点缺失不直接排除,而是保留 freshness warning。S06

关键词通道擅长 EGFR, exon 19, NCT id 和药物名;dense 通道捕捉“non-small cell lung carcinoma”与“NSCLC”;结构通道处理 condition、age 和 location。三个通道各自返回带分数列表。RRF 用排名而非不可比原始分数合并,随后规则/交叉编码器重排。top-20 才进入昂贵的协议解析检查与 criterion matching。

若最终正确试验没出现,错误归 retrieval;若出现但排在很后,错误归 ranking;若在 top-k 却被资格判断错,错误归 matching。这种分层比只看最终“有没有推荐”更可调试。

索引文档应以 trial version 为单位,字段分成公开元数据、brief/official title、conditions、interventions、eligibility、locations 和 contacts;但 contacts 等敏感/动态字段未必需要进入向量。索引 manifest 记录 API data timestamp、schema、embedding model、chunker、document count 和 hash。更新采用新索引构建-评测-原子切换,不能边更新边让同一任务跨两个版本。

试验状态是动态事实。工作单展示“快照时为 Recruiting”并提供重新核验按钮;签署前可强制 refresh。缓存 TTL 不用拍脑袋统一设置,按字段和业务风险定义。历史评测必须冻结 snapshot,否则同一个测试第二天就可能得到不同答案。

一个 patient 可能有多个相关试验,gold 也可能不完整。Recall@k 需要明确 relevant 定义:文本主题相关、专家认为值得筛选,还是最终确认符合?这三者不同。检索层更适合“值得进一步筛选”的宽标签,资格层再判断条件。

时间切分防止同一 trial amendment 泄漏;按疾病/机构分析亚组;报告索引覆盖、无结果率和每 query 候选数。TREC Clinical Trials 提供公开比较背景,S12 但真实部署要补本地人群、地点与时间变化。TrialGPT 的研究结果是方法参考,不可复制成 MedAgent Forge 的数字。S30

结构过滤、BM25 和小 embedding 可批量/缓存;逐 criterion LLM 调用昂贵。检索层先减少候选,matching 对 criteria 并行但受预算与限流;常用协议解析结果缓存到冻结版本。缓存 key 包含数据、模型和规则版本,不能只用 trial id。

为了速度删除高召回通道往往得不偿失。更合理的优化是观察每层漏召回和耗时:若 dense 只贡献极少独有相关项,可以缩小;若基因同义词大量漏掉,优先补词汇/查询表示,而不是让生成模型反复尝试。

排名分数不解释为“入组概率”。工作台展示为何进入候选:疾病匹配、变异相关、位置/状态快照,以及哪些资格尚未评估。排序只是审核顺序,不是医学结论。

一次离线评测要这样做:冻结 ClinicalTrials.gov snapshot、患者 queries、relevance judgments 和索引 manifest;分别运行 BM25、dense、hybrid;调参只用 validation,test 一次性报告。列出每个相关试验首次出现排名和漏召回原因。若 hybrid 改善只是因候选更多,计算相同 top-k/成本下收益。

章末练习:构造一个关键词完全匹配但疾病错误的试验,以及一个同义词匹配但真正相关的试验;让硬过滤、关键词和 dense 各自产生可解释结果,再用 RRF 合并。检查任何 hard filter 是否把未知误当 false。最后记录索引 snapshot 和查询成本,使第二次实验能重放同一排名。对每个漏召回写具体原因与下一项可证伪改动,不用“换更强 embedding”概括。改动前后都保留结果清单。

  • top-k 中没有正确试验,后续模型再强也无法恢复。
  • 招募状态和地点是动态事实,必须带抓取时间,不能缓存成永真。
  • 公开相关性标签不等同于入组资格金标。
  • 本章不报告本项目检索效果;只有接入锁定数据版本后才能产生指标。

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

  • S06 ClinicalTrials.gov API
  • S12 TREC Clinical Trials
  • S30 TrialGPT paper
  • S31 TrialGPT code