Install any skill in seconds. Free to start, no credit card required.
Get Started Free →从 PubMed/PMC 论文取结构化全文并按章节切分为 RAG 友好的 chunk。当用户需要批量抽取论文全文、按 Methods/Results 等章节分段、提取表格上下文、构建文献向量库时,使用本 skill。支持 PMID(标题+摘要)和 PMCID(全文,开放获取子集)。token 级 RAG chunking 不跨章节,支持缓存与限流。
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 127% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 123% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 221% | 0% |
封装 NLM BioC API,把 PubMed / PMC 文献转成 RAG 友好的结构化全文 chunk。只做全文抽取与切片,不做检索、不做实体标注、不做向量化。
触发场景:
不要使用本 skill:
pubmed-eutils efetchpubmed-search / europepmc-searchpubtator-entity-search(可在本 skill 输出之上叠加,不替代)medical-evidence-grading(其内部会调用本 skill 抽 Methods 段落)| 上游 / 下游 skill | 协作方式 | |------------------|----------| | pubmed-eutils.elink_pubmed_to_pmc() | 上游,把 PMID 列表转换成 PMCID 列表,喂给本 skill | | europepmc-search.get_full_text_links() | 替代源,当 PMC OA 不可用时回退到 Europe PMC 的开放获取链接 | | pubtator-entity-search | 后置叠加,在本 skill 产的 chunk 上做实体标注,不替代本 skill | | medical-evidence-grading | 下游,调 extract_paragraphs 取 Methods 段落做 sample size / study design 检测 |
典型 pipeline:
europepmc-search ──► PMID list ──► pubmed-eutils.elink ──► PMCID list
│
▼
bioc-fulltext-fetch (本 skill)
│
├─► [可选] pubtator-entity-search 实体叠加
├─► [可选] medical-evidence-grading 证据评分
└─► LangChain / LlamaIndex 入库| 端点 | 输入 | 覆盖 | 内容 | |------|------|------|------| | BioC PubMed | PMID | 全 PubMed (~37M) | 标题 + 摘要 + MeSH(结构化) | | BioC PMC OA | PMCID | PMC 开放获取子集(~10M,约占 PMC 25%) | 全文章节、段落、表格、图注 |
摘要级: https://www.ncbi.nlm.nih.gov/research/bionlp/RESTful/pubmed.cgi/BioC_json/{PMID}/unicode
全文级: https://www.ncbi.nlm.nih.gov/research/bionlp/RESTful/pmcoa.cgi/BioC_json/{PMCID}/unicode格式细节、section_type 取值、字符 offset 一致性见 references/bioc-format-spec.md。
PMID list
│
▼
[可选] elink PMID→PMCID (调 pubmed-eutils)
│
├─ PMCID 存在且 OA ──► fetch_fulltext_bioc(pmcid) ──► extract_paragraphs / extract_tables_context ──► to_rag_chunks
│
└─ 不可用/非 OA ─────► fetch_abstract_bioc(pmid) ─────────────────────────────────────────────────► to_rag_chunksfetch_abstract_bioc(pmid: str) -> BioCDocument取标题 + 摘要 + MeSH 的 BioC 结构化版本。
pythondoc = fetch_abstract_bioc("39523456") # doc.passages → [{"infons": {"section_type": "TITLE"}, "text": "..."}, # {"infons": {"section_type": "ABSTRACT"}, "text": "..."}] # doc.infons["mesh"] → ["Leukemia, Myeloid, Acute", "FLT3 Mutation", ...]
fetch_fulltext_bioc(pmcid: str) -> BioCDocument取 PMC OA 全文,按章节切分(section_type ∈ {TITLE, ABSTRACT, INTRO, METHODS, RESULTS, DISCUSS, CONCL, REF, FIG, TABLE, ...})。
pythondoc = fetch_fulltext_bioc("PMC10234567") # 不可用时抛 NotOpenAccessError,调用方应回退到 fetch_abstract_bioc
extract_paragraphs(doc: BioCDocument) -> list[Paragraph]按 BioC passage 提取段落数组,保留 section 标签 + 字符 offset。
pythonparagraphs = extract_paragraphs(doc) # [{"section_label": "METHODS", "text": "Flow cytometry was performed...", # "offset_start": 2890, "offset_end": 3401}, ...]
要点:跳过 REF / FIG_LABEL 纯标签段;合并同 section 内 < 50 字符短 passage;offset 来自 BioC passage.offset。
extract_tables_context(doc: BioCDocument, window: int = 200) -> list[TableContext]提取每个表格的上下文(前后 200 字),帮 RAG 给表打语义标签。
pythontables = extract_tables_context(doc, window=200) # [{"table_id": "T1", "caption": "Patient baseline characteristics", # "before_context": "...", "after_context": "...", "section": "RESULTS"}, ...]
表格本体(cell 数据)不解析;只取 caption + 周围文本。
to_rag_chunks(doc: BioCDocument, chunk_size: int = 512, overlap: int = 50) -> list[Chunk]把 BioC 文档切成 RAG 入库 chunk。token 单位、不跨章节、段落优先、可追溯 chunk_id。
pythonchunks = to_rag_chunks(doc, chunk_size=512, overlap=50) # { # "chunk_id": "PMC10234567_METHODS_2890", # "text": "...", # "metadata": {"pmcid": "...", "pmid": "...", "section": "METHODS", # "offset_start": 2890, "offset_end": 3401, "doi": "...", # "year": 2024, "is_oa": True, "title": "...", "journal": "..."} # }
切片策略详见 references/chunking-strategies.md。
asyncio.Semaphore(5) 或 aiolimiter~/.cache/bioc/{id}.json + SQLite 索引 index.db(key, file_path, fetched_at, is_oa, doi, year)BIOC_CACHE_TTL_DAYS),force_refresh=True 绕过所有错误继承 BioCFetchError 基类。
| 错误 | 类型 | 处理 | |------|------|------| | 404 (PMC OA) | NotOpenAccessError | 调用方回退 abstract | | 404 (PubMed) | NotFoundError | PMID 不存在,跳过 | | 429 | RateLimitError | 指数退避 1s/2s/4s,最多 3 次 | | 5xx | BioCServerError | 重试 3 次后抛出 | | JSON 解析失败 | BioCParseError | 记录原始响应,跳过 | | 超时(默认 30s) | BioCTimeoutError | 重试 1 次 |
section_type 命名有差异(Methods vs Materials and Methods vs Subjects and Methods),BioC 已部分归一化但仍有例外(综述偶尔出现 INTRO_RESULTS 合并段)。下游过滤时用集合而非精确匹配。BioC_xml 端点取,或解析时检测 ? 占位符。to_rag_chunks 默认按 section 分组,不要绕过。fetched_at 而非只看文件存在。failed.jsonl),不能让一篇阻断整批。pubtator-entity-search 后置叠加。LangChain / LlamaIndex / 元数据过滤检索 / pubtator 实体叠加 / medical-evidence-grading 联用的完整代码示例见 references/rag-integration.md。
toml[project] dependencies = [ "httpx>=0.27", # 异步 HTTP "aiolimiter>=1.1", # RPS 限流 "tiktoken>=0.7", # token 计数 "tenacity>=9.0", # 退避重试 "platformdirs>=4.0", # 缓存目录 ]
环境变量:BIOC_CACHE_DIR(默认 ~/.cache/bioc)、BIOC_CACHE_TTL_DAYS(30)、BIOC_MAX_RPS(5)、BIOC_TIMEOUT_SEC(30)。
| 测试 | 预期 | |------|------| | fetch_abstract_bioc("39523456") | 返回 title + abstract passages | | fetch_fulltext_bioc("PMC10234567") | 返回章节切分的全文 | | fetch_fulltext_bioc("PMC1") 非 OA | 抛 NotOpenAccessError | | extract_paragraphs(doc) | 段落 ≥ 3,每段有 section_label | | extract_tables_context(doc) | 上下文左右各 ≤ 200 字 | | to_rag_chunks(doc, 512, 50) | chunk token ≤ 512、overlap ≈ 50、不跨章节 | | 缓存命中 | 第二次调用 < 50ms,无网络请求 | | 429 退避 | 模拟 429 后能在 3 次内成功 |
最低单元测试覆盖率 80%,集成测试用 5 篇真实 PMCID 跑端到端。
references/bioc-format-spec.md — BioC XML/JSON 格式规范、字段定义、section_type 取值references/rag-integration.md — LangChain Chroma + LlamaIndex 完整代码、批量入库要点、向量库选型references/chunking-strategies.md — token 切片、不跨章节策略、overlap 设计、chunk_id 设计、评测指标Other measured skills in the registry, with their headline benchmark lift.