OpenMed v2.2 到 v2.3 迁移指南:Python API 兼容边界验证、npm 令牌偏移对齐与隐私安全升级实践
【免费下载链接】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 官方迁移文档 docs/migration/2.2-to-2.3.md 为核心骨架,结合仓库内
api_surface_diff.py比对工具、openmed/源码实现与js/openmedkit-web前端契约,系统讲解从v2.2.0升级到v2.3.0时你需要关注的兼容性结论、可复现的静态比对方法、八个升级检查项、多模态/Agent/训练三大新增契约,以及 npm 端 token-classification 偏移对齐的破坏性细节与修复方案。读完本文,你将能够独立复现官方 API 兼容性审计、正确迁移 npm 消费者代码,并完成一次符合隐私边界的版本升级。
OpenMed 2.3.0 是一个以"纯增量、零破坏"为设计目标的 SDK 版本:Python 公开 API 从 37,735 个符号增长到 41,729 个,新增 3,994 个公开符号,没有移除或重命名任何公开符号、没有收窄任何已有可调用对象的签名、也没有任何既有符号被新标记为@deprecated。但这并不意味着升级可以"原地替换包版本"了事——REST 契约、Swift、Kotlin/Android、JavaScript、CLI、配置、trace schema、证据记录、模型产物与部署契约都需要单独复核,尤其是 npm 端 token-classification 的偏移对齐行为发生了实质变化。
一、兼容性结论:一次由静态 AST 审计背书的安全升级
官方迁移文档给出的兼容性结论如下:
- Python 静态 API 清单从37,735 增长到 41,729个符号;
- 新增3,994个公开符号;
- 0个公开符号被移除或重命名;
- 0个既有可调用对象签名被收窄;
- 0个既有符号被新标记为
@deprecated; - REST 契约保持不变:仍为19 个路径(paths)与 17 个组件 schema(component schemas)。
由于静态比对没有发现"移除"或"弃用"类变更,因此官方没有提供任何"before/after 替换片段"。同时文档明确划定了审计边界:Python AST 比对不检查Swift、Kotlin/Android、JavaScript、CLI、配置、trace schema、证据记录、模型产物与部署契约,这些表面必须单独人工复核。
从版本发布记录(docs/release/v2.3.0.md)可知,v2.2.0..v2.3.0的审计分支区间包含 252 个提交、651 个变更文件,提交主题关联 127 个 issue/PR 编号;REST 表面保持 19 路径与 17 组件 schema 的结论与迁移文档一致,属于官方公布的实现事实。
二、复现比对:api_surface_diff.py 的工作原理与运行方式
迁移文档提供了在本地完整复现上述结论的命令(运行于仓库根目录):
python scripts/release/api_surface_diff.py \ v2.2.0 HEAD \ --json api-surface-diff.json \ --check docs/migration/2.2-to-2.3.mdbefore_ref(如v2.2.0)为基线 Git 引用;after_ref(如HEAD)为候选引用,也支持WORKTREE哨兵值来直接比对当前工作区文件;--json api-surface-diff.json输出机器可读的 diff(使用-可输出到 stdout);--check docs/migration/2.2-to-2.3.md将迁移文档作为完整性门禁:若文档缺少某个 breaking 或 deprecated 符号,命令以非零码退出并列出缺失符号。
该工具的实现位于 scripts/release/api_surface_diff.py,其设计有几个值得注意的特点:
1. 纯静态、零导入。提取器从 Git(git archive打包指定 ref)或当前工作区读取源码,用 Python 标准库ast解析,故意从不 import 被审计的包(见文件头注释:"It deliberately never imports the package being inspected")。这保证比对过程离线安全、无 import 期副作用。
2. 符号指纹(fingerprint)。每个函数/类/属性都会基于ast.dump的规范化输出计算 SHA-256 指纹(scripts/release/api_surface_diff.py),用于识别"重命名"(同一指纹的 removed+added 配对)而非单纯删除。
3. 破坏性分类精确。diff_surfaces()将变更归类为renamed、removed、deprecated、signature-narrowed、added五类;签名收窄检查逐参数比对(参数是否被移除、可选变必选、positional/keyword 种类转换、位置顺序变化等),并允许positional_only -> positional_or_keyword这类安全放宽。
4. 迁移文档完整性门禁。missing_migration_symbols()用单词边界正则((?<![A-Za-z0-9_.])symbol(?![A-Za-z0-9_.]))扫描迁移文档是否提及每个 breaking/deprecated 符号,缺一个就让--check失败,把"文档与代码事实同步"变成发布流程的硬约束。
除了上面的 CLI,脚本还提供可编程入口:extract_surface()、diff_surfaces()、compare_refs()与check_migration_document(),适合接入 CI 脚本做常规的 API 漂移巡检。
三、升级检查清单:八个必须在发布前完成的动作
迁移文档给出了八个升级检查项,按执行顺序整理如下:
- 精确安装:仅安装
openmed==2.3.0,且只带应用实际用到的 optional extras(避免引入无谓依赖与隐私面)。 - 重新执行隐私与安全测试:在合成测试样本(synthetic fixtures)上,对每个部署语言、脚本、文档格式和量化运行时重跑隐私(privacy)、直接标识符召回(direct-identifier recall)、关键泄漏(critical-leakage)、span 完整性(span-integrity)与确定性安全(deterministic safety)测试。
- 重新认证多模态资产:对应用自有的多模态资产 profile、manifest 限制、摘要(digest)策略、弃权(abstention)处理与文档解析验收测试做重新认证。
- 刷新机器可读契约消费者:在启用新契约前,重新生成/升级 CLI、REST、schema、agent-outcome 与 trace 的消费者代码。
- 保持数据边界:PHI、凭据、受限数据集、授权词表(licensed vocabularies)、外部模型权重与第三方运行时不得进入包及证据记录。
- 临床证据定位:临床证据、SDOH 提取、记录过滤、术语映射与训练输出一律视为需合格人员复核的辅助证据,不得自动触发诊断、治疗、计费或发布行为。
- 真实平台演练:对启用的桌面、浏览器、Android、Apple、TensorRT、WebGPU、搜索、批处理或集群适配器,在实际部署平台上逐一演练。
- 运维表面评审:在推广新运维表面之前,评审容器、Helm、Kubernetes operator、HPA、sidecar 与 remote-function 策略。
第 5、6 条与 OpenMed 一贯的隐私边界一致:核心本地处理不新增强制网络或遥测路径;v2.3.0发布说明也再次强调,模型下载与可选的远程集成是显式边界,且遥测默认关闭、证据记录只保留哈希/计数/偏移/溯源而非原始标识符。
四、多模态 intake 与溯源:受限清单、流式摘要与失败关闭
OpenMed 2.3 在多模态侧新增的能力包括:有界资产清单(bounded asset manifests)、流式摘要(streaming digests)、媒体类型检测(media-type detection)、类型化弃权记录(typed abstention records)、确定性 manifest profile、扩展的 PDF 布局与保真工具、本地邮件处理,以及更多文档格式的隐私安全提取与脱敏。这些 API 对畸形或无界输入一律"失败关闭"(fail closed)。
4.1 隐私安全的资产清单(AssetManifest)
模块openmed.multimodal.asset_manifest(源码见 openmed/multimodal/asset_manifest.py,配套文档 docs/multimodal/asset-manifests.md)为 preflight 步骤提供一个刻意狭窄的清单,只记录以下字段:
| 字段 | 说明 |
|---|---|
version | 清单版本 |
asset_id | 资产标识 |
media_type | 媒体类型 |
sha256 | SHA-256 摘要 |
byte_size | 字节数 |
pages/width/height/frames/duration_seconds | 可选的有界计数 |
验证器拒绝未知字段、路径、URL 与任何自由文本字段(如描述或来源元数据);校验失败的错误只点名失败字段或规则,不回显提交的值。整数大小与计数分别使用显式的 64 位与 32 位上界;duration 必须是有限、正数且与其他计数同界;布尔值、标量子类、重复 JSON 字段与越界数值一律失败关闭。官方示例:
from openmed.multimodal.asset_manifest import AssetManifest manifest = AssetManifest.from_dict( { "asset_id": "dicom-001", "media_type": "application/dicom", "sha256": "a" * 64, "byte_size": 4096, "frames": 12, "width": 512, "height": 512, } ) payload = manifest.to_json()to_dict()按稳定顺序输出字段并省略未设置的可选字段;to_json()输出键排序的紧凑 JSON,保证两个调用方能确定性比较清单负载。清单本身不读取资产、不执行 OCR/推理、不保留内嵌元数据,也不定义模型相关的张量形状。
4.2 有界流式摘要(digest_asset)
openmed.multimodal.digest的digest_asset(源码见 openmed/multimodal/digest.py,文档见 docs/multimodal/asset-digests.md)在不把 PDF、图片、DICOM、音频等二进制资产整体载入内存的前提下计算 SHA-256:
from openmed.multimodal.digest import digest_asset with open("synthetic-scan.dcm", "rb") as stream: result = digest_asset(stream, max_bytes=2 * 1024**3) print(result.sha256) print(result.byte_count)- 流从当前读取位置开始,按不超过1 MiB的块读取;
- 可寻址流在成功或失败后都会恢复到原位置,且 helper 从不关闭调用方拥有的流;不可寻址流则被消耗;直接传
bytes则对内存中的值就地哈希; max_bytes为可选硬限制:helper 至多再多读 1 字节以检测溢出,然后抛出DigestLimitExceededError,错误信息只含digest_size_limit类别、maximum_bytes与bytes_read,绝不包含路径、文件名或资产内容;其他流故障抛出无值的DigestStreamError;- 结果包含小写 64 字符 SHA-256 十六进制摘要与精确哈希字节数;该函数不打开路径、不校验媒体、不查毒、不签名、不提供内容寻址存储。
4.3 媒体类型检测、类型化弃权与 manifest profile
配套的多模态能力在仓库中均有独立文档与实现可查:媒体类型检测的失败关闭语义见 docs/multimodal/media-type-detection.md("Invalid declared types fail closed with a value-free error");类型化弃权记录的实现位于 openmed/multimodal/abstention.py,其设计要点是只接受 stage + reason code 两个元数据字段,没有自由文本字段,因此调用方在说明"为何停止处理"时不可能顺带序列化出 OCR 文本、转写、DICOM 值、文件路径、URL 或模型提示词。弃权阶段与原因构成一个封闭组合矩阵:
| 阶段(AbstentionStage) | 允许的原因(AbstentionReason) |
|---|---|
preflight | unsupported_media、resource_limit、provider_unavailable |
decode | malformed_media、resource_limit、low_quality |
inference | resource_limit、low_quality、phi_uncertainty、speaker_uncertainty、temporal_instability、provider_unavailable |
post_process | 同类封闭集合(实现于 openmed/multimodal/abstention.py 起的常量表) |
更多细节可继续阅读 docs/multimodal/abstention-reasons.md、docs/multimodal/asset-limits.md、docs/multimodal/manifest-profiles.md、docs/multimodal/pdf-redaction-fidelity.md 与 docs/multimodal/preflight.md。迁移文档给出的使用原则是:保留调用方拥有的流语义、使用声明的 size 与 digest 限制、只保留复核所需的元数据/偏移/哈希/聚合证据——这正是"有界输入 + 元数据化证据"的落地方式。
五、Agent、trace 与训练契约:封闭词表与可复现记录
5.1 Agent:封闭的结局码与确定性摘要
Agent 运行新增了封闭结局码(closed outcome codes)、确定性摘要、单调时间记录、consent 校验结果与 PHI 安全的失败原因。核心实现是openmed.agent.outcomes(源码见 openmed/agent/outcomes.py,配套文档 docs/agent/outcome-reasons.md),其载荷只有三个字段:schema_version(openmed.agent.outcome.v1)、outcome_class、reason_code,不存储提示词、工具参数、工具输出、证据文本、路径或凭据。
公开 API 区分以下五类结局,且类/原因组合是封闭的:
| outcome_class | 允许的 reason_code |
|---|---|
success | completed |
abstained | insufficient_evidence、out_of_scope、low_confidence |
review_required | conflicting_evidence、safety_review、human_gate |
policy_denied | consent_required、purpose_mismatch、phi_policy |
failed | tool_error、timeout、invalid_input |
未知类、未知原因码、类/原因不匹配、多余字段与自由文本原因一律失败关闭;异常消息只点名字段或稳定错误码,不回显提交值。示例:
from openmed.agent import WorkflowOutcome outcome = WorkflowOutcome.from_dict( { "outcome_class": "abstained", "reason_code": "insufficient_evidence", } ) payload = outcome.to_json()to_dict()稳定排序输出、to_json()键排序紧凑序列化,保证相同输入字节级一致——这与 run-summaries、timing-metadata 等契约共同构成"可确定性复核"的 Agent 证据链(参见 docs/agent/run-summaries.md、docs/agent/timing-metadata.md、docs/agent/event-correlation.md 与 docs/agent/governance-identifiers.md)。
5.2 Trace:本地发现、保 schema 脱敏与事务恢复
trace 工具新增了本地发现(local discovery)、保 schema 脱敏(schema-preserving redaction)、流式与并行执行、事务恢复(transactional recovery)、保真校验(fidelity checks)与训练 schema 适配器。仓库内对应实现与文档包括:
- 事务式本地脱敏:openmed/traces/transaction.py——源文件只读一次,转换后的字节先写入并
fsync到同级临时文件(前缀.openmed-transaction-),所有 pre-commit 检查通过后才替换目录项;所有异常消息刻意无值,敏感文本永远不会被复制进异常(TransactionError的类注释明确说明这一点); - 保真校验:openmed/traces/fidelity.py 与 docs/reference/trace-fidelity.md;
- 流式执行:openmed/traces/streaming.py 与 docs/guides/streaming-traces.md;
- schema 化 trace:openmed/traces/schemas/ 下的
chat.py、columnar.py、preference.py与 docs/reference/chat-schema.md 等。
5.3 训练:教师集成清单、联邦轮次与可复现性
训练侧新增教师集成清单(teacher-ensemble manifests)、确定性联邦轮次记录(deterministic federated-round records)与可复现性验证(reproducibility verification)。这些都是增量契约(additive contracts),但下游 schema 消费者应当先刷新快照并显式处理新的封闭词表,再启用它们。
一个可直接体验的示例是训练会话 schema 注册表 openmed/traces/schemas/registry.py(文档见 docs/reference/training-schemas.md):它内置识别messages(role/content 消息列表)、sharegpt(conversations[].value)与preference(prompt/chosen/rejected)三类布局,检测是结构化且确定性的,不加载模型、不读数据集、不发网络请求:
from openmed.traces.schemas.registry import TrainingSchemaRegistry registry = TrainingSchemaRegistry() record = { "messages": [ {"role": "user", "content": "Synthetic user value"}, {"role": "assistant", "content": "Synthetic answer"}, ] } schema = registry.resolve(record) print(schema.name) # messages for path, text in registry.walk(record, schema=schema): print(path, text)自动检测在"无 schema 匹配"或"多个 schema 同时匹配"时失败关闭;歧义记录必须显式指定schema=才能重建副本。transform()始终返回副本并保留原始嵌套;注册表错误从不包含记录值,诊断中的 schema 标识以确定性schema_sha256_...标签代替调用方提供的名称。自定义 schema 可实现TrainingConversationSchema协议并通过registry.register(...)注册(进程内注册,无发现或网络副作用)。
六、集成与运行时:可选适配器与显式信任边界
v2.3 新增的集成适配器均为opt-in,覆盖:OpenSearch、Elasticsearch、Spark、Beam、Airflow、LlamaIndex、PostgreSQL、dbt、仓库远程函数(warehouse remote functions)、Kubernetes、浏览器扩展、Electron、Tauri、Android 加速器、TensorRT、GGUF 与 WebGPU。迁移文档明确提醒:外部进程、集群、浏览器、数据库、模型文件与凭据都是显式信任边界,适配器必须在真实目标平台上逐一演练(对应检查项 7)。
关于 MedCAT 桥接,迁移文档给出了三条明确事实:它保持进程外(out of process)运行、不安装任何受限依赖,并要求使用者显式确认其第三方许可证。核心本地处理不获得强制网络或遥测路径——这与 OpenMed "local-first、默认无遥测" 的整体设计一致。
七、npm token-classification 兼容性:偏移契约与修复路径
这是 2.2→2.3 迁移中最容易被忽略、也最需要动手改代码的部分。官方结论:npm 默认模型现在是OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1-onnx-android,导出为DEFAULT_MODEL_ID;-onnx-android仓库使用根目录 INT8 加载器。使用该默认路径需要安装可选的@huggingface/transformerspeer 依赖,或注入你自己的本地 pipeline;模型下载与本地推理是分离的(推理不把临床文本发给托管 API)。
7.1 类型契约:既有数值偏移保留,新增 Raw 输入类型
既有TokenClassificationEntity、TokenClassificationPipeline与模型加载器的输出保留 v2.2 的数值start/end契约;新增的RawTokenClassificationEntity、RawTokenClassificationPipeline与RawTransformersRuntime类型则接受可能省略偏移的运行时输入(类型定义见 js/openmedkit-web/src/types.ts:
/** Unaligned runtime input; public aligned entities retain numeric offsets. */ export type RawTokenClassificationEntity = Omit<TokenClassificationEntity, "start" | "end"> & { start?: number; end?: number; };最终的OpenMedSpan偏移仍然必填,且使用 JavaScript UTF-16 索引(OPENMED_SPAN_SCHEMA_VERSION = 1,span 含start/end/text_hash/entity_type等字段)。
7.2 对齐机制:alignTokenOffsets 与失败关闭
调用alignTokenOffsets(text, tokens)可以把 raw token 转换成对齐实体;模型加载器在返回 pipeline 输出之前会自动执行该转换(见 js/openmedkit-web/src/model-loader.ts 中loadTokenClassificationPipeline对输出统一调用alignTokenOffsets的 Proxy 包装)。对齐实现位于 js/openmedkit-web/src/offsets.ts,关键行为:
- 大小写与变音符不敏感:先做 Unicode 规范化(NFD + 剥离组合标记),因此 BERT 式小写分词器仍能对齐;
- 容忍多种分词标记:WordPiece
##、SentencePiece▁、byte-level BPEĠ都会被剥离后参与匹配,特殊 token([CLS]/[SEP]/<s>等)被跳过; - 顺序游标 + 有界回退搜索:连续 token 只在游标附近 16 字符窗口内搜索,被上游丢弃的 token(如 ignored labels)造成的 index 空洞会自动放宽为全串搜索;
- 对齐失败抛"无内容"错误:
new Error("Token offset alignment failed; provide source offsets.")——绝不静默省略脱敏 span; - 显式偏移优先:已带有限数值偏移的 token 原样保留并推进游标。
7.3 extractPii 的 O-token 保留与错误语义
extractPii()保留Otoken 以保证顺序对齐;显式 pipeline 偏移被原样保留;缺失偏移通过大小写/变音符与 token 标记归一化重建;未知或无法对齐的 token 抛出无内容错误,而不是静默省略脱敏 span。对自定义分词器或过滤输出,迁移文档给出两条硬性规则:
- 提供精确的源偏移(exact source offsets);
- 把对齐错误当作**一次失败的扫描(a failed scan)**处理,永远不能当作"文档不含 PII"的证据——因为未对齐意味着可能存在漏掉的实体,而不是没有实体。
八、发布与模型证据:SDK 发布不等于模型指针推进
OpenMed 2.3.0 是SDK 发布,本身不推进任何模型指针。模型转换、评估与发布是显式的地方维护者操作(explicit local maintainer operations)。未来若推进指针,仍必须满足完整门槛:真实的分阶段候选(staged candidate)、公开的 SHIELD 与 golden 证据、签名的抽取门禁(signed extraction gates),以及最终就绪判定恰好为READY。
SDK 标签由以下证据独立限定(qualify):
- 本文所讲的迁移指南;
- 机器可读的 API 比对结果(
api-surface-diff.json); - 精确提交(exact-commit)的单元与平台门禁;
- 包产物检查(artifact inspection);
- 保留的 last-green 模型证据。
配套的 docs/release/v2.3.0.md 进一步明确了发布硬约束:宿主检查必须指向精确发布提交(早期 source head 上的成功任务不算数)、镜像/包发布与注册表验证都是 tag 驱动的后续动作;证据链层面,docs/security/evidence-integrity.md 展示了报告/清单摘要使用sha256:<64 位小写十六进制>规范、invalid_manifest/hash_mismatch等稳定失败类别的落地形态。
一个值得注意的上游依赖事实:可选依赖树中的 NLTK 3.10.3 存在模型产物路径安全公告 CVE-2026-81726,截至 2026-09-14 无已发布修复版本;OpenMed 不调用受影响的模型文件 API,服务镜像也不安装 NLTK。使用agents、llamaindex、quickumls、scrubadub等可选依赖的应用不得把 NLTK 模型导入/导出路径暴露给不可信输入,且该豁免仅限此 CVE、nltk包与uv.lock目标,到期时间为 2026-09-28——这是官方明示的已知上游限制,而非"NLTK 已修复"的声明。
九、迁移动作汇总
把全文要点压缩成一张可直接执行的自检表:
| # | 动作 | 关键依据 |
|---|---|---|
| 1 | 运行api_surface_diff.py v2.2.0 HEAD --json ... --check ...复现兼容性结论 | scripts/release/api_surface_diff.py |
| 2 | pip install --upgrade "openmed==2.3.0"(或按需加[hf,fhir]/[mlx]等 extras) | docs/release/v2.3.0.md |
| 3 | 在全部部署语言/格式/量化运行时重跑隐私与泄漏测试 | 迁移文档检查项 2 |
| 4 | 复核多模态 manifest/digest/abstention 相关实现与配置 | docs/multimodal/asset-manifests.md、docs/multimodal/asset-digests.md |
| 5 | 刷新 Agent 结局码、trace、训练 schema 的消费者代码 | openmed/agent/outcomes.py、openmed/traces/schemas/registry.py |
| 6 | npm 侧:确认默认模型、安装@huggingface/transformers、检查偏移对齐 | js/openmedkit-web/src/model-loader.ts、js/openmedkit-web/src/offsets.ts |
| 7 | 逐平台演练启用的集成/运行时适配器,评审容器与 K8s 运维策略 | 迁移文档检查项 7、8 |
| 8 | 保持 PHI/凭据/受限数据在包与证据之外,临床输出走合格复核 | 迁移文档检查项 5、6 |
升级到 OpenMed 2.3.0 的本质是:在"零 Python 破坏"的前提下,把多模态资产的隐私安全边界、Agent 结局的封闭词表、trace 的事务式脱敏、训练记录的可复现性,以及 npm 端偏移对齐的正确性,一次性引入你的部署栈。只要按本文的比对方法复现审计、按八项清单逐条执行、并在 npm 侧落实偏移契约与"失败即扫描失败"的错误语义,这次升级就是一次低风险、可验证、可追溯的版本演进。
【免费下载链接】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),仅供参考