Citation anchor resolver
Skill EthanYoQ/Skill-hub/skills/10-business-industry/citation-anchor-resolver
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. content-verification-layer 要扫全报告锚点 (parse_citations_in_text) 3. drug-citation-verifier / 任何 cross-check skill 需要"锚点 → 源原文" P0 守护:本 skill **绝不维护任何已知字典**。源里没有 = verified=False, 无例外、无 fallback、无"已知答案兜底"。 P0 guard: this skill **never maintains any built-in dictionary**. Not in source = verified=False. No fallback, no built-in answers.From its SKILL.md
npx -y skills add EthanYoQ/Skill-hub --skill citation-anchor-resolverAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.6 KB, ~2.2k tokens by cl100k_base, as published. Nobody here has run it
citation-anchor-resolver
TL;DR
输入:报告文本 + 一个 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 关键词
Iron Law(P0 守护,绝对不可违反)
本 skill 是 Cite-or-Block 架构的基石。永远不做以下事:
- ❌ 维护"已知 PMID 列表 / 已知指南列表 / 已知药品列表"等任何字典
- ❌ 在 resolve 失败时 fallback 到"内置答案"
- ❌ 用 LLM 生成关键词的"语义匹配",必须用严格 substring(可加规范化)
- ❌ 任何形如
_known_xxx_fallback.py/KNOWN_XX_LIST = [...]的代码
唯一允许的姿势:
- ✅ 打开
sources_dir内的源文件(JSON/HTML/PDF/text) - ✅ 用字符串
in操作核对关键词 - ✅ 源里没有 → 返回
verified=False - ✅ 锚点解析不出文件 → 返回
None
源里没有 = verified=False,无例外。
Citation 锚点 schema
格式:[<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。
公开 API
from 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"}
协作矩阵 / Collaboration matrix
| 上游(给本 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') 阻断重写
反例(给将来的 reviewer)
❌ 不要做这些事:
# ❌ 反例 1:维护已知字典
KNOWN_PMIDS = {"12345678": "Lorlatinib paper", ...}
def resolve_citation_BAD(anchor, sources_dir):
if anchor in KNOWN_PMIDS: # ← 字典!
return KNOWN_PMIDS[anchor]
...
# ❌ 反例 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
# ❌ 反例 3:语义匹配代替源核对
def verify_BAD(claim, citation, sources_dir):
return llm.judge(f"is `{claim}` consistent with the literature?")
# ← LLM 凭训练知识判断,绕过了"必须在源里"的约束
✅ 正确姿势:打开文件,字符串 in,源里没有 = False。
跨平台
- Python 3.10+
- 标准库
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 # 失败模式 + 排错指南
决策检查清单(每次修改本 skill 必过一遍)
- 我没有在维护"已知 XX 列表"?
- 我的 cross-check 是"核对源"不是"查表"?
- 这个 skill 在我从未见过的疾病/药/数字上能工作吗?
- resolve_citation 找不到时返回 None,不 fallback?
- verify_claim_against_source 用严格 substring 而不是语义判断?
任一项 No → 停下来重设计。
本 skill 由 Phase 1.5 Task A11 实现,2026-04-26 commit。 P0 铁律见项目根 CLAUDE.md。
What ships with it: 8 files
38.9 KB alongside SKILL.md, 5 of them executable
references/
- anchor-schema.md3.1 KB
- failure-modes.md3.7 KB
- source-types.md3.1 KB
scripts/
- _anchor_schema.pyruns3.2 KB
- __init__.pyruns1.0 KB
- _keyword_match.pyruns7.6 KB
- resolver.pyruns7.4 KB
- _source_loader.pyruns9.7 KB