news 2026/9/18 7:43:05

OpenMed 测试体系实战:测试分层、CI 基线命令与 Zero-Shot 冒烟验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenMed 测试体系实战:测试分层、CI 基线命令与 Zero-Shot 冒烟验证

OpenMed 测试体系实战:测试分层、CI 基线命令与 Zero-Shot 冒烟验证

【免费下载链接】openmedLocal-first healthcare AI: clinical NER & HIPAA PII de-identification that runs 100% on-device. 2,200+ medical models, 21 languages, Apple MLX + Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed

OpenMed 是一个本地优先(local-first)的临床 NER 与 HIPAA PII 脱敏工具库,其测试体系围绕"离线可运行、快速回归、可复现 CI"三个目标构建。本篇基于 docs/testing.md 的官方指引,结合 pyproject.toml 中的 pytest 配置、Makefile 质量门禁与 tests/run-tests.sh 基线脚本,系统讲解 OpenMed 的测试分层(unit / integration / slow)、完整运行命令、GLiNER zero-shot 冒烟检查参数,以及文档与 API 一致性校验方法。读完后你可以直接在本仓库中复现整套测试与冒烟流程,并为新特性补充符合项目规范的测试。

测试体系总览

官方文档 docs/testing.md 的开篇定位很明确:借助现有的测试套件与冒烟脚本,让回归问题远离你的临床工作流。整个测试资产分布在tests/目录下,按 tests/README.md 的说明组织如下:

  • tests/unit/—— 快速、隔离的单元测试,覆盖配置、模型加载辅助函数、分词、格式化与工具模块;
  • tests/integration/—— 更高层的场景测试,通过 mock 的 transformers pipeline 验证analyze_textlist_models等公共 API 表面;
  • tests/fixtures/—— 共享样例文本与可复用的 pytest fixtures;
  • tests/conftest.py—— 全局 fixture,负责 mock transformers 组件、配置重置与样例数据。

关键设计决策是重度 mock 下游 Hugging Face API:transformer 层在测试期间被打补丁(patched),但transformers包本身仍必须可导入,因此安装时需要pip install transformers(可选安装torch作为 CPU 后端)。这使得捆绑测试完全不需要网络访问即可运行——一旦测试失败,问题要么指向 OpenMed 代码的回归,要么指向本地依赖缺失,排障边界清晰。

测试标记(Marker)分类

docs/testing.md 给出的测试分类表如下:

标记位置用途
unit(默认)tests/unit/**对模型注册表辅助函数、配置工具与核心 API 的快速验证
integration显式选择(opt-in)演练多组件流程(如 pipeline 创建 + formatter)
slow显式选择(opt-in)运行较重的 GLiNER 或 Hugging Face 调用;除非传入-m slow否则禁用

文档指出这些标记通过 pyproject.toml 中的pytest.ini条目(即[tool.pytest.ini_options])来配置。仓库中该配置块的实际内容比文档表格更丰富,除了文档提到的integrationslow之外,还定义了契约与模糊测试标记,以及测试文件匹配规则与测试路径:

[tool.pytest.ini_options] markers = [ "integration: marks end-to-end or external integration tests", "slow: marks tests that are expected to run slowly", "contract: marks property-based stage-boundary contract tests", "fuzz: marks property-based (Hypothesis) fuzz tests", "doctest_examples: runs doctest examples for targeted public modules" ] python_files = [ "test_*.py", "*_test.py", ] testpaths = [ "tests", ]

从源码结构看,标记在测试代码中通过@pytest.mark.<marker>声明。例如 tests/integration/test_end_to_end.py 中的TestEndToEndAnalysis使用@pytest.mark.integration标注,并同时 patchopenmed.core.backends._module_availableopenmed.core.models.pipelineAutoConfigAutoTokenizerAutoModelForTokenClassification等 HF 组件,再驱动analyze_text全流程——这正是文档表格中"pipeline 创建 + formatter 组合"类集成流程的真实示例。仓库中同时标注了integrationslow双标记的文件(如 tests/integration/test_distroless_image.py、tests/mobile/test_flutter_ffi.py)则会同时受两层选择条件约束。

运行完整测试套件

docs/testing.md 给出的标准运行流程是:

uv pip install ".[dev,hf]" make lint make format-check make lint-swift # 用于 Swift/OpenMedKit 变更 pytest # 快速的 unit/integration 混合 pytest -m "not slow" # 默认行为 pytest -m slow # 仅运行长耗时用例

这些命令与 Makefile 中的目标一一对应,理解各目标的具体实现有助于排查失败:

  • make lint—— 实际执行uv run --frozen --extra dev ruff check .,即基于锁定的 uv 开发环境运行 Ruff 静态检查;
  • make format-check—— 执行uv run --frozen --extra dev ruff format --check .,只检查不修改文件;
  • make lint-swift—— 调用 scripts/lint_swift.sh,对swift/OpenMedKit等 Swift 侧代码做格式 lint,仅在你改动了 Swift/OpenMedKit 时需要;
  • make test—— 执行uv run --frozen --extra dev pytest,与裸pytest等效但复现性更强(锁定依赖解析);
  • make quality—— 组合门禁lint type-check format-check test,适合作为本地提交前的完整质量关卡;
  • 另外,pyproject.toml 中 Ruff 的 lint 规则为E9F63F7F82I(目标版本py310,行宽 88,并排除examples/notebooksopenmed/service/proto/generated)。

tests/run-tests.sh:CI 基线脚本

docs/testing.md 特别强调:tests/run-tests.sh把 lint、格式检查、单元测试与慢速冒烟检查串在一起,应作为 CI 的基线。查看 tests/run-tests.sh 的完整实现,它按以下顺序执行:

#!/usr/bin/env bash set -euo pipefail python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip >/tmp/pip-up.log pip install -e '.[dev]' >/tmp/pip-install.log ruff check . ruff format --check . # Core test suite without slow markers (collect coverage for zero-shot modules) pytest -m "not slow" --cov=openmed/ner --cov-report=term-missing # Zero-shot slow checks (gracefully skip when dependencies unavailable) pytest -m slow

其中有三个值得注意的细节:

  1. set -euo pipefail保证任何一步失败立即终止,避免"假绿";
  2. 核心套件显式指定--cov=openmed/ner,即对 zero-shot NER 模块单独收集覆盖率(终端缺失行报告),这与 OpenMed 将 zero-shot 能力列为核心特性之一的定位一致;
  3. pytest -m slow位于脚本末尾,慢速检查在依赖不可用时优雅跳过(gracefully skip),不会阻塞主流程。

若不需要脚本、只想手动分层运行,tests/README.md 给出了等效命令:

# 全量(unit + integration) pytest # 仅轻量单元测试 pytest tests/unit pytest -m "not integration" # 仅集成场景 pytest -m integration # 生成覆盖率报告 pytest --cov=openmed --cov-report=term-missing

GLiNER Zero-Shot 冒烟检查

对于 zero-shot 场景,docs/testing.md 建议的冒烟命令是:

uv pip install ".[gliner]" python scripts/smoke_gliner.py --limit 2 --threshold 0.4 --adapter

scripts/smoke_gliner.py 是一个独立的命令行冒烟运行器,其参数与行为在源码中都有明确定义,可结合参数注释逐一理解:

参数默认值作用
--index打包内置索引指定models/index.json的路径,缺省时使用包内模型索引
--limit3限制被测 GLiNER 模型数量
--threshold0.4实体置信度阈值
--adapter关闭(flag)推理后运行 token classification 适配器,输出 BIO/BILOU 标签

脚本的源码级执行链路为:先通过is_gliner_available()检查依赖,缺失时直接报错并提示pip install .[gliner];然后load_index()加载模型索引并筛选出family == ModelFamily.GLINER的记录;对每个被选中的模型,按其第一个 domain 从DEFAULT_SAMPLE_TEXTS中选取领域样例文本(覆盖 biomedical、clinical、genomic、finance、legal 等 15 个领域,如 biomedical 域的 "Imatinib inhibits BCR-ABL in chronic myeloid leukemia patients."),构造NerRequest(model_id=..., text=..., threshold=..., domain=...)后调用infer()执行推理;每个实体以- <label>: '<text>' [start-end] score=<score>的格式打印。若传入--adapter,再调用to_token_classification(response.entities, request.text)把实体结果转换为 token 级标注(TokenClassificationResult),逐 token 打印标签——这条链路同时验证了推理与 BIO/BILOU 转换两个环节,正好对应后文"覆盖建议"中提到的 zero-shot 适配器测试点。

文档与 API 一致性检查

docs/testing.md 还给出两类"套件之外"的检查:

1. 文档构建检查——在 CI 中加入uv run mkdocs build --strict,让缺失的导航条目、重复锚点或损坏的 markdown 直接导致构建失败。仓库中 Makefile 的docs-serve(本地 127.0.0.1:8008 热重载)与docs-build/docs-stage目标(构建并暂存最终 Pages 产物到site/)提供了本地对照手段。

2. 轻量 API 冒烟测试——确保打包与 extras 保持同步的最小示例:

from openmed import analyze_text result = analyze_text("QA ping", model_name="disease_detection_superclinical") assert result.entities is not None

这个 snippet 的价值在于:它同时验证了打包完整性(openmed可导入)、模型注册表条目(disease_detection_superclinical是合法model_name)与核心 API 返回结构(result.entities非空)。

新增功能时的覆盖建议清单

docs/testing.md 结尾给出的四项覆盖建议,结合仓库现有测试资产可以落地为具体做法:

  1. 模型注册表条目——确保新模型键出现在list_model_categories中。可在tests/unit下补充断言新键的分类归属,参照 tests/integration/test_end_to_end.py 中对list_models的调用方式;
  2. 公共 API 选项与默认行为——验证analyze_text等入口的参数默认值与行为契约;
  3. Formatter 行为(HTML/CSS 属性、元数据传播)——单元测试 formatter 的输出属性与元数据透传;
  4. Zero-shot 适配器(BIO/BILOU 转换)——即scripts/smoke_gliner.py--adapter路径所覆盖的to_token_classification转换逻辑。

编写这些测试时可直接复用 tests/conftest.py 提供的全局 fixture,例如:

  • sample_text/sample_long_text—— 短、长两档临床样例文本;
  • mock_pipeline/sample_predictions—— 预置了B-CONDITION(diabetes, score 0.95)、B-MEDICATION(metformin, score 0.89)、B-DOSAGE(500mg)等 BIO 标签预测结果的 mock pipeline,使 NER 后处理逻辑可以完全离线断言;
  • mock_tokenizer—— 带offset_mappingword_ids()的 mock tokenizer,用于验证 span 对齐;
  • sample_config—— 基于OpenMedConfig的测试配置(device="cpu"、缓存目录指向/tmp)。

小结

OpenMed 的测试实践可以浓缩为一条本地工作流:

# 安装开发依赖并运行质量门禁 make quality # = lint + type-check + format-check + test # 或以 CI 基线脚本方式一键执行(含 zero-shot 覆盖率) bash tests/run-tests.sh # zero-shot 冒烟(需要 [gliner] extras) uv pip install ".[gliner]" python scripts/smoke_gliner.py --limit 2 --threshold 0.4 --adapter

遵循 docs/testing.md 的分类表、tests/run-tests.sh 的 CI 基线以及上文覆盖建议清单,可以让新增代码的文档保持准确、自动化流水线保持绿色,这也是该项目"离线优先、可复现"工程哲学在质量保障层面的直接体现。

【免费下载链接】openmedLocal-first healthcare AI: clinical NER & HIPAA PII de-identification that runs 100% on-device. 2,200+ medical models, 21 languages, Apple MLX + Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 7:42:26

NavRL无人机动态避障实战:深度强化学习算法拆解与部署经验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 7:42:21

校徽大全.doc转图片资产库:批量提取、pHash去重与SQLite检索

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 7:42:04

2024年AI人才需求与核心技能解析

1. 行业现状与趋势解读2023-2024年全球AI人才争夺战已进入白热化阶段。根据LinkedIn最新发布的《全球AI人才趋势报告》&#xff0c;AI相关岗位的招聘需求同比增长达到惊人的187%&#xff0c;其中大模型研发、AI基础设施、垂直领域应用三大方向占据总需求的72%。国内头部互联网企…

作者头像 李华
网站建设 2026/9/18 7:41:58

SpringBoot+Vue企业级宠物猫认养系统架构解析

1. 项目概述&#xff1a;企业级宠物猫认养系统的技术架构解析这个基于SpringBootVueMyBatis的全栈项目&#xff0c;是为宠物救助机构或猫舍设计的专业化管理系统。不同于简单的宠物展示网站&#xff0c;它实现了从猫咪信息管理、在线认养申请到后台审核的完整业务流程闭环。我在…

作者头像 李华
网站建设 2026/9/18 7:40:57

VSCode搭建C/C++开发环境:从MinGW安装到调试配置全攻略

1. 先搞清楚你装的是什么&#xff1a;VSCode本身不编译C/C很多新手上路的第一课不是学语法&#xff0c;而是先挨一顿“为什么我装完了还是跑不了”的毒打。你在VSCode里新建了一个hello.cpp&#xff0c;敲完代码&#xff0c;兴冲冲点右上角的运行按钮&#xff0c;结果终端一堆红…

作者头像 李华
网站建设 2026/9/18 7:40:49

ModernTCN:用纯卷积结构取代Transformer,实现高效时间序列分析

一直做时间序列建模的朋友&#xff0c;估计都经历过这种纠结&#xff1a;用Transformer吧&#xff0c;效果确实好&#xff0c;可训练慢、显存吃紧&#xff0c;而且注意力机制对时间顺序天然不敏感&#xff0c;得靠位置编码硬拉回来&#xff1b;用传统CNN吧&#xff0c;速度快是…

作者头像 李华