资讯详情

openmed 临床上下文分析(analyze_clinical_context)实战指南:零模型的 ConText/体验者合成、结构化任务与事件时间线

📅 2026/9/19 19:23:39 | 华诺云谱 👁 阅读
openmed 临床上下文分析(analyze_clinical_context)实战指南:零模型的 ConText/体验者合成、结构化任务与事件时间线
openmed 临床上下文分析analyze_clinical_context实战指南零模型的 ConText/体验者合成、结构化任务与事件时间线【免费下载链接】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导读analyze_clinical_context是 openmed 中面向服务端调用方提供的源文本对齐临床上下文合成入口它不加载任何 NLP 模型、不调用外部服务而是把上游抽取器产出的实体 span 与 SDK 既有的章节检测sections、实体 span 校验、ConText/体验者experiencer分析组合成一个带边界约束的确定性流水线。本文基于 docs/clinical/context-analysis.md 展开结合 analysis.py 等源码实现系统讲解该函数的输入契约、默认任务、结构化任务medications / labs / vitals / relations、事件与时间线任务events / timeline、语言与限定qualification语义以及服务端必须遵守的 30 秒协作式截止与取消约束。读完本文你将能在自己的 openmed 服务或脚本中安全、正确地调用这一合成层并理解每条记录的出处与待审阅needs_review语义。1. 函数定位组合而非推理从源码 docstring 可以看出该模块的设计基调This module consumes already extracted entities. It does not load a model or contact a service, and it does not qualify an extraction as a patient fact.analysis.py。这意味着输入是实体不是文本模型analyze_clinical_context接收上游抽取器例如 openmed 的 NER 模型产出的(start, end, label)span并在此基础上做章节归属、断言negation/uncertainty/experiencer/temporality分析、结构化候选组装和时间线拼装。结果不带原文字面量返回记录只包含源文本偏移量offset与证据类别不复制触发词cue文本。测试test_german_sections_assertions_and_evidence_preserve_original_unicode_offsets专门断言序列化后的结果中不出现任何原始人名、疾病名、药物名或触发词test_context_analysis.py。结果是启发式注释不是确诊事实所有输出统一携带needs_review状态与clinical_task_not_qualified警告绝不把候选当作已确认的患者事实。在 openmed 中该函数从openmed.clinical顶层导出ClinicalAnalysisError、analyze_clinical_context、validated_clinical_entities见init.py服务端调用方通常在受控 worker 线程中执行它而不是放在 HTTP 事件循环上原因见第 7 节。2. 最小可运行示例德语家族史断言原文档给出了一个德语示例直接来自上游抽取结果from openmed.clinical import analyze_clinical_context text Familienanamnese: Mutter mit Diabetes. start text.index(Diabetes) result analyze_clinical_context( text, [{start: start, end: start len(Diabetes), label: Disease}], languagede, entity_coverage_completeTrue, ) assertion result[tasks][assertions][records][0] assert assertion[experiencer] family该示例展示了三件事章节先验驱动体验者Familienanamnese:被 SDK 章节检测识别为family_history章节因此Diabetes的experiencer被解析为family而非patient。测试test_section_prior_is_not_reported_as_an_explicit_subject_cue进一步确认此时context_sources[experiencer] section证据列表里只有section_header而不会把章节先验误报为显式主语线索test_context_analysis.py。结果按任务组织每个被请求的任务都有自己的complete、status、records字段。默认任务集合是(sections, entities, assertions)常量DEFAULT_CONTEXT_TASKSanalysis.py。语言必须显式给出断言规则目前只有英文/德文预览语言包自动语言检测若不确定、混合或不支持断言任务会返回unsupported绝不会静默回退到英文规则。返回的顶层结构包含schema_version、version当前为clinical-context-v5即常量CLINICAL_CONTEXT_VERSION、status、complete、source_chars、language含language/locale/source/confidence/mixed/needs_review与tasks字典以及qualification见第 6 节。3. 输入契约与显式失败边界3.1 实体校验validated_clinical_entitiesanalyze_clinical_context内部首先调用validated_clinical_entities(text, entities)analysis.py它负责源文本边界text必须是非空字符串且长度不超过 100,000 字符否则抛出ClinicalAnalysisError(invalid_clinical_source)。实体数量上限最多接受 2,000 个实体MAX_ANALYSIS_ENTITIES超出即报clinical_entity_limit实现使用islice提前截断不会无限消费上游的懒迭代器对应测试test_entity_limit_stops_consuming_an_unbounded_iterator。偏移与标签start/end必须是int且满足0 start end len(text)label必须是字符串并通过_LABELS归一化映射如medication/dosage→Drug、labname→Lab Test等。未知标签报unsupported_clinical_label。表面一致性若实体携带text字段必须与text[start:end]完全一致否则报clinical_source_mismatch。分数score/confidence若提供必须是0..1的有限数值nan、inf、布尔值或越界值都会报invalid_clinical_score缺失置信度保持unknownscoreNone绝不会被补成一个完美分数。去重与稳定 ID按(start, end, canonical_label)去重保留分数更高的记录随后按偏移排序并赋e1, e2, ...形式的确定性局部 ID。测试test_invalid_entities_fail_without_raw_error_echo逐项验证了这些失败路径并断言错误信息中不泄露任何私有标记文本test_context_analysis.py。3.2entity_coverage_complete上游覆盖承诺这是调用方必须认真对待的开关在设置entity_coverage_completeTrue之前上游调用方必须确认输入的全部 token 都已被实体抽取器处理过。若为False只有独立的sections任务可以成功entities、assertions以及所有结构化/时间线任务都会被扣留返回failedincomplete_entity_coveragerecords为空。对应测试test_incomplete_model_coverage_retains_only_independent_section_result验证status partial且 sections 完整、entities/assertions 不完整test_context_analysis.py。3.3 其他显式失败tasks必须是CONTEXT_TASKSsections、entities、assertions、medications、labs、vitals、relations、events、timeline的无重复子集否则invalid_clinical_tasks。entity_coverage_complete必须是严格布尔值。reference_date仅在timeline任务被请求时允许出现且必须是合法 ISO 日期\d{4}-\d{2}-\d{2}且date.fromisoformat可解析否则invalid_clinical_reference_date。timeout_seconds必须是0 timeout 30的有限数值否则invalid_clinical_deadline。4. 语言处理与语言限定语义语言解析由resolve_clinical_languagecore/clinical_language.py完成language参数支持显式语言/区域或保守的自动检测。关键行为只有 EN/DE 有断言与结构化预览规则CONTEXT_LANGUAGES (en, de)。当entity_coverage_complete为真、且语言解析结果needs_reviewFalse、mixedFalse、语言属于 EN/DE 时才进入完整的断言/结构化/时间线流水线。不支持的语言不会静默回退例如法语文本Antécédents familiaux: diabète.显式指定languagefr时断言任务返回statusunsupported错误码assertion_language_requires_supported_override而 sections/entities 仍可独立完成整体statuspartial测试test_unimplemented_assertion_language_never_falls_back_to_english。自动检测不确定或混合时同样要求显式覆盖测试test_uncertain_or_mixed_language_requires_an_explicit_context_override用 monkeypatch 模拟needs_reviewTrue/mixedTrue的自动检测结果断言任务同样返回unsupported——即不确定、混合或不支持三种情形都不会偷偷套用英文规则必须由调用方在审阅后显式提供一个受支持的语言。结构化任务medications/labs/vitals/relations与时间线任务events/timeline同样只在 EN/DE 显式支持下运行错误码为structured_language_requires_supported_override。从源码看analysis.py章节检测的语言只在language.source explicit或needs_reviewFalse时使用解析出的语言否则section_languageNone走保守的默认路径这进一步避免在不确定语言下引入误判。5. 结果语义偏移、出处与待审阅5.1 断言记录当任意需要断言的子任务assertions / 结构化 / 时间线被请求且complete时合成层会调用assert_context与scan_context_cuescontext.py并为每个实体生成一条断言记录{ entity_id: e1, start: 26, end: 34, negation: affirmed, uncertainty: none, experiencer: family, temporality: historical, context_sources: {negation: default, experiencer: section, ... : ...}, evidence: [{start: ..., end: ..., category: ..., direction: ..., source: context_cue}] }要点四个上下文轴negation、uncertainty、experiencer、temporality均保留local/section/default三种出处provenance即某个结论到底是来自局部线索、章节先验还是默认值。证据只含偏移与类别不含触发词文本evidence中的条目来自上下文线索sourcecontext_cue、局部主语线索sourcesubject_cue由resolve_experiencer提供见 experiencer.py或章节标题sourcesection_header且章节标题内的线索会被过滤in_header检查避免把章节名里的词误当成显式断言线索。体验者解析优先使用局部主语线索只有context_sources[experiencer] local且存在 cue 偏移时才追加subject_cue证据。记录不携带原始文本调用方如需复核需结合源文本按偏移切片查看。5.2 顶层状态所有请求任务都complete→ 顶层statusneeds_review注意即使完全处理结果仍是needs_review因为没有任何语言拥有独立的临床合格凭证。部分任务完成 →statuspartial。全部失败 →statusfailed。6. 结构化任务medications / labs / vitals / relationsSTRUCTURED_TASKSanalysis.py是可选任务对应合约clinical-context-v5底层实现在 structured_analysis.py。它们要求上游覆盖完整entity_coverage_completeTrue、语言为 EN/DE 显式支持即使没有显式请求assertions结构化任务也会计算上下文轴。所有结构化记录保留 negation / family 或其他 experiencer / temporality / uncertainty且coding_eligiblefalse——它们是未经确认的候选不是可编码的临床事实。6.1 通用边界源作用域scope_scopes以换行、分号、感叹号、问号、句点、对比连接词but/however/aber/jedoch以及章节边界为切点划分作用域跨作用域的 span 不能形成链接边。作用域内实体上限 64MAX_SCOPE_ENTITIES超限抛clinical_scope_entity_limit该任务不产出任何结构化记录但其他独立任务仍可保持complete。输出上限 4,096 条/任务MAX_STRUCTURED_RECORDS。歧义偏移同一偏移出现多个不同标签same-offset ambiguous labels时该任务返回空结果clinical_ambiguous_entity_offsets。辅助助手意外失败时只返回有限错误码不携带异常文本或源文本取消/超时则中止整个组合。6.2 Medications药物候选策略filter_medication_candidatesmedication_sig.py要求上游分数≥ 0.75拒绝计量缩写measurement abbreviations且不做术语接地grounding。缺失置信度不能变成完美分数。语言会透传给观测过滤器因此德语K 4,2 mmol/L不会被当作药物候选。剂量/频次/时长 span 的链接使用本地化归一化德语用,作小数点、英文用.其他属性只保留证据不做臆造的归一化。模式与链接分数是启发式不是校准后的临床概率。同标签相邻小数片段合并_repair_numeric_attributes只允许把显式本地化十进制数量的相邻同标签片段Dose/Dose、Strength/Strength重新拼接成一个合法数量/单位且要求片段间恰好是语言对应的小数分隔符、原始文本能整体解析为一个有效数值。结构化记录会同时引用source_parts原始两部分与修复规则原始实体记录不因该修复而改变structured_analysis.py。Strength 永远是 Strength包括产出DRUG_STRENGTH关系也不能被当成处方剂量其数值/单位走数量归一化器。部分单位、不同数量、跨作用域/上下文变化都不能拼接。数量边界检查先于归一化小数尾巴不能变成更小的剂量即使模型片段带有冲突的 Dose/Strength 标签。无法识别或不完整的数量只暴露有限原因和recognizedfalse不给出猜测值、单位或携带源码文本的解析器消息。不安全的数量尾巴不能形成剂量关系。章节标题预测保留在实体结果中但不能成为结构化的药物/检验发现。6.3 药物小数边界修复boundary repair当 Drug 预测的终点落在一个十进制数量内部、且该数量后有可识别单位时SDK 可以把药物边界修剪到前一个词_repair_drug_decimal_boundariesquantity_evidence.py。规则要求数量片段内含语言对应的小数点德语,/ 英文.、药物结束于数量数字之前、数字前是空白修复后的实体带span_repairdrug_boundary_inside_decimal_quantity、score_kindmodel_score_before_boundary_repair该置信度描述的是原始 span不是新边界的校准分数、source_parts保留原始预测、quantity_evidence记录完整数量偏移药物名称中的整数后缀、未知单位、部分复合单位不能触发该修复。6.4 书面数量written_amounts药物记录还携带written_amountsquantity_evidence.py它们是紧跟在药物同一行、紧随其后的完整可归一化数量带源偏移、支持 Dose/Strength 预测且semantic_typeunspecified、score_kinddeterministic_source_pattern、requires_reviewTrue。典型场景当冲突的模型标签阻止属性合并时恢复书面量47,5 mg但不把它转换为已确认的处方剂量或产品规格。不推断缺失数量、不挂接远处测量既有的部分剂量拒绝逻辑仍然生效。数量扫描上限2,000 个源数量候选超出抛clinical_quantity_limit没有药物预测的文档不会触发该扫描也不会继承该限制。数量正则_QUANTITY覆盖µg/mcg/mg/kg/g/ml/l/iu/ie等单位并要求完整、可单位归一化命中范围如-//接续不会被当作截断数量释放。6.5 Labs 与测量提供的 analyte/value/range/flag span 只允许在同一源作用域内链接。规范量纲与范围边界包含单位例如55 %归一化为0.55unit1原始值范围作为证据保留缺失范围单位时使用显式测量单位。未知单位、无法解析的值、未链接的 analyte、孤儿 value span 都保持可检查状态不臆造数值显式异常标志优先于参考范围比较不发明任何危急阈值。合并的 Lab Test span单个 token 名称 一个完整可解析的书面数量可以同时提供名称与数值两部分。合并的 Vital Sign span只有显式 LVEF或英/德全称如left ventricular ejection fraction/linksventrikuläre Ejektionsfraktion加书面百分数可以走这条路——把 LVEF 当作labs中的命名测量处理不新增 vital-sign 类别。原始实体与未解析的 vital 记录仍可检查。派生名称/数值引用具有确定性的模式出处pattern provenance且不分配置信度source_parts保留原始模型偏移、标签与分数上下文继承自该模型 span。重叠模型证据、不完整数量、比较式、范围、多值和跨作用域都不能提供派生测量它不借用邻近观察的参考范围或异常标志、不补缺单位或代码始终是未确认的复核候选。6.6 Vitals每个定位到的源 span 必须只描述一个测量同时含血压与心率的 span 会被标记为 unparsed而不是悄悄保留第一个测量。血压分量保留其源上下文与捕获单位不推断缺失单位。6.7 Relations仅偏移量组成的 drug-dose/route、problem-anatomy、finding-severity 候选保留两个端点的上下文。源作用域阻止跨行、跨句/分号边界、跨对比从句与跨章节的链接。7. 事件与时间线events / timelineTEMPORAL_TASKS (events, timeline)analysis.py底层实现在 temporal_analysis.py组合了 SDK 的药物变更事件extract_medication_change_events与实验室趋势事件extract_lab_trend_eventsframe 构建器以及时间线组装器timeline/。它们同样是 EN/DE 预览任务。7.1 调用示例result analyze_clinical_context( text, entities, languagede, tasks[events, timeline], reference_date2026-09-08, # 仅当这是文档已知日期时才提供 )7.2 事件规则要点药物 head 保持0.75 候选阈值原始 head、trigger、属性与日期偏移伴随每个候选但源表面文本与原始助手消息被排除。数量片段守卫同样作用于新旧事件剂量显式from/von数量在缺少对应方时不能充当 new-dose 角色to/auf数量不能充当 old-dose 角色。显式变更语法可把完整 Strength span 赋给旧/新剂量von/zuvor/bisher或英文from/prior/previous/former→old_doseauf/jetzt/nun或to/now/new→new_doserole_sourceexplicit_change_grammar且保留原始标签与偏移孤立的 Strength 没有这类措辞就不能成为事件剂量。德语 trigger 规则区分开始、重启、停止、增加、减少与保持start/restart/stop/increase/decrease/hold以及上升/下降/稳定的实验室趋势。持续中的方案不会被推断为已重启同一从句内存在多个竞争 head 且动作无法无歧义指派时保持为临床提及药物旁的升高实验室值不能变成剂量增加事件。事件不能跨句、跨行、跨分号、跨对比从句或跨章节边界链接构造作用域时保护源日期不被内部标点切分时间表达式所在范围不会成为切点见_windows中if not any(t[start] m.start() t[end] for t in timexes)。事件 head 上下文与 trigger 上下文都保留一个带否定 trigger 上下文的stopped触发器不会断言该药物真的被停用。family、historical、uncertain、negated 的发现都是可复核候选每个事件coding_eligiblefalse。7.3 reference_date 与时间锚定reference_date是可选 ISO 日期只在 timeline 任务下接受否则invalid_clinical_reference_date用于解析相对表达如昨天/上周没有它相对表达保持未锚定。实现不会用当前日期或文档日期替代表达式缺少可用日期证据的事件。一个临床事件最多使用其自身作用域内的一个无歧义日期竞争日期、无效日期与出生日期上下文保持未锚定_IDENTIFIER_DATE正则覆盖dob/date of birth/born/geburtsdatum/geboren/geb命中时valueNone且加identifier_date标志。德语数字日期遵循显式 DMY 规则支持完整德语月份名与相对日/周/月/年短语两位年份保持有歧义。英文模糊斜杠日期不解析。德语Morgen在am/jeden Morgen等语境中不解释为明天加ambiguous_time_of_day标志。不做任何文本翻译。7.4 时间线呈现已锚定事件按时间戳顺序呈现未锚定事件单独按源顺序分组。呈现顺序不是声称的时间或因果先后区间锚点保留两端未锚定事件不会被断言为发生在锚定组之后。原始的四个上下文轴negation/uncertainty/experiencer/temporality被完整保留而不会收窄成组装器的历史上下文枚举。7.5 时间线边界最多接受2,048 个时间跨度MAX_TEMPORAL_SPANS超限clinical_temporal_span_limit、每个源作用域64 个实体、每个作用域/引擎128 个事件触发器clinical_event_trigger_limit、输出4,096 条事件记录。依赖任务失败时返回空结果与有限错误独立的 sections/entities 仍可用。这些都是确定性预览规则没有独立的事件或时间线精度合格认证。8. 章节语义补充德语治疗/病程标题原文档还说明了一个德国章节映射细节德语Therapie/Behandlung与Verlauf/Klinischer Verlauf标题被映射为中性neutral的treatment与clinical_course章节见 lexicons/section_headers.py 中的(Therapie, Behandlung)与(Verlauf, Klinischer Verlauf)。这一映射会结束继承的家族史作用域不再把后续内容当作 family-history 语境但不为这些章节分配未经验证的术语代码也不赋予未来/过去的时间先验。9. 协作式截止、取消与服务端部署约束analyze_clinical_context的 30 秒后处理截止与取消检查是协作式的deadline time.monotonic() timeout_secondscheck()在 SDK 阶段之间与证据组装期间被反复调用一旦超过截止时间抛clinical_timeout取消回调返回真时抛clinical_cancelledanalysis.py。它们不能抢占正在执行的 Python 助手helper因此数量、实体数、时间跨度等计数与源长度上限用于限制准入的工作量见第 3、6、7 节的各种_limit。服务端必须在受控 worker 上运行此函数绝不能放在 HTTP 事件循环上——这正是设计文档强调的部署约束避免阻塞事件循环或让截止/取消机制失效。测试test_cancellation_and_postprocessing_deadline_cannot_return_partial_success验证取消回调为真时立即抛错模拟time.monotonic跳变到 31 秒时抛clinical_timeout——截止/取消不能返回部分成功test_context_analysis.py。10. 最佳实践清单综合原文档与源码实现调用方应遵守以下实践先保证覆盖完整只有在确认上游引擎处理了输入的每一个 token 后才设置entity_coverage_completeTrue否则只依赖独立的 sections 结果。显式给出受支持语言在人工审阅语言判定后为上下文分析显式传入languagede或en不要把自动检测的不确定/混合/不支持语言悄悄交给断言规则。善用reference_date需要相对时间表达解析时在tasks[timeline]下传入文档已知的 ISO 日期不要期望实现替你做日期替代。把输出当复核候选所有记录都是启发式注释coding_eligiblefalse、needs_review线上使用前需结合源文本复核尤其是 written_amounts、Strength 赋角色、LVEF 派生测量等特殊路径。服务端放在受控 worker遵守 30 秒协作式截止与取消检查约束通过任务计数与源限制控制准入工作量。尊重边界上限2,000 实体、64 实体/作用域、2,000 数量候选、2,048 时间跨度、128 触发器/作用域/引擎、4,096 记录/任务——超限行为是显式失败或任务级空结果属于预期契约而非缺陷。结语analyze_clinical_context把 openmed SDK 中分散的章节检测、ConText 断言、体验者解析、药物/检验/生命体征结构化与事件时间线组装整合为一个有边界、确定性、源对齐的合成入口。它不引入模型、不泄漏原文表面、不虚构数值也不把启发式注释伪装成确诊事实——所有输出都带出处local/section/default与明确的待审阅状态。理解其输入契约、语言限定语义与各类上限是在 openmed 服务中正确使用这一组合层的先决条件而 tests/unit/clinical/test_context_analysis.py 与 tests/unit/clinical/test_structured_analysis.py、tests/unit/clinical/test_temporal_analysis.py 则提供了可直接运行的契约验证样例可作为接入时的回归基准。【免费下载链接】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),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。