medical-evidence-grading · git:20260912.c74de71 · 2026-09-12 · sha256 4f45a647eb984ac9

medical-evidence-grading git:20260912.c74de71A

Immutable. This exact content is served forever at /api/v1/blob/4f45a647eb984ac9.

---
name: medical-evidence-grading
description: 医学证据等级排序与编排层 skill。当用户请求"证据等级 / GRADE / 证据排序 / 医学证据评估 / evidence grading / quality of evidence / 系统评价排序 / 临床证据排序 / 证据金字塔"时触发。本 skill 不直接调用 NCBI/Europe PMC/ClinicalTrials API,而是编排 6 个底层原子 skill(pubmed-eutils / europepmc-search / clinical-trials-v2 / aact-bulk-trials / bioc-fulltext-fetch / pubtator-entity-search)的召回结果,按 ChatGPT 推荐的证据金字塔(指南 > Meta > RCT > Cohort > Case-Control > Case Report)做 GRADE 自动评级,自动剔除 editorial / letter / comment。优先使用 BioMCP MCP server 做 fan-out(genomoncology/biomcp),无 BioMCP 时走自建 slow-path 兜底。
---

# medical-evidence-grading · 医学证据等级排序与编排层

## 一句话定义
对底层 6 个原子 skill 召回的医学文献做"证据金字塔排序",输出带 GRADE A/B/C/D 评级的文献集与汇总报告;同时充当智能调度器,根据问题类型(疗效/诊断/病因/预后/不良反应)自动选择最佳原子 skill 与 PubMed Clinical Queries filter。

---

## 何时调用

**调用本 skill**:
- 用户问"找血液 IFI 最新指南" / "查 venetoclax AML 高质量证据" / "找 CAR-T 不良反应 RCT"
- 已有一批 PMID,需要按证据等级重新排序
- 给临床声明做"反向证据交叉验证"
- 上游 ifi-market-sizing-skill 需要"数据回补"(找指南/RCT 替代低质量来源)
- 下游 evidence-appendix-sync 需要按 GRADE 组织参考文献

**不调用本 skill**(直接调原子 skill):
- 只需要 PubMed 摘要 → `pubmed-eutils`
- 只需要 ClinicalTrials.gov 试验列表 → `clinical-trials-v2`
- 只需要全文段落 → `bioc-fulltext-fetch`
- 只需要实体标注(基因/疾病) → `pubtator-entity-search`
- 全库批量分析 → `aact-bulk-trials`

---

## 与其他 skill 协作矩阵

| 协作方向 | Skill | 用途 | 数据流 |
|---------|-------|------|--------|
| 上游调用方 | `ifi-market-sizing-skill` | 市场规模研究中的"数据回补" | 接收疾病关键词 → 返回分级文献 |
| 上游调用方 | `disease-market-sizing-orchestration` | 市场调研启动阶段的证据搜集 | 接收 PICO 问题 → 返回 Top 20 高等级文献 |
| 下游被调方 1 | `pubmed-eutils` | PubMed 检索 + Clinical Queries filter | 调 esearch/efetch 拿 PMID + publication_type |
| 下游被调方 2 | `europepmc-search` | EuropePMC 检索 + 全文链接 | 拿 PMCID + open access 标记 |
| 下游被调方 3 | `clinical-trials-v2` | ClinicalTrials.gov v2 API | 拿 NCT ID + phase + status |
| 下游被调方 4 | `aact-bulk-trials` | AACT 数据库 PostgreSQL bulk | 大批量试验过滤(n>=500 等) |
| 下游被调方 5 | `bioc-fulltext-fetch` | BioC 全文段落抽取 | 验证摘要中的样本量数字 |
| 下游被调方 6 | `pubtator-entity-search` | PubTator 实体标注 | 给文献打疾病/药物 tag |
| 下游接收方 | `evidence-appendix-sync` | 报告附录 C 同步 | 输出 grade-A/B/C/D 分级清单 |

```
                   用户/上游 skill
                         │
                         ▼
              [medical-evidence-grading]
                  ↙          ↘
        BioMCP fast-path   自建 slow-path
            │                    │
       (单次 fan-out)      并发调 6 个原子 skill
            ↓                    ↓
        统一去重 (PMID > PMCID > DOI)
                    ↓
           publication_type 解析
                    ↓
          GRADE 评级算法 (A/B/C/D/排除)
                    ↓
        evidence-appendix-sync (报告附录)
```

---

## BioMCP fast-path vs 自建 slow-path 双模式

### Fast-path: BioMCP MCP server(优先)
[genomoncology/biomcp](https://github.com/genomoncology/biomcp) 已实现:
- PubMed + Europe PMC + ClinicalTrials.gov + PubTator 一次性 fan-out
- 内置去重(PMID/DOI 归一)
- 暴露为 MCP `article_searcher` / `trial_searcher` / `variant_searcher` 等工具

```python
def detect_biomcp_available() -> bool:
    """
    检测 BioMCP MCP server 是否可用:
    1. 检查 ~/.claude/mcp.json (or ~/.codex/config.toml) 中是否有 'biomcp' 服务器配置
    2. 或检查环境变量 BIOMCP_ENDPOINT
    3. 或检查 `mcp__biomcp__*` 工具是否在已加载工具列表
    4. 都没有 → 返回 False,走 slow-path
    """
    import os, json
    from pathlib import Path

    if os.environ.get("BIOMCP_ENDPOINT"):
        return True

    for cfg in [
        Path.home() / ".claude" / "mcp.json",
        Path.home() / ".claude" / "settings.json",
        Path.home() / ".codex" / "config.toml",
    ]:
        if cfg.exists():
            text = cfg.read_text(encoding="utf-8")
            if "biomcp" in text.lower():
                return True
    return False
```

### Slow-path: 自建 fan-out(兜底)
当 BioMCP 不可用,本 skill 自己:
1. 并发调 `pubmed-eutils` + `europepmc-search` + `clinical-trials-v2`(asyncio.gather)
2. 用 PMID > PMCID > DOI 三段优先级去重(创建 `evidence_id` 哈希)
3. 调 `pubmed-eutils` efetch 拿 publication_type
4. 调 `bioc-fulltext-fetch` 抽样本量(只对疑似 RCT/Cohort)
5. 应用 GRADE 算法

**性能对比**:
| 模式 | 100 条文献延迟 | API 调用数 | 缓存命中后 |
|------|---------------|-----------|-----------|
| Fast-path (BioMCP) | ~3-5s | 1 | <500ms |
| Slow-path (自建) | ~15-25s | 6-10 | <500ms |

---

## GRADE 自动评级规则表

```
输入: publication_type(MeSH 标签数组) + abstract + 样本量(可选)

┌─────────────────────────────────────────────────────────────────┐
│  Grade A (最高质量,优先纳入)                                    │
│  ├─ "Practice Guideline" / "Guideline"                          │
│  ├─ "Consensus Development Conference"                          │
│  ├─ "Meta-Analysis"                                             │
│  ├─ "Systematic Review"                                         │
│  └─ "Randomized Controlled Trial" AND n >= 1000                 │
├─────────────────────────────────────────────────────────────────┤
│  Grade B (高质量)                                               │
│  ├─ "Randomized Controlled Trial" AND n < 1000                  │
│  ├─ "Clinical Trial, Phase III"                                 │
│  ├─ "Multicenter Study" AND prospective                         │
│  └─ "Cohort Studies" AND n >= 500                               │
├─────────────────────────────────────────────────────────────────┤
│  Grade C (中等质量)                                             │
│  ├─ "Cohort Studies" AND n < 500                                │
│  ├─ "Case-Control Studies"                                      │
│  ├─ "Cross-Sectional Studies"                                   │
│  ├─ "Clinical Trial, Phase II"                                  │
│  └─ "Observational Study"                                       │
├─────────────────────────────────────────────────────────────────┤
│  Grade D (低质量,谨慎引用)                                      │
│  ├─ "Case Reports"                                              │
│  ├─ "Review" (非 systematic)                                    │
│  ├─ "Narrative Review"                                          │
│  ├─ "Clinical Trial, Phase I"                                   │
│  └─ Preprint (medRxiv / bioRxiv) → Grade D-                     │
├─────────────────────────────────────────────────────────────────┤
│  EXCLUDED (自动剔除)                                            │
│  ├─ "Editorial"                                                 │
│  ├─ "Letter"                                                    │
│  ├─ "Comment"                                                   │
│  ├─ "News"                                                      │
│  ├─ "Biography"                                                 │
│  └─ "Retracted Publication" (除非用户明确要求)                  │
└─────────────────────────────────────────────────────────────────┘
```

### 降权信号(Grade 降一级)
- 单中心研究(non-multicenter)
- 样本量 < 100 且非罕见病
- 期刊影响因子 < 2.0(可选,需 NLM Catalog API)
- 发表 > 10 年前且无更新指南
- 无利益冲突声明

### 升权信号(Grade 升一级,极少用)
- 大型登记研究 n > 10000
- NEJM / Lancet / JAMA / BMJ 顶刊一级证据

---

## Clinical Queries 推荐策略表

PubMed 提供 Clinical Queries filter,本 skill 根据"问题类型"自动选择:

| 问题类型 | Clinical Queries filter | 等价 PubMed query | 优先 grade |
|---------|------------------------|-------------------|-----------|
| 疗效 (Therapy) | `therapy/narrow` | `randomized controlled trial[pt]` | A-B |
| 诊断 (Diagnosis) | `diagnosis/narrow` | `sensitivity[ti] OR specificity[ti]` | A-C |
| 病因 (Etiology) | `etiology/narrow` | `cohort studies[mh] OR risk[ti]` | B-C |
| 预后 (Prognosis) | `prognosis/narrow` | `prognosis[mh] OR survival[ti]` | B-C |
| 不良反应 (Harm) | `etiology/broad` | `adverse effects[sh]` | A-C |
| 临床预测 (Prediction) | `clinical_prediction_guides/narrow` | `decision rule[tw]` | A-B |
| 指南 (Guidelines) | n/a | `practice guideline[pt]` | A only |
| 系统综述 (SR) | n/a | `systematic[sb]` | A only |

`recommend_search_strategy(question_type)` 函数返回上表对应行的 query 模板。

---

## 5 核心函数签名

```python
# ============== 1. 主入口:综合证据搜索 ==============
def evidence_search(
    query: str,
    target_grade: str = "all",  # 'guideline_only' | 'rct_or_above' | 'all'
    max_results: int = 200,
    include_preprints: bool = False,
    include_trials: bool = True,
    date_range: str = "10years",  # 'all' | '5years' | '10years' | '2020:2025'
    use_biomcp_if_available: bool = True,
) -> dict:
    """
    返回:
    {
        "total": 156,
        "by_grade": {"A": 12, "B": 34, "C": 67, "D": 43, "excluded": 21},
        "fast_path_used": True,  # BioMCP 是否命中
        "cache_hit": False,
        "elapsed_seconds": 4.2,
        "results": [
            {
                "evidence_id": "PMID:38234567",
                "pmid": "38234567",
                "pmcid": "PMC10891234",
                "doi": "10.1056/NEJMoa2024xxx",
                "title": "...",
                "authors": [...],
                "journal": "NEJM",
                "year": 2024,
                "publication_types": ["Randomized Controlled Trial", "Multicenter Study"],
                "sample_size": 1450,
                "is_preprint": False,
                "grade": "A",
                "grade_rationale": "RCT n>=1000",
                "abstract": "...",
                "source": "pubmed",  # 或 'europepmc' / 'clinicaltrials' / 'biomcp'
            },
            ...
        ]
    }
    """

# ============== 2. 给现有 PMID 列表打分 ==============
def grade_pmid_list(
    pmid_list: list[str],
    fetch_fulltext_for_sample_size: bool = False,
) -> list[dict]:
    """
    输入: ['38234567', '37123456', ...]
    输出: 每条带 grade / grade_rationale 的字典
    用 efetch 拿 publication_type;若 fetch_fulltext_for_sample_size=True,
    对疑似 RCT/Cohort 调 bioc-fulltext-fetch 验证 n。
    """

# ============== 3. 推荐检索策略 ==============
def recommend_search_strategy(
    question_type: str,  # 'therapy' | 'diagnosis' | 'etiology' | 'prognosis' | 'harm' | 'guideline' | 'sr'
    user_query: str = "",
) -> dict:
    """
    返回:
    {
        "clinical_queries_filter": "therapy/narrow",
        "pubmed_query_template": "({user_query}) AND randomized controlled trial[pt]",
        "europepmc_query_template": "({user_query}) AND PUB_TYPE:\"Randomized Controlled Trial\"",
        "expected_grade_distribution": {"A": "30%", "B": "50%", "C": "20%"},
        "recommended_skill": "pubmed-eutils",  # 可能是 clinical-trials-v2 等
        "tips": ["建议加 hasabstract filter", "排除 case report"],
    }
    """

# ============== 4. 反向证据交叉验证 ==============
def cross_validate_evidence(
    claim: str,  # 例: "Posaconazole 预防 IFI 比 fluconazole 更有效"
    top_k: int = 10,
    min_grade: str = "B",
) -> dict:
    """
    用 NLP 拆解 claim → PICO → 反向搜索高等级证据。
    返回:
    {
        "claim": "...",
        "pico": {"P": "high-risk neutropenia", "I": "posaconazole", "C": "fluconazole", "O": "IFI incidence"},
        "supporting": [...top_k 文献,grade>=min_grade...],
        "contradicting": [...],
        "verdict": "supported" | "contradicted" | "mixed" | "insufficient",
        "confidence": 0.87,
    }
    """

# ============== 5. 证据汇总报告 ==============
def evidence_summary(
    graded_list: list[dict],
    output_format: str = "markdown",  # 'markdown' | 'html' | 'json'
    output_path: str | None = None,  # 若提供,写入 evidence/summary.md
) -> str:
    """
    生成:
    - 各等级数量统计表
    - Top 5 grade-A 文献的摘要表
    - 关键发现摘要(用 sequential-thinking 提炼)
    - 引用建议(优先 grade-A,补充 grade-B)
    - 缺口提示(若 grade-A 数量<3,提示扩展检索)
    """
```

---

## 输出格式示例

### 分级文献集 (JSON)
见 `evidence_search` 返回结构。

### 汇总报告 (Markdown)
```markdown
# 证据汇总报告 · venetoclax AML 一线治疗

**生成时间**: 2026-04-25  **总计**: 156 篇  **检索路径**: BioMCP fast-path

## 等级分布
| Grade | 数量 | 占比 |
|-------|------|------|
| A (指南/Meta/大型 RCT) | 12 | 7.7% |
| B (RCT/Phase 3/大型队列) | 34 | 21.8% |
| C (中小型队列/对照) | 67 | 42.9% |
| D (病例/综述/Preprint) | 43 | 27.6% |
| 已剔除 (社论/letter) | 21 | -- |

## Top 5 Grade-A 证据
1. **DiNardo et al. NEJM 2020** · PMID 32786187 · VIALE-A 三期 RCT (n=431)
2. **NCCN Guidelines AML v3.2024** · 临床实践指南
3. **Konopleva et al. Cancer Discov 2016 Meta-analysis** · PMID 27520294
...

## 关键发现
- Grade-A 证据一致支持 venetoclax+azacitidine 优于 azacitidine 单药 (OS HR 0.66)
- 老年/不耐受强化化疗人群证据最充足
- 缺口: 中国人群头对头 RCT 数据稀缺(仅 3 篇 grade-B)

## 引用建议
- 主结论引用 ≥3 篇 grade-A
- 补充安全性数据可用 grade-B 大型队列
- 不建议引用 grade-D(除非"首次报道某不良反应")
```

---

## 缓存层

```python
# SQLite 本地缓存,7 天有效期
CACHE_DB = Path.home() / ".claude" / "skills" / "medical-evidence-grading" / "cache.sqlite"

# 表结构
CREATE TABLE evidence_cache (
    query_hash TEXT PRIMARY KEY,    -- SHA256(query + target_grade + date_range)
    query_text TEXT,
    result_json TEXT,
    created_at TIMESTAMP,
    expires_at TIMESTAMP,           -- created_at + 7 days
    fast_path_used BOOLEAN,
    total_results INTEGER
);

CREATE TABLE pmid_grade_cache (
    pmid TEXT PRIMARY KEY,
    publication_types TEXT,         -- JSON array
    sample_size INTEGER,            -- 可空
    grade TEXT,                     -- A/B/C/D/excluded
    grade_rationale TEXT,
    cached_at TIMESTAMP             -- 30 天有效(publication_type 不变)
);
```

**缓存策略**:
- `evidence_search` 完整结果缓存 7 天
- 单 PMID 的 grade 缓存 30 天(publication_type 几乎不变)
- 命中加速 10-50x,完全避免重复 API 调用

---

## 失败模式速查

| 症状 | 可能原因 | 处理方式 |
|------|---------|---------|
| BioMCP 检测到但调用失败 | MCP server 未启动 | 静默回退 slow-path,日志告警 |
| Slow-path 调用 pubmed-eutils 超时 | NCBI 限流 (3 req/s) | 加 jitter 重试 3 次,仍失败用 europepmc-search 替代 |
| `publication_type` 字段为空 | 文献过新还未 MeSH 索引 | 用 abstract 关键词启发式判定(`randomized` / `meta-analysis`),grade 加 `_inferred` 后缀 |
| 样本量提取失败 | 全文不可访问 | 跳过样本量校验,Grade 按 publication_type 默认值 |
| 同一研究多 PMID(预印本+正式版) | medRxiv → 期刊 | 优先保留期刊版,预印本标记 `superseded_by` |
| 中文期刊文献缺失 | NCBI 不索引部分中文期刊 | 提示用户用 CNKI / 万方补充,本 skill 不覆盖 |
| `evidence_search(target_grade='guideline_only')` 返回 0 | 该领域确无指南 | 自动 fallback 到 `rct_or_above` 并提示用户 |
| 缓存损坏 | SQLite 文件错误 | 自动重建,丢弃旧缓存 |

---

## evidence/ 文件夹组织规范

下游 `evidence-appendix-sync` 期望本 skill 产出如下结构:

```
evidence/
├── grade-A/
│   ├── PMID-32786187_DiNardo-NEJM-2020.json     # 完整元数据
│   ├── PMID-32786187_DiNardo-NEJM-2020_abs.txt  # 摘要全文
│   └── ...
├── grade-B/
├── grade-C/
├── grade-D/
├── excluded/
├── _summary.md                                   # 汇总报告
├── _index.csv                                    # 全部文献索引(便于附录 C 引用)
└── _query_log.jsonl                              # 检索日志(可复现)
```

每条 `*.json` 文件结构:
```json
{
  "evidence_id": "PMID:32786187",
  "grade": "A",
  "grade_rationale": "Phase 3 multicenter RCT, n=431, NEJM",
  "citation_apa": "DiNardo CD, et al. (2020). NEJM, 383(7), 617-629.",
  "citation_chinese": "DiNardo 等,2020,NEJM",
  "metadata": {...},
  "abstract": "...",
  "fetched_at": "2026-04-25T14:32:11Z",
  "source_chain": ["biomcp", "pubmed_efetch"]
}
```

---

## 跨疾病移植清单

本 skill 与具体疾病无关,移植到新疾病(如肺癌、糖尿病)只需:

- [ ] 确认底层 6 个原子 skill 都已安装(`/skills` 列表查看)
- [ ] 调整 `recommend_search_strategy` 中的 question_type 映射(领域可能有专属 filter)
- [ ] 若涉及罕见病,把 GRADE 算法中的"n>=500/1000"阈值降低(建议 100/200)
- [ ] 若涉及外科器械,补充 `Comparative Effectiveness Research` 类型识别
- [ ] 中文医学领域加 CNKI / 万方补充检索(本 skill 不直接支持,建议外挂)

---

## 跨平台兼容

| 平台 | 兼容性 | 说明 |
|------|-------|------|
| Claude Code | 原生支持 | SKILL.md 自动加载,Skill 工具直接调用 |
| Codex (CLI) | 兼容 | 复制到 `$CODEX_HOME/skills/medical-evidence-grading/SKILL.md`,通过 `skill-installer` |
| Gemini CLI | 兼容 | 作为 prompt template 使用,函数签名需手工实现 |
| Cursor / Continue | 部分 | 提取 GRADE 规则表作为 system prompt |

**关键不变量**(确保跨平台一致):
- 函数签名稳定(5 个核心函数不重命名)
- GRADE 规则表用 Markdown 表格表达(任何 LLM 都能解析)
- 缓存路径用 `~/.claude/skills/medical-evidence-grading/cache.sqlite`(各平台自动适配)
- BioMCP 检测逻辑同时支持 `~/.claude/mcp.json` / `~/.codex/config.toml` / 环境变量三路

---

## 版本与依赖

- **版本**: 1.0.0 (P0.2 阶段首发)
- **依赖原子 skill**: pubmed-eutils ≥1.0, europepmc-search ≥1.0, clinical-trials-v2 ≥2.0, aact-bulk-trials ≥1.0, bioc-fulltext-fetch ≥1.0, pubtator-entity-search ≥1.0
- **可选 MCP**: biomcp (genomoncology/biomcp,推荐安装以启用 fast-path)
- **Python 依赖**: requests / httpx, sqlite3 (标准库), asyncio (标准库)
- **更新日志**:
  - v1.0.0 (2026-04-25): 初始版本,5 核心函数 + BioMCP 双模式 + SQLite 缓存

---

## 调用示例

### 示例 1: 找血液 IFI 最新指南
```python
result = evidence_search(
    query="invasive fungal infection prophylaxis hematology",
    target_grade="guideline_only",
    max_results=20,
    date_range="5years",
)
# 返回 ECIL / IDSA / NCCN 等指南
```

### 示例 2: 给已有 PMID 列表打分
```python
graded = grade_pmid_list(
    pmid_list=["32786187", "37123456", "38234567"],
    fetch_fulltext_for_sample_size=True,
)
# 返回 [{pmid, grade, rationale}, ...]
```

### 示例 3: 反向验证临床声明
```python
verdict = cross_validate_evidence(
    claim="Posaconazole 预防 IFI 比 fluconazole 更有效",
    top_k=10,
    min_grade="B",
)
# 返回 supporting / contradicting 文献 + 总体判断
```

---

**End of SKILL.md**