Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Cite-or-Block 架构的基石原子 skill。把报告里的 citation 锚点 (例如 [guideline:CSCO-2024-NSCLC:§5.5.2] / [pmid:12345678:abstract]) 解析为源文件片段,并核对事实声明里的关键词是否在引用源原文里出现。 Foundation atomic skill for the Cite-or-Block architecture. Resolves citation anchors (e.g. [guideline:CSCO-2024-NSCLC:§5.5.2] / [pmid:12345678:abstract]) to raw source text fragments, and verifies whether claim keywords actually appear in the cited source. 使用场景 / Use when: 1. 报告生成后要做 fact-check (resolve_citation + verify_claim_against_source) 2
.claude/skills/ethanyoq-citation-anchor-resolver/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 598% | 0% |
| case-07 | ✗→✓ | ▲ Improved | -18% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 12% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 54% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -1% | 0% |
输入:报告文本 + 一个 citation 锚点字符串 + sources_dir 目录 输出:锚点对应的源原文片段、或事实声明的关键词核对结果(verified True/False)
3 个公开函数:
parse_citations_in_text(text) — 扫描文本提取所有锚点 + 上下文resolve_citation(anchor, sources_dir) — 锚点 → 源文本片段verify_claim_against_source(claim_text, citation, sources_dir) — 核对 claim 关键词本 skill 是 Cite-or-Block 架构的基石。永远不做以下事:
_known_xxx_fallback.py / KNOWN_XX_LIST = [...] 的代码唯一允许的姿势:
sources_dir 内的源文件(JSON/HTML/PDF/text)in 操作核对关键词verified=FalseNone源里没有 = verified=False,无例外。
格式:[<source_type>:<source_id>:<locator>]
| source_type | source_id 形如 | 文件路径(相对 sources_dir)| locator 例 | |------------|---------------|------------------------|-----------| | guideline | CSCO-2024-NSCLC | guidelines/CSCO-2024-NSCLC.{txt,md,html,pdf} + .toc.json | §5.5.2 | | pmid | 12345678 | pubmed/12345678.json | abstract / title | | nct | NCT01828099 | trials/NCT01828099.json | results / eligibility | | aact | NCT01828099 | aact/NCT01828099.json | results | | europepmc | PMC1234567 | europepmc/PMC1234567.json | abstract | | bioc | PMC1234567 | bioc/PMC1234567.json | intro / methods / results | | evidence | 01_lit.md | <sources_dir>/../evidence/01_lit.md | line:42 / line:42-58 | | nmpa-page | H20180123 | nmpa/H20180123.{html,json} | (可空) |
详见 references/anchor-schema.md 和 references/source-types.md。
pythonfrom resolver import ( parse_citations_in_text, resolve_citation, verify_claim_against_source, ) # 1. 扫文本拿锚点 cites = parse_citations_in_text(report_html) # [{"anchor": Citation(...), "anchor_str": "[pmid:12345678:abstract]", # "claim_sentence": "...", "position": 42}, ...] # 2. 锚点 → 源原文 src = resolve_citation("[guideline:CSCO-2024-NSCLC:§5.5.2]", sources_dir) # str(源章节内容)or None # 3. 核对关键词 res = verify_claim_against_source( claim_text="洛拉替尼商品名博瑞纳", citation="[guideline:CSCO-2024-NSCLC:§5.5.2]", sources_dir=sources_dir, ) # {"verified": True, "matched_keywords": [...], "missing_keywords": [], # "source_excerpt": "...", "reason": "all keywords matched"}
| 上游(给本 skill 喂源) | 内容 | 下游(调本 skill 做 verify) | 用途 | |--------------------|------|------------------------|------| | cn-clinical-guidelines-fetch (A1') | 原文存到 sources/guidelines/<id>.{txt,html,pdf} + .toc.json | drug-citation-verifier (A5') | 核对每个药品提及的 citation | | pubmed-eutils | PMID JSON 存 sources/pubmed/<pmid>.json | content-verification-layer (A6') | 核对每段事实声明 | | clinical-trials-v2 | NCT JSON 存 sources/trials/<nct>.json | disease-market-sizing-orchestration (A2') | Step 8 全报告 cross-check | | aact-bulk-trials | AACT 切片存 sources/aact/<nct>.json | quality eval (A8) | citation coverage 审计 | | europepmc-search | PMC 摘要存 sources/europepmc/<pmcid>.json | CI lint (A10) | 无 citation 的事实声明 = build fail | | bioc-fulltext-fetch | 全文 chunk 存 sources/bioc/<pmcid>.json | — | — |
1. parse_citations_in_text(html) # 拿到所有锚点 + claim 上下文
↓
2. for each citation:
resolve_citation(anchor, sources_dir) # → 源文本片段 or None
↓
verify_claim_against_source(claim, anchor, sources_dir)
↓
{verified: True/False, matched: [...], missing: [...]}
↓
3. 任一 verified=False 或 None → 报告写错了/源不支持 → 上层 (A6'/A2') 阻断重写❌ 不要做这些事:
python# ❌ 反例 1:维护已知字典 KNOWN_PMIDS = {"12345678": "Lorlatinib paper", ...} def resolve_citation_BAD(anchor, sources_dir): if anchor in KNOWN_PMIDS: # ← 字典! return KNOWN_PMIDS[anchor] ...
python# ❌ 反例 2:fallback 到"内置答案" def resolve_citation_BAD(anchor, sources_dir): src = load_from_disk(anchor, sources_dir) if src is None: return BUILT_IN_ANSWERS[anchor] # ← fallback! return src
python# ❌ 反例 3:语义匹配代替源核对 def verify_BAD(claim, citation, sources_dir): return llm.judge(f"is `{claim}` consistent with the literature?") # ← LLM 凭训练知识判断,绕过了"必须在源里"的约束
✅ 正确姿势:打开文件,字符串 in,源里没有 = False。
re + json + pathlib(必需)pypdf(只在 guideline 锚点指向 PDF 时使用,缺失则跳过 PDF 锚点返回 None)citation-anchor-resolver/
├── SKILL.md # 本文件
├── scripts/
│ ├── __init__.py
│ ├── _anchor_schema.py # Citation dataclass + 解析正则
│ ├── _source_loader.py # 8 source_type 的加载器
│ ├── _keyword_match.py # 中英文关键词在源里的核对
│ └── resolver.py # 3 个公开函数
└── references/
├── anchor-schema.md # 完整锚点 schema 文档
├── source-types.md # 8 种 source_type 详细
└── failure-modes.md # 失败模式 + 排错指南任一项 No → 停下来重设计。
本 skill 由 Phase 1.5 Task A11 实现,2026-04-26 commit。 P0 铁律见项目根 CLAUDE.md。
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | pass→pass | 6,410 | 14,720 | +130% | 1 | 1 | 0% | 1,305 | 4,842 | +271% | 0 | 0 | — |
case-01 | fail→pass | 2,953 | 6,960 | +136% | 1 | 1 | 0% | 511 | 3,566 | +598% | 0 | 0 | — |
case-03 | pass→pass | 2,372 | 2,826 | +19% | 1 | 1 | 0% | 551 | 2,738 | +397% | 0 | 0 | — |
case-04 | pass→pass | 5,153 | 5,032 | -2% | 1 | 1 | 0% | 1,057 | 3,151 | +198% | 0 | 0 | — |
case-05 | pass→pass | 2,104 | 2,471 | +17% | 1 | 1 | 0% | 418 | 2,586 | +519% | 0 | 0 | — |
case-06 | pass→pass | 8,457 | 2,833 | -67% | 1 | 1 | 0% | 1,873 | 2,942 | +57% | 0 | 0 | — |
case-07 | fail→pass | 12,530 | 1,748 | -86% | 1 | 1 | 0% | 3,159 | 2,605 | -18% | 0 | 0 | — |
case-08 | fail→pass | 9,688 | 1,788 | -82% | 1 | 1 | 0% | 2,261 | 2,524 | +12% | 0 | 0 | — |
case-09 | fail→pass | 8,546 | 2,011 | -76% | 1 | 1 | 0% | 1,725 | 2,650 | +54% | 0 | 0 | — |
case-10 | fail→pass | 11,134 | 1,586 | -86% | 1 | 1 | 0% | 2,578 | 2,546 | -1% | 0 | 0 | — |
case-11 | fail→pass | 4,761 | 2,943 | -38% | 1 | 1 | 0% | 895 | 2,803 | +213% | 0 | 0 | — |
case-12 | pass→pass | 5,552 | 1,921 | -65% | 1 | 1 | 0% | 1,323 | 2,617 | +98% | 0 | 0 | — |
case-13 | fail→pass | 11,758 | 5,002 | -57% | 1 | 1 | 0% | 2,355 | 3,181 | +35% | 0 | 0 | — |
case-14 | fail→pass | 7,692 | 1,925 | -75% | 1 | 1 | 0% | 1,492 | 2,598 | +74% | 0 | 0 | — |
case-15 | pass→pass | 4,435 | 2,128 | -52% | 1 | 1 | 0% | 839 | 2,674 | +219% | 0 | 0 | — |
case-16 | fail→pass | 9,898 | 1,783 | -82% | 1 | 1 | 0% | 1,946 | 2,603 | +34% | 0 | 0 | — |
case-17 | fail→pass | 7,705 | 3,385 | -56% | 1 | 1 | 0% | 1,580 | 2,963 | +88% | 0 | 0 | — |
case-18 | pass→pass | 11,441 | 5,395 | -53% | 1 | 1 | 0% | 2,314 | 3,273 | +41% | 0 | 0 | — |
case-19 | fail→pass | 9,380 | 4,392 | -53% | 1 | 1 | 0% | 1,855 | 3,051 | +64% | 0 | 0 | — |
case-20 | fail→pass | 8,263 | 3,789 | -54% | 1 | 1 | 0% | 1,747 | 3,010 | +72% | 0 | 0 | — |
case-21 | pass→pass | 9,952 | 3,411 | -66% | 1 | 1 | 0% | 2,300 | 3,009 | +31% | 0 | 0 | — |
case-22 | pass→pass | 9,794 | 5,789 | -41% | 1 | 1 | 0% | 2,150 | 3,450 | +60% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.