本体论术语策展规则实战:从候选选择到元数据表审计的完整决策流程(scientific-agent-skills 深度解析)
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
导读
在scientific-agent-skills仓库的ontology-term-resolution技能中,策展规则(Curation Rules)是连接"机器搜索"与"人类判断"的关键一环:OLS 检索工具能把自由文本映射为候选本体术语(CURIE),但是否接受一个候选、何时判定"无此术语"、如何审计一张已有元数据表,则是一套有纪律的决策流程。本文基于 skills/ontology-term-resolution/references/curation-rules.md 展开,并结合 resolve_terms.py、validate_terms.py 与 ols_client.py 的源码实现与测试用例,讲解如何在注释组织、细胞类型、疾病、表型等字段时产出可审查、可复现、不会静默出错的术语映射。
策展的本质:为每个字符串走一遍决策程序
策展规则的第一条原则极其克制:在候选项中做选择,并在诚实的答案是"无此术语"时正确收尾。它拒绝一切"看着像就填上"的捷径——因为一个格式正确的UBERON:0002108(小肠道)与UBERON:0002107(肝脏)在形式上毫无破绽,审查者也难以凭肉眼区分,这正是虚构 ID 能穿过审阅、混入已发表数据集的原因。
决策程序逐字符串运行,并在第一步给出站得住脚的答案时停止,共五步:
| 步骤 | 情形 | 处置 |
|---|---|---|
| 1 | 在目标本体中找到精确标签匹配 | 接受 |
| 2 | 精确同义词匹配 | 接受,但记录主标签而非同义词——元数据文件应携带本体自身的标签,以便与本体发布版本干净地对齐 diff |
| 3 | 精确匹配但本体错误 | 通常是源数据列的分类错误而非命名问题:hepatocyte出现在 tissue 字段,说明该列混入了组织与细胞类型两类概念。修复数据列,不要强行凑匹配 |
| 4 | 仅有部分匹配 | 不得静默接受。要么规范化输入后重试,要么给出带标签的 Top 候选交由人工选择,要么标记为 unresolved |
| 5 | 毫无匹配 | 标记 unresolved 并如实说明——unresolved 行是正确输出 |
从源码结构看,resolve_terms.py实现了第 1~4 步的搜索侧:其to_rows()函数把每个候选标记为exact_label、exact_synonym或partial(resolve_terms.py),而"partial 是否可接受"这一判断留给你,工具不会替你决定。与之对应,ols_client.py 中的match_type()纯函数通过normalize_label比较查询与标签/同义词,明确区分三者——测试 test_scripts.py 中test_match_type_distinguishes_label_synonym_and_partial验证了 OLS 会把部分命中与精确命中混排,客户端必须自行区分。
值得重试的规范化:把 partial 变成 exact_label 的廉价重写
当仅得到部分匹配时,先不要放弃。以下廉价重写按产出率大致排序,可以把partial升级为exact_label:
- 去掉源数据附加的限定词:
liver (donor)→liver,Liver - left lobe [FFPE]→liver left lobe。 - 展开实验室缩写:
PBMC→peripheral blood mononuclear cell,WT→ 实际基因型,M/F→male/female。 - 反转倒置短语:
ventricle, left→left ventricle,cortex, kidney→kidney cortex。 - 单数化:
hepatocytes→hepatocyte——本体标签是单数形式。 - 英式/美式拼写切换:本体标签两种都有,
oesophagus与esophagus都试。 - 去掉物种前缀:
human liver→liver(物种应放入单独的 NCBITaxon 字段)。
同时有一条红线:不要规范化掉连字符、希腊字母、数字或大写基因符号——CD4-positive与alpha-beta T cell的表意必须原样保留。这正是 ols_client.py 中normalize_label()的设计哲学:它只折叠大小写与空白,其他一概不动。测试test_normalize_folds_case_and_whitespace_only(test_scripts.py)专门断言"CD4-positive"归一化后仍是"cd4-positive"——连字符承载语义,必须存活。
当普通搜索在实验室缩写上反复失败时,可以尝试带本体过滤的ZOOMA(详见 ols4-api.md):它基于策展人此前对该精确字符串的映射历史匹配,与纯词法搜索是不同且往往更优的信号。注意 ZOOMA 必须始终携带过滤参数(如propertyType=organism part&filter=required:[none],ontologies:[uberon]),未过滤时propertyValue=liver会返回gold.vocab之类的无关结果。
"unresolved"应当长什么样:宁可空白,不可虚构
永远不要为了填满单元格而发明 ID。一条 unresolved 行应当携带原始字符串、空的 ID 与原因:
- 可见的空白:下游能清楚地看到缺口;
- 虚构的
UBERON:0002108:一个无声的错误,因为它看起来与真实 ID 一模一样,能骗过审阅存活至今。
从源码看,resolve_terms.py的to_rows()对无候选的查询输出match_type=unresolved、curie=""的空行(resolve_terms.py);测试test_unresolved_query_becomes_a_visible_row(test_scripts.py)也断言"unresolved 术语绝不能携带 ID"。
如果概念确实无对应术语、而项目又依赖它,正确路径是向本体提出新术语申请(在本体追踪器上开 GitHub issue,附上定义与参考文献),而不是本地铸造一个标识符。
审计一张既有元数据表:高产出检查清单
策展规则给出了按优先级排序的审计步骤,每一步都对应validate_terms.py的一个能力:
- 每个 ID 都存在:
python3 validate_terms.py --input table.tsv。 - 无过时 ID:过时术语通常携带
term_replaced_by,修复近乎机械——但要审慎应用替换,因为替代项可能比原术语更宽或更窄。 - 标签与 ID 一致:提供标签列。不匹配之处正是复制粘贴漂移与虚构 ID 浮出水面的地方——ID 是真的、标签也是真的,但描述的是两回事。对应
label_mismatch状态。 - 每列对应正确本体:
--expect-ontology。 - 每列对应正确分支:
--branch,并记住它不能把细胞类型从解剖学中排除出去(详见 ols4-api.md 与 ontology-registry.md 的 CARO 陷阱)。
--strict将警告升级为失败,是 CI 门禁的正确设置。三种警告是:
| 警告状态 | 含义 |
|---|---|
matched_synonym | 所声称的标签是同义词而非主标签 |
imported_only | 主本体已不再断言该 ID(只在导入者的副本中存在) |
not_a_class | 术语是属性或个体,而非类 |
从 validate_terms.py 源码结构看,失败状态集FAIL_STATUSES包含not_found、obsolete、label_mismatch、wrong_branch、wrong_ontology、malformed_curie,警告集WARN_STATUSES则为上述三者;check_term()按"格式 → 存在性 → 过时 → 本体 → 标签 → 分支 → 归属/类型"的顺序裁决,且失败优先于警告(测试test_failure_beats_warning验证了 wrong_branch 会压过 synonym 警告)。退出码为 0(干净)、1(有失败)、2(用法或网络错误),天然可作为 CI 门禁;test_clean_run_exits_zero、test_failure_exits_one、test_strict_makes_warnings_fail等测试(test_scripts.py)逐一固定了这些行为。
实际审计组合示例:
cd skills/ontology-term-resolution/scripts # ID + 标签列;捕捉"存在但标签是别的"的 ID python3 validate_terms.py --input metadata.tsv --strict # 组织列必须只容纳 UBERON 解剖实体 python3 validate_terms.py --input tissue_ids.tsv \ --branch UBERON:0000465 --expect-ontology uberon--branch的实现细节值得注意:resolve_terms.py会先通过iri_for()把分支 CURIE 解析为 IRI 再传给 OLS 的allChildrenOf参数(resolve_terms.py),而validate_terms.py则用ancestor_curies()拉取被检术语的传递祖先集合并做集合成员判断(ols_client.py)。
过时术语:废弃不是删除
本体中的"废弃"不是删除——ID 依然可以解析,但其标签通常带有obsolete_前缀。这个前缀在任何元数据文件中都是有用的气味信号:
EFO:0001067 obsolete_parasitic infection -> replaced by MONDO:0005135从实现看,validate_terms.py的check_term()检测到is_obsolete后,会把term_replaced_by这条完整 IRI(如http://purl.obolibrary.org/obo/MONDO_0005135)通过iri_to_curie()转为 CURIEMONDO:0005135(ols_client.py)。iri_to_curie()按最后一个下划线切分,因此能同时处理 OBO PURL、EFO 专属命名空间、Orphanet 命名空间以及APOLLO_SV_00000001这种多下划线前缀——测试test_multi_underscore_prefix_splits_on_the_last_underscore与 live 测试test_obsolete_term_carries_its_replacement均验证了该行为。
有些过时术语没有替代项,只有consider注解或什么都没有。此时必须手工重新策展,没有自动答案——这正是策展规则反复强调"不要发明 ID"的又一场景。
跨本体映射:OxO 已退役,两条可行路线
文档明确记录:OxO 已退役,返回的是带 HTTP 200 的 HTML 页面——一个朴素的curl | jq会困惑地失败而非干净地报错。两条可行路线是:
- 术语交叉引用(cross-references):
term_detail(curie)["annotation"]["database_cross_reference"]列出等价物——UBERON:0002107携带MESH:D008099、NCIT:C12392、FMA:7197、UMLS:C0023884等:
from ols_client import term_detail xrefs = (term_detail("UBERON:0002107") or {}).get("annotation", {}).get( "database_cross_reference", [] )- SSSOM 映射集:由 Monarch 与 OBO 社区发布,当来源出处与映射谓词(
skos:exactMatchvscloseMatch)至关重要时使用。
文档给出关键警告:交叉引用由策展人以不同置信度断言,并非全部都是exactMatch。当映射驱动的是分析而非展示时,单个 xref 应视为线索而非证据。
报告规范:让结果可以被审查
把解析出的术语交还时,同时给出 ID 与标签,并说明每个是如何匹配的。一张裸 ID 表无法被审阅——没有人能用肉眼区分UBERON:0002107与UBERON:0002108,而这恰恰是虚构 ID 能通过审阅的原因。这正是resolve_terms.py输出的 TSV 八列(query/rank/curie/label/ontology/match_type/strategy/defining_ontology,见 resolve_terms.py)中match_type与strategy两列存在的意义:它们让"这个 ID 是怎么来的"对下游完全透明。
决策流程背后的源码支撑
策展规则的五步决策程序并非停留在文档层面,resolve_terms.py用一套**策略阶梯(strategy ladder)**将其落地:exact(精确,限定 label/synonym 字段)→token(全字段整词匹配)→fulltext(无限制相关性搜索),在第一个返回候选的策略处停止(resolve_terms.py)。测试test_ladder_stops_at_the_first_strategy_that_hits与test_ladder_escalates_when_exact_finds_nothing验证了阶梯的停止与升级行为;--exact-only则禁用阶梯,任何非精确命中直接输出为 unresolved(对应决策第 5 步)。
阶梯的起点exact策略在底层依赖ols_client.search()的query_fields="label,synonym"参数——这不是随意选择:ols4-api.md记录的陷阱 1 说明 OLS 的exact=true是精确 token 匹配而非精确标签匹配(q=liver&ontology=uberon&exact=true会返回 161 条命中,加上queryFields=label才收敛到 1 条)。因此客户端在match_type()中自己重新判定精确性,绝不相信服务端的排序(ols_client.py)。同理,rank_candidates()先把候选按exact_label → exact_synonym → partial分层、层内保留服务端相关度序、并让定义本体的副本压过导入副本(ols_client.py)——这正是决策第 1、2 步"精确匹配优先"与"同一术语在多个本体出现"陷阱的机器实现。
延伸阅读(仓库内)
- SKILL.md:技能总览,两个方向的用法、状态机表格与全部 OLS 陷阱速查。
- ols4-api.md:OLS4 端点、参数、响应字段与每个已验证的陷阱。
- ontology-registry.md:前缀→OLS ID 映射、分支根、每个概念应归属哪个本体。
- ols_client.py:纯函数实现(
normalize_label、match_type、iri_to_curie等)。 - test_scripts.py:离线单元测试 + 门控的 live 冒烟测试,是上述行为最精确的行为契约。
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考