agentsclimarketplace

Nmpa drug registry lookup

Skill EthanYoQ/Skill-hub/skills/06-data-search/nmpa-drug-registry-lookup

Reusable AI agent skills for Codex, Claude Code etc. — AI coding workflows, SKILL.md templates, and automation practices. 面向 Codex、Claude Code等agents 的可复用 AI Agent Skill 技能库:AI 编程工作流、SKILL.md 模板与自动化实践。

Install
npx -y skills add EthanYoQ/Skill-hub --skill nmpa-drug-registry-lookup

Assembled 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.

What its author says it does

Copied from the file, not written here

中国药品权威数据库查询 — NMPA(国家药监局) + CDE(药品审评中心) + DrugBank(英文名/ATC) + PubChem(化学结构) 多源融合,提供(通用名 ↔ 商品名 ↔ 英文名 ↔ 适应症 ↔ NMPA 批文号 ↔ 上市日期)精确映射。Make sure to use this skill whenever the user mentions 药品名 / 通用名 / 商品名 / NMPA / 国药准字 / 药监局 / CDE / drug brand name / generic name / drug registry / pharmaceutical lookup / drug verification / 药物核验 / 商品名对应 / drug cross-check / drug authority lookup. 在任何 medical / market-sizing / clinical / disease-research 报告生成阶段,**所有药品提及必须先调 lookup_drug() 验证**,严禁 LLM 凭训练记忆拼凑通用名↔商品名↔英文名对应关系。LLM 训练截止后药企会更换商品名、新药持续上市,记忆完全不可信。Iron Law:lookup 返回 None → 调用方必须 raise UnverifiedDrugError,不允许 fallback 到记忆。免费公开数据源,跨平台:Claude Code / Codex / Gemini CLI 三端均可 auto-trigger,实现仅依赖 httpx + beautifulsoup4 标准生态,不绑定任何厂商 SDK。

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

11.7 KB, as published. Nobody here has run it

nmpa-drug-registry-lookup · 中国药品权威 Registry

一句话定义

中国市场所有报告里的药品提及,都从此 skill 拿权威记录(NMPA + CDE + DrugBank + PubChem 多源融合);LLM 不允许凭记忆改写药品名


Iron Law(违反此条 = 报告作废)

任何写入报告的药品名(通用名 / 商品名 / 英文名),必须通过 lookup_drug() 验证。

LLM 不允许凭训练记忆拼凑通用名↔商品名↔英文名对应关系 — 因为:

  1. 药企会更换商品名(如同一通用名在不同厂家有多个商品名)
  2. 新药持续上市,LLM 训练截止日期之后的数据完全不可信
  3. 中国市场命名经常与全球市场不同(如:Lorlatinib 中国商品名"博瑞纳",国外"Lorbrena")
  4. 一个药名错位 = 整份报告地基塌方(治疗推荐错 / GRADE 错 / LP 错 / 市场份额测算错)

唯一允许的姿势:

from nmpa_drug_registry_lookup.scripts.registry import lookup_drug, UnverifiedDrugError

rec = lookup_drug("洛拉替尼")
if rec is None:
    raise UnverifiedDrugError("洛拉替尼 not in NMPA registry — refuse to write to report")

# 报告里写药名时,通用名和商品名都从 record 取,不要手输
generic_zh = rec.generic_name_zh   # "洛拉替尼"
generic_en = rec.generic_name_en   # "Lorlatinib"
brand_zh   = rec.brand_names_zh[0] # "博瑞纳"

绝不允许:

# 错误!LLM 凭记忆
report_html += "<p>洛拉替尼(商品名:赛可瑞)...</p>"   # 赛可瑞实际是克唑替尼!
report_html += "<p>布加替尼(商品名:博瑞纳)...</p>"   # 博瑞纳实际是洛拉替尼!

何时调用

  • 任何报告生成阶段(market-sizing / 治疗方案 / 决策树 / 市场份额测算)涉及具体药品名
  • 用户问"X 药的通用名是什么?"、"Y 药什么时候在中国上市?"、"Z 药 NMPA 批文号是多少?"
  • medical-evidence-grading / disease-market-sizing-orchestration 调用方需要药名锚定
  • 报告生成完毕,跑 cross_check_drug_mentions() 全文扫描,catch LLM 偷偷凭记忆改写的药名

何时不调用

  • 文献检索本身(给 PubMed query 时)— 不需要,query 自由文本即可
  • 询问药物机制 / 靶点(用 pubtator-entity-search 抓基因/通路)
  • 询问临床试验状态(用 clinical-trials-v2)

与其他 skill 的协作关系

                        ┌─ pubmed-eutils(召回主力)
召回层 ───┬─────────────┤
          │             └─ europepmc-search
          │
          │             ┌─ cn-clinical-guidelines-fetch(指南权威)
权威层 ───┼─────────────┤
          │             └─ ★ nmpa-drug-registry-lookup(本 skill · 药品权威)
          │
          ▼
报告生成层 ───  所有药品名引用 → lookup_drug() / cross_check_drug_mentions()

调用顺序:

  1. orchestration 解析疾病 → fetch_chinese_guidelines() 拿指南治疗推荐表
  2. 用指南里提到的药品 → lookup_drugs_batch() 建立本疾病 drug_registry.json
  3. 报告生成时所有药品名引用从 drug_registry.json 取(LLM 禁止改写)
  4. 报告草稿出来 → cross_check_drug_mentions(html) 全文扫描,critical 严重性 → raise

核心函数签名

from dataclasses import dataclass
from pathlib import Path

@dataclass(frozen=True)
class DrugRecord:
    """中国药品权威记录。frozen 防止 LLM 修改。"""
    generic_name_zh: str         # 通用名(中文,首选 NMPA 命名)
    generic_name_en: str         # 通用名(英文,DrugBank/INN)
    brand_names_zh: tuple[str, ...]    # 商品名列表(中国市场,可能多个生产厂家)
    brand_names_en: tuple[str, ...]    # 商品名列表(全球)
    nmpa_approval_no: str        # NMPA 批准文号(如 国药准字 H20180123 / 进 J20180012)
    first_approval_date_cn: str  # 中国首次上市日期 ISO yyyy-mm-dd
    indications_cn: tuple[str, ...]    # 适应症(中文)
    atc_code: str                # ATC 分类(如 L01ED05 = 抗肿瘤 ALK 抑制剂)
    target: str                  # 靶点(对靶向药,如 "ALK")
    drug_class: str              # 类别(如 "三代 ALK-TKI")
    sources: tuple[str, ...]     # 来源 URL,可追溯


class UnverifiedDrugError(Exception):
    """lookup 失败时调用方应 raise — 拒绝写入报告。"""


def lookup_drug(
    name: str,                   # 任意名称(通用/商品/英文,模糊匹配)
    market: str = "CN",
    cache_dir: Path | None = None,
) -> DrugRecord | None:
    """单药查询。匹配优先级:
    1. 已知 fallback dict 精确命中(NMPA 网站抓不到时的兜底)
    2. NMPA 精确通用名
    3. NMPA 商品名
    4. DrugBank generic_name
    5. PubChem 化学名 fallback

    None = 该药不在权威数据库 → 调用方必须 raise UnverifiedDrugError。
    """


def lookup_drugs_batch(
    names: list[str],
    market: str = "CN",
    cache_dir: Path | None = None,
) -> dict[str, DrugRecord | None]:
    """批量查询。返回 {name: DrugRecord or None}。"""


def cross_check_drug_mentions(
    text: str,                   # 报告 HTML / Markdown 草稿
    market: str = "CN",
    cache_dir: Path | None = None,
) -> dict:
    """扫描文本里所有疑似药品名,逐个 lookup,返回错误清单。

    Returns:
        {
            "ok": bool,
            "verified_drugs": [...],       # 命中权威数据库的药名
            "unverified_drugs": [...],     # 在数据库找不到 → 必须修
            "name_mismatches": [           # 通用名/商品名混用错位(critical)
                {"text_uses": "赛可瑞", "claimed_as": "洛拉替尼",
                 "actual_generic": "克唑替尼", "evidence_source": "NMPA"},
                ...
            ],
            "violation_severity": "none" | "warning" | "critical",
        }

    严重性规则:
    - 任何 name_mismatches 非空 → critical(LLM 把 A 药写成 B 药商品名)
    - 仅 unverified_drugs 非空 → warning(可能是新药 / 仿制名)
    - 全部 verified → none
    """

数据源(免费公开)

来源提供抓取方式
NMPA 国家药监局 (nmpa.gov.cn)通用名 / 批文号 / 适应症 / 上市时间 (权威 I 级)REST + WebFetch + bs4
CDE 药品审评中心 (cde.org.cn)临床试验阶段 / 适应症详情WebFetch
DrugBank(免费学术)英文名 / ATC code / mechanismAPI / scrape
PubChem(NIH)化学结构 / IUPAC / 同义词REST(pug.ncbi.nlm.nih.gov/rest/pug)
NCBI Gene靶点基因(已通过 pubtator-entity-search 间接)复用

fallback 兜底:NMPA 网站反爬严重时,scripts/_known_drugs_fallback.py 含一份手工核验的常用药字典,每条都有 NMPA 公开页 URL 引用作来源。


失败模式(8 条)

#症状原因修复
1lookup 返回 None药品不在 NMPA(可能仿制名 / 进口未上市 / 拼写错)让用户确认是否仍写入,标 unverified;不允许 LLM 凭记忆补
2同一通用名多个商品名多厂家(如阿仑膦酸钠)brand_names_zh 列出全部,不要任选一个
3通用名↔商品名错位(LLM 偷偷脑补)LLM 训练截止后数据老化cross_check_drug_mentions 全文扫描,critical 级 raise
4NMPA 网站抓取失败(反爬)nmpa.gov.cn 改版 / 验证码降级:cache → fallback dict → 报错
5同一英文名对应多个中文音译早期未规范化NMPA 通用名为唯一权威,其他作 alias
6DrugBank 拒绝学术抓取反爬升级该字段标 None,不阻塞主流程(NMPA 字段是必需)
7LLM 在 prompt 里"脑补"通用名训练数据过时任何 grade_evidence / LP 阶段都强制 lookup,不接受 LLM 直出
8进口药批文号格式 vs 国产不同NMPA 编号规则:H/J/Z/S 前缀不要自行拆解,作为 opaque string 存储

跨平台

  • Python: 3.10+(用 PEP 604 X | None 类型注解)
  • 依赖: httpx, beautifulsoup4, 可选 pypdf(适应症附件解析)
  • 测试: python -m pytest(Windows 用 C:\Python3\python.exe -m pytest,不要用 python3 stub)
  • Windows GBK 终端兼容: 测试入口加 sys.stdout.reconfigure(encoding="utf-8")
  • Cache: JSON 文件 <cache_dir>/drug_registry.json,TTL 7 天;SQLite 是 future work
  • 不联网测试: 测试用 tests/fixtures/nmpa_responses/*.html 预录制响应,绝不在 CI 里直连 NMPA

调用方典型用法

A. 单药 lookup

from nmpa_drug_registry_lookup.scripts.registry import lookup_drug, UnverifiedDrugError

rec = lookup_drug("洛拉替尼")
if rec is None:
    raise UnverifiedDrugError("洛拉替尼 not in NMPA registry — refuse to write to report")

print(rec.generic_name_en)    # "Lorlatinib"
print(rec.brand_names_zh)     # ("博瑞纳",)
print(rec.atc_code)           # "L01ED05"
print(rec.first_approval_date_cn)  # "2022-04-29"

B. 批量预热(orchestration 在 Step 0b 调)

from nmpa_drug_registry_lookup.scripts.registry import lookup_drugs_batch

key_drugs = ["克唑替尼", "阿来替尼", "塞瑞替尼", "恩沙替尼",
             "布加替尼", "洛拉替尼", "伊鲁阿克", "依奉阿克"]
results = lookup_drugs_batch(key_drugs)

missing = [d for d, r in results.items() if r is None]
if missing:
    raise UnverifiedDrugError(f"NMPA registry missing: {missing}")

# 写入 .cache/<slug>/drug_registry.json,后续报告生成阶段 LLM 必须从此取

C. 报告生成完毕全文扫描

from nmpa_drug_registry_lookup.scripts.registry import cross_check_drug_mentions

result = cross_check_drug_mentions(report_html)
if result["violation_severity"] == "critical":
    # LLM 把 A 药写成 B 药商品名 — 必须重生成
    raise OrchestrationError(f"drug name mismatches: {result['name_mismatches']}")
elif result["violation_severity"] == "warning":
    # 仅有 unverified(可能是新药)— §0 加红色警告
    add_section_zero_warning(result["unverified_drugs"])

引用与下游

  • 上游: cn-clinical-guidelines-fetch(指南先告诉本 skill 该疾病有哪些药)
  • 下游: disease-market-sizing-orchestration(在内容生成阶段强制锚定)、content-verification-layer(report-level cross-check 的事实审计层)
  • 同层互补: pubtator-entity-search(基因靶点)、pubmed-eutils(文献召回)

参考文档:

  • references/nmpa-fetch-api.md — NMPA 网站结构 + 抓取策略
  • references/drugbank-integration.md — DrugBank 学术免费用法
  • references/failure-modes.md — 8 失败模式详解

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.