Commit 4fbc08a7 by luoqi

fix(ingest): 宿主 ICD 错标纠偏 K00后天缺失→K08 — 修"萌出异常"误召(一线反馈⑭)

关平 BJ0U007377 牙25:残根拔除后缺失,宿主把"牙齿缺少"标 std_code K00(=牙发育/
萌出障碍)→ 误召"萌出异常未处置"。全库 1044 例同类错标。

区分符=「先天」:K00 真覆盖先天缺牙(462 例,发育障碍)+ 萌出/多生牙;后天缺失的
"牙齿缺少/缺失/缺牙"被错标 → 应 K08。
- types reconcileDiagnosisCode(code,name):code=K00 且 name 含缺(牙/失/少)且无"先天"
  → K08;其余 K00 不动(单一源,与 K07.303→K03 同类纠偏);
- diagnosis.parser 落码前调用纠偏。

验证(本地重摄 21 人):关平25 K00→K08,召回从"萌出异常"(66)改判"缺失牙未修复"
(81,正确);21 人里 9 条诊断纠偏;前四类修复回归全保留。需 reparse 存量诊断。
仅本地,未部署。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parent d0e76a61
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
FactStatus, FactStatus,
FactType, FactType,
PACDiagnosisCodeSchema, PACDiagnosisCodeSchema,
reconcileDiagnosisCode,
} from '@pac/types'; } from '@pac/types';
import type { Parser, ParserContext, FactDraft } from './parser.interface'; import type { Parser, ParserContext, FactDraft } from './parser.interface';
import { normalizeToothPosition } from './tooth-position.util'; import { normalizeToothPosition } from './tooth-position.util';
...@@ -42,7 +43,11 @@ export class DiagnosisParser implements Parser { ...@@ -42,7 +43,11 @@ export class DiagnosisParser implements Parser {
// code 校验:命中 PAC 闭集 → 用;否则置 null(不丢 fact,留 message 给 LLM 补码) // code 校验:命中 PAC 闭集 → 用;否则置 null(不丢 fact,留 message 给 LLM 补码)
const rawCode = String(c.code ?? '').trim(); const rawCode = String(c.code ?? '').trim();
const codeParsed = PACDiagnosisCodeSchema.safeParse(rawCode); const codeParsed = PACDiagnosisCodeSchema.safeParse(rawCode);
const code = codeParsed.success ? codeParsed.data : null; const validCode = codeParsed.success ? codeParsed.data : null;
// 宿主 std_code 错标纠偏(K00 后天缺失 → K08;详见 reconcileDiagnosisCode)
const reconciled = reconcileDiagnosisCode(validCode, cleanName((c.name as string | undefined) ?? null));
const code = (reconciled ? PACDiagnosisCodeSchema.safeParse(reconciled) : { success: false }) as { success: boolean; data?: string };
const resolvedCode = code.success ? (code.data as typeof validCode) : validCode;
// code_source 溯源:① 上游显式声明(如影像 AI codeSource='image_ai')优先; // code_source 溯源:① 上游显式声明(如影像 AI codeSource='image_ai')优先;
// ② 否则按 stdCode 有无推断(std_code / name_map);③ 无码 → null。通用,非宿主特化。 // ② 否则按 stdCode 有无推断(std_code / name_map);③ 无码 → null。通用,非宿主特化。
...@@ -50,7 +55,7 @@ export class DiagnosisParser implements Parser { ...@@ -50,7 +55,7 @@ export class DiagnosisParser implements Parser {
const hasStdCode = !!String(c.stdCodeRaw ?? '').trim(); const hasStdCode = !!String(c.stdCodeRaw ?? '').trim();
const codeSource: string | null = explicitSource const codeSource: string | null = explicitSource
? explicitSource ? explicitSource
: code : resolvedCode
? hasStdCode ? hasStdCode
? 'std_code' ? 'std_code'
: 'name_map' : 'name_map'
...@@ -70,10 +75,10 @@ export class DiagnosisParser implements Parser { ...@@ -70,10 +75,10 @@ export class DiagnosisParser implements Parser {
status: FactStatus.ACTIVE, status: FactStatus.ACTIVE,
clinicId: ctx.transaction.clinicId, clinicId: ctx.transaction.clinicId,
occurredAt: ctx.transaction.occurredAt, occurredAt: ctx.transaction.occurredAt,
title: `诊断 ${code ?? nameZh ?? '未定码'}${toothPosition ? ` · 牙位 ${toothPosition}` : ''}`, title: `诊断 ${resolvedCode ?? nameZh ?? '未定码'}${toothPosition ? ` · 牙位 ${toothPosition}` : ''}`,
summary: nameZh, summary: nameZh,
content: { content: {
code, code: resolvedCode,
code_source: codeSource, code_source: codeSource,
name_zh: nameZh, name_zh: nameZh,
tooth_position: toothPosition, tooth_position: toothPosition,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
## 逐例核验 ## 逐例核验
| # | 患者 | 一线反馈 → 核验结论 | 状态 | | # | 患者 | 一线反馈 → 核验结论 | 状态 |
| --- | -------------- | --------------------------------------------------------------------------------------------------------------------- | ------ | | --- | -------------- | ------------------------------------------------------------------------------------------------------------------------ | ------ |
| ① | 李然 BA38586 | 47 已种植(1.25)+拆线(2.7)仍被召 → **误召:拆线归 review 不算治疗**;修复 1 后 47 召回消失,剩 16 牙体损伤/17;38 龋(真实待治,该召) | ✅ 已修 | | ① | 李然 BA38586 | 47 已种植(1.25)+拆线(2.7)仍被召 → **误召:拆线归 review 不算治疗**;修复 1 后 47 召回消失,剩 16 牙体损伤/17;38 龋(真实待治,该召) | ✅ 已修 |
| ② | 李姝妤 BJ0U005102 | 外院矫正,4.21 转院 → **回访记录有提到外院**(4.21 常规回访,结果"已在外院矫正中",已入 PAC 回访表/历史联系可见,但召回未消费回访文本);客户动态数据与数仓核对中 | ⏸ 待定 | | ② | 李姝妤 BJ0U005102 | 外院矫正,4.21 转院 → **回访记录有提到外院**(4.21 常规回访,结果"已在外院矫正中",已入 PAC 回访表/历史联系可见,但召回未消费回访文本);客户动态数据与数仓核对中 | ⏸ 待定 |
| ③ | 秦溢泽 BJ0A057103 | 1.27 取资料未回复 → **召回正确**:1.27 EMR"今取正畸资料…约日复诊"+ planned 正畸/充填;2.23 回访"微信约看方案",此后无到诊 → 方案悬置,正是该召的人("取资料"归流程类不算治疗,口径正确) | ✅ 正确召回 | | ③ | 秦溢泽 BJ0A057103 | 1.27 取资料未回复 → **召回正确**:1.27 EMR"今取正畸资料…约日复诊"+ planned 正畸/充填;2.23 回访"微信约看方案",此后无到诊 → 方案悬置,正是该召的人("取资料"归流程类不算治疗,口径正确) | ✅ 正确召回 |
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
| ⑪ | 韩俊和 BJ0U017563 | 26;27,2.3 已种 2.12 戴牙 → **① 同款,修复 1 后种植召回自动消失**(本地仅剩 47 龋·影像AI) | ✅ 随修复1 | | ⑪ | 韩俊和 BJ0U017563 | 26;27,2.3 已种 2.12 戴牙 → **① 同款,修复 1 后种植召回自动消失**(本地仅剩 47 龋·影像AI) | ✅ 随修复1 |
| ⑫ | 刘哲昕 BJ0U017233 | ✓ 认可(正畸) | ⬜ | | ⑫ | 刘哲昕 BJ0U017233 | ✓ 认可(正畸) | ⬜ |
| ⑬ | 王晨 BJ0U016815 | ✓ 动态、回访过未停 → 待核(回访记录与召回并存逻辑) | ⬜ | | ⑬ | 王晨 BJ0U016815 | ✓ 动态、回访过未停 → 待核(回访记录与召回并存逻辑) | ⬜ |
| ⑭ | 关平 BJ0U007377 | 25 萌出异常 ✗(2020.9.18 增平?);外院矫正→完成后全面修复 → 待核 | ⬜ | | ⑭ | 关平 BJ0U007377 | 25 萌出异常 ✗**误召:宿主 ICD 错标** — 25 残根拔除后缺失,宿主把"牙齿缺少"标成 K00(萌出障碍)→ 误召"萌出异常"。修复 5(K00→K08 纠偏)后改判"缺失牙未修复"(正确;患者外院矫正后再修是意向) | ✅ 已修 |
| ⑮ | 李鹏 BJ0U017637 | ✓ 15;27;47 缺失 | ⬜ | | ⑮ | 李鹏 BJ0U017637 | ✓ 15;27;47 缺失 | ⬜ |
| ⑯ | 高美玲 BJ0U016360 | ✗ 36 缺失;EMR 固定桥修复已做 → **④ 同款(固定桥牙位盲点)**,修复 2 后召回消失 | ✅ 随修复2 | | ⑯ | 高美玲 BJ0U016360 | ✗ 36 缺失;EMR 固定桥修复已做 → **④ 同款(固定桥牙位盲点)**,修复 2 后召回消失 | ✅ 随修复2 |
| ⑰ | 陈秀玲 BJ0U017344 | ✗ 27;1.20 已修复(活动义齿) → 待核 | ⬜ | | ⑰ | 陈秀玲 BJ0U017344 | ✗ 27;1.20 已修复(活动义齿) → 待核 | ⬜ |
...@@ -36,3 +36,4 @@ ...@@ -36,3 +36,4 @@
- **修复 3(检查文本无修复指征)**:缺牙"间隙关闭/无修复间隙"在 emr_record.exam_findings 文本里(非诊断字段)→ 召回看不到 → 误召(⑤)。gap 排除加分支:解析 exam_findings JSON,牙位 message 中"缺失/缺牙"与"间隙关闭/无修复间隙"同段共现 → 该牙计入已解决(词典 NO_RESTORATION_GAP_EXAM_PATTERNS 单一源:间隙关闭/已关闭/无修复间隙/间隙不足;共现限定排除正畸"关闭间隙"治疗噪音,全库 104 患者命中)。Layer C 上线后平移给抽取器。 - **修复 3(检查文本无修复指征)**:缺牙"间隙关闭/无修复间隙"在 emr_record.exam_findings 文本里(非诊断字段)→ 召回看不到 → 误召(⑤)。gap 排除加分支:解析 exam_findings JSON,牙位 message 中"缺失/缺牙"与"间隙关闭/无修复间隙"同段共现 → 该牙计入已解决(词典 NO_RESTORATION_GAP_EXAM_PATTERNS 单一源:间隙关闭/已关闭/无修复间隙/间隙不足;共现限定排除正畸"关闭间隙"治疗噪音,全库 104 患者命中)。Layer C 上线后平移给抽取器。
- 21 人本地基线 diff:三修复共修 6 例误召(①47、⑪26;27、④31;41、⑯36、⑤26 收缩、⑥36;46),其余理由零变动、无误杀。 - 21 人本地基线 diff:三修复共修 6 例误召(①47、⑪26;27、④31;41、⑯36、⑤26 收缩、⑥36;46),其余理由零变动、无误杀。
- **修复 4(患者无意愿)**:treatment_record subtype 含"无意愿/不愿/拒绝"(余奕铭⑧:planned"正畸取资料,患者无意愿")→ 患者拒绝该 category,对**本次诊断**排除(类目级,靠时间方向;新诊断更晚→重新可召,非永久非时效)。词典 TREATMENT_REFUSAL_SUBTYPE_PATTERNS;gap 全口分支加 NOT EXISTS、按牙分支加 resolved 拒绝。全库 1225 患者命中。验证:余奕铭正畸/牙周消失,其余 6 正畸+1 牙周召回保留(零误杀)。 - **修复 4(患者无意愿)**:treatment_record subtype 含"无意愿/不愿/拒绝"(余奕铭⑧:planned"正畸取资料,患者无意愿")→ 患者拒绝该 category,对**本次诊断**排除(类目级,靠时间方向;新诊断更晚→重新可召,非永久非时效)。词典 TREATMENT_REFUSAL_SUBTYPE_PATTERNS;gap 全口分支加 NOT EXISTS、按牙分支加 resolved 拒绝。全库 1225 患者命中。验证:余奕铭正畸/牙周消失,其余 6 正畸+1 牙周召回保留(零误杀)。
- **修复 5(宿主 ICD 错标纠偏)**:K00=牙发育/萌出障碍(含先天缺牙),但宿主把后天缺失的"牙齿缺少"也标 K00(全库 1044 例)→ 误召"萌出异常"(关平⑭ 牙25)。diagnosis parser 加 reconcileDiagnosisCode:code=K00 且 name 含缺(牙/失/少)且无"先天"→ K08;先天缺牙/萌出/多生牙等 K00 不动。需 reparse 存量。
...@@ -282,6 +282,17 @@ export const RESTORATION_INELIGIBLE_DX_NAMES = ['废用牙', '无功能牙'] as ...@@ -282,6 +282,17 @@ export const RESTORATION_INELIGIBLE_DX_NAMES = ['废用牙', '无功能牙'] as
/// 「间隙不足」(李强 BJ0U017401 牙36;46:缺牙十年邻牙倾倒,空间不够直接修复 → 需先正畸) /// 「间隙不足」(李强 BJ0U017401 牙36;46:缺牙十年邻牙倾倒,空间不够直接修复 → 需先正畸)
/// 也归此类:对 K08 缺牙召回效果一致(不该推"快去种植修复")。共现"缺牙"护栏已挡掉正畸 /// 也归此类:对 K08 缺牙召回效果一致(不该推"快去种植修复")。共现"缺牙"护栏已挡掉正畸
/// 拥挤语境(全库 844 段「间隙不足」仅 246 段共现缺牙,均为真·缺牙无空间)。 /// 拥挤语境(全库 844 段「间隙不足」仅 246 段共现缺牙,均为真·缺牙无空间)。
/// 宿主 std_code 错标纠偏:K00=牙发育/萌出障碍(含先天缺牙),但宿主常把【后天缺失】的
/// "牙齿缺少/缺失/缺牙"也错标成 K00(全库 1044 例)→ 冒出"萌出异常"误召(关平 BJ0U007377 牙25:
/// 残根拔除后缺失,宿主标 K00 → 误召萌出处置)。区分符=「先天」:有先天 → 真 K00(发育障碍),
/// 无先天的缺牙义 → 后天缺失 = K08。萌出/多生牙等其它 K00 不动。
export function reconcileDiagnosisCode(code: string | null, nameZh: string | null): string | null {
if (code === 'K00' && nameZh && /缺(牙|失|少)/.test(nameZh) && !/先天/.test(nameZh)) {
return 'K08';
}
return code;
}
/// 治疗记录 subtype 里"患者拒绝该类治疗"的说法(余奕铭 BJ0F022277:planned 治疗 /// 治疗记录 subtype 里"患者拒绝该类治疗"的说法(余奕铭 BJ0F022277:planned 治疗
/// "正畸取资料,患者无意愿"/"洁牙,患者无意愿"→ 患者当场拒绝该 category)。 /// "正畸取资料,患者无意愿"/"洁牙,患者无意愿"→ 患者当场拒绝该 category)。
/// 语义=对【本次诊断】排除该治疗类目(同"已启动治疗"对称,靠时间方向;新诊断不受影响)。 /// 语义=对【本次诊断】排除该治疗类目(同"已启动治疗"对称,靠时间方向;新诊断不受影响)。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment