# PAC 三大算法 · 通俗说明

> **谁该读**:产品 / 运营 / 诊所管理者 / 客服主管 —— 不需要懂技术。
> **本文目的**:用大白话讲清楚 PAC 到底在算什么、为什么这么算。
> **读法**:正文是大白话;每个关键论断下面带一个 `📐 证据` 块(简短公式 / 数值 / 判断条件),
> 专业人员看证据即可核对,非技术读者可跳过。完整实现见 `potential-treatment-recall-flow.md`。

---

## 〇、PAC 是干什么的

一句话:**PAC 帮诊所找出"该回来但还没回来"的患者,告诉客服先打给谁、怎么打。**

诊所每天产生大量就诊记录(诊断、治疗、收费、预约……),但里面藏着很多"漏掉的机会":
医生说过"这颗牙该补",患者却一直没来补;种植做完该年度复查,患者忘了（将来实现）……

PAC 的算法连成一条流水线,客服拿到的是排好序、带背景、带话术的电话清单:

```
   全院患者                                                          客服每天的电话
      │                                                                    ▲
      │ ① 召回算法        ② 画像算法       ③ 优先级算法      ⑤ 话术生成      │
      └「谁该回来?」──→「他是谁?」──→「先打给谁?」──→「怎么打?」────┘
        (筛候选)         (贴标签)         (排顺序)         (定制脚本)
                            ▲
                            │ ④ 治疗链合成「治疗走到哪一步?」
                            └ (算出治疗进度,喂②的治疗链标签 + 画可视化进度条)
```

- **① 召回算法**:从几万患者里,筛出"该回来做点什么、但还没做"的人。
- **② 画像算法**:给每个筛出来的患者贴标签(价值高不高、容易流失吗、能不能打扰)。
- **③ 优先级算法**:把这些人排个先后,客服时间有限,先打最该打的。
- **④ 治疗链合成算法**:还原每个患者"治疗走到哪一步",既喂②的"治疗链状态"标签,也单独画成可视化进度条(支撑算法)。
- **⑤ AI 话术生成算法**:按患者具体情况,生成定制通话脚本(怎么开场、怎么应对异议)。

> ⚙️ **关于数值**:本文列出的**权重/分档/阈值都是"出厂默认配置",可按宿主或集团策略调整**
>(不同集团对种植 vs 补牙的重视、对"多久算流失"的口径可能不同)。
> **列出默认值是为了给一个基线对照**——万一线上行为跟预期偏了,先核是不是配置被调过。
> **算法的"形状"(公式 / 流程)是稳定的;"数值"是可调的**,真理源永远在代码/配置,改参后同步更新本文即可。

下面逐个讲。

---

## 一、召回算法 —— "谁该被请回来?"

### 它在找什么

**核心判断只有一句**:
> 医生发现/建议了某个治疗,但患者一直没去做 —— 这个人就该被召回。

举例:
- 医生诊断"36 号牙缺失,建议种植",可患者半年都没来种 → **该召回**
- 医生说"有牙周炎,要做基础治疗",患者没做 → **该召回**
- 医生发现龋齿要补,患者没补 → **该召回**

> 📐 **证据 · 触发条件**(两个信号源,任一命中):
> ```
> 有一条 active 的「诊断记录」或「医生建议」  (diagnosis_record / recommendation_record)
>   且其 code ∈ 本子场景的诊断码/建议码集合
>   且【不存在】诊断之后启动的同类治疗(见第 3 道过滤网)
> ```
> 诊断 confidence=1.0;AI 从文本抽的建议 confidence=0.8(影响优先级,见算法三⑥)。

### 覆盖哪些情况

按牙科疾病大类(ICD-10 的 K00–K09),PAC 覆盖 **10 种**"发现了没去做"的情况。

每种病有**两个触发码**——**诊断码**(医生明确诊断,如 `K08`)或**建议码**(医生/AI 写的建议,如 `IMPLANT_RECOMMENDED`),**任一命中**即触发召回。建议码确实也是码,是诊断之外的第二信号源(置信度低些,见上)。

| 诊断码 | 建议码 | 情况(大白话) |
|---|---|---|
| K08 | `IMPLANT_RECOMMENDED` | 缺失牙未修复(缺了牙没去种/没镶,价值最高 ¥1.5–3 万) |
| K07 | `ORTHO_CONSULT_RECOMMENDED` | 错颌畸形未正畸(牙不齐没去矫正) |
| K04 | `RCT_RECOMMENDED` | 牙髓炎未做根管(牙神经发炎没去做根管) |
| K05 | `SRP_RECOMMENDED` | 牙周炎未做基础治疗(牙周病没去洗治) |
| K09 | `JAW_CYST_REMOVAL_RECOMMENDED` | 颌骨囊肿未处理(没去摘) |
| K02 | `FILLING_RECOMMENDED` | 龋齿未充填(蛀牙没去补) |
| K03 | `HARD_TISSUE_REPAIR_RECOMMENDED` / `CROWN_RECOMMENDED` | 牙体损伤未修复(磨损/缺损没去修) |
| K06 | `GUM_TREATMENT_RECOMMENDED` | 牙龈牙槽问题未处置(没去看) |
| K01 | `EXTRACTION_RECOMMENDED` | 阻生牙未拔除(智齿该拔没拔) |
| K00 | `ERUPTION_INTERVENTION_RECOMMENDED` | 牙发育/萌出异常未处置(儿童换牙/长牙问题) |

**每种病的默认调参值**(下表是**出厂默认**,列出来作基线对照——万一线上行为跟预期偏了,先核是不是配置被调过):

| 诊断码 | 起步分(base) | 冷静期(天)<br/>诊断后多久才召 | 黄金窗末(天)<br/>超过开始衰减 | 紧迫临界(天)<br/>超过加紧迫分 |
|---|---|---|---|---|
| K08 缺失牙 | **60** | 30 | 180 | 120 |
| K07 正畸 | 55 | 30 | 365 | 180 |
| K04 根管 | 52 | 14 | 60 | 45 |
| K05 牙周 | 50 | 30 | 120 | 90 |
| K09 颌骨囊肿 | 50 | 14 | 90 | 60 |
| K02 龋齿 | 45 | 14 | 90 | 60 |
| K03 牙体损伤 | 35 | 14 | 90 | 60 |
| K06 牙龈牙槽 | 35 | 14 | 120 | 60 |
| K01 阻生牙 | 30 | 14 | 180 | 90 |
| K00 发育萌出 | 25 | 30 | 365 | 180 |

> 📐 **证据 & 真理源**:起步分在 `SUB_SCENARIOS[*].base`;冷静期/黄金窗/紧迫临界在
> `DiagnosisTreatmentMap[K??]`(`packages/types/src/canonical-codes.ts`)。
> **这些是默认值,可按宿主/集团策略调**;改参改代码这一处,本表同步更新即可。
> **不漏码**:host 数据出现的 K-code 都进池,起步分低的靠综合分自然沉底。

> 📐 **证据**:每种病的 **起步分**(`SUB_SCENARIOS[*].base`)+ **冷静期 / 黄金窗 / 紧迫临界**
>(`DiagnosisTreatmentMap[K??]`)是**集中可配的默认值**,真理源在代码,改参不改本文。
> **不漏码**:host 数据出现的 K-code 都进池,起步分低的靠综合分自然沉底。

### 三道"过滤网"(避免打错电话)

筛选时层层过滤,确保不打扰不该打的人:

**第 1 道 · 合规红线**(硬规矩,一票否决):
- 已故患者 → 不打
- 标了"勿打扰"的 → 不打
- 没有电话的 → 提示客服补,但不算合规拒绝(数据问题 ≠ 不让打)

> 📐 **证据**:`p.active = true AND pp.do_not_contact = false AND pp.deceased = false`(SQL 三条 WHERE,任一不满足直接踢)。

**第 2 道 · 时间窗**(不能太早,但不嫌晚):
- **太早不打**:医生刚诊断完(比如缺牙 30 天内),患者还在考虑,这时候打像催债。每种病有自己的"冷静期"(见上表)。
- **越晚越该打**:缺牙拖了一年,比拖三个月更该召回。所以**没有"太晚就不打"这一说**——拖得久的不会被踢掉,只是在排队时往后稍微挪(交给优先级算法的时间窗因子衰减)。

> 📐 **证据**:只设下界 `诊断日 <= now − cooldownDays`,**不设上界**。
> (旧版曾有上界 = 黄金窗末,导致缺牙 >180 天被错误踢出池;W3 末已废上界。)

**第 3 道 · 已经做了就别打**(关键):
- 患者**已经做了**对应治疗 → 不召回(已经补过这颗牙了,别再喊人来补)
- 精确到**牙位**判断:36 号牙补过了,但 46 号牙还没补 → 只召回 46 号那件事
- 已经把这颗牙**拔了** → 这件事终结,不召回
- 已经**约了**未来的号 → 人在来的路上了,不打扰

> 📐 **证据**:`NOT EXISTS(诊断之后启动的同类 actual 治疗)`,且:
> ```
> 双方都有牙位 → 牙位集合有交集才算"已做"(36 做了不挡 46)
> 信号无牙位(如牙周全口) → 按类别(category)算
> 治疗无牙位(如全口洁治)  → 视为覆盖该类全部牙位
> 时间方向:治疗.occurred_at >= 诊断.occurred_at(只算诊断后启动的,历史旧治疗不算)
> ```

### 一个聪明的合并

同一个患者,可能同时有好几颗牙、好几个问题。PAC 会**智能合并**:
- 同一类问题、相邻或重叠的牙位 → 合成一条(比如"18、28、38、48 四颗智齿都要拔"合成一件事)
- 不同牙位的不同问题 → 分开列(36 要补 + 全口要做牙周 → 两件事)

这样客服看到的是**整理好的几件事**,而不是一堆零散记录。

> 📐 **证据 · 合并规则**(按牙位重叠做并查集 union-find):
> ```
> 同患者同病种的多条诊断:牙位集合有交集 → 并入同一组
> 每组产出一条理由,标识 = 病种@牙位:
>   caries_no_filling@36          (单颗)
>   impacted_tooth@18;28;38;48    (多颗合并)
>   perio_no_srp@whole            (全口诊断无具体牙位)
> 组内取最早诊断日(最有召回价值);两种信号源都有 → 标"(诊断+医生建议)"
> ```

> **本质**:召回算法不"猜",它只认医生白纸黑字写下的诊断和建议。医生没说的,它绝不编。

---

## 二、画像算法 —— "这个患者是谁?"

筛出候选后,光知道"该召回"还不够,客服打电话前想知道:这人值不值得花时间?会不会已经不想来了?能不能打?

画像算法给每个患者打 **4 个标签**:

### 标签 1 · 患者价值
**这个患者对诊所贡献多大?** 按累计净消费分 5 档。

> 📐 **证据**:`累计净额 = Σ收款 + Σ充值 − Σ退费`,分档:
> ```
> ≥ ¥30,000 → 钻卡 (score 4)    ≥ ¥10,000 → 金卡 (3)
> ≥ ¥3,000  → 银卡 (2)          ≥ ¥500   → 普通付费 (1)
> < ¥500    → 新客/未消费 (0)
> ```
> 这个 score 直接喂优先级算法的"价值加分"(见算法三③)。

### 标签 2 · 流失风险
**这个患者是不是快"跑"了?** 看多久没来 + 治疗链有没有断口。

> 📐 **证据**(距上次临床事件天数 + 链缺口):
> ```
> ≥540 天 且 链有缺口 → high   (score 3)
> ≥360 天 或 链有缺口 → medium (2)
> ≥180 天             → low    (1)
> 否则                 → none   (0)
> ```
> 风险越低 → 越可能接电话愿意来 → 优先级"转化加分"越高(见算法三④,有点反直觉:不是越高风险越优先,而是越可能转化越优先)。

### 标签 3 · 治疗链状态
**这个患者的治疗进行到哪一步了?** 从"发现"到"闭环"分几个状态。

> 📐 **证据**(读独立的诊断/治疗记录判断):
> ```
> 有 actual 治疗在管(active)        → in_progress(进行中)
> 所有诊断都有对应已完成治疗          → closed(已闭环)
> 有诊断但缺对应治疗                  → gap(有缺口,= 召回切入点)
> ```

### 标签 4 · 不打扰状态
**这个患者能不能联系?** 标记勿打扰、已故、投诉等合规状态。

> 📐 **证据**(硬命中任一即标 DNC):
> ```
> profile.do_not_contact = true  或  profile.deceased = true  或  有投诉记录
> ⚠️ phone 缺失【不算】DNC(那是数据问题,只提示客服补,不拦召回)
> ```
> 跟召回算法第 1 道合规红线呼应,双保险。

### 两个特点

1. **全靠规则,不靠 AI 猜**:每个标签怎么算都是写死的明确规则(比如"累计消费 > X 算高价值"),**可解释、可追溯**——主管问"为什么标他高价值",能立刻答出来。
2. **自动更新**:患者有新动作(新就诊、新消费)就自动重算标签,不用人工维护。

> **本质**:画像算法把散乱的就诊记录,浓缩成客服一眼能读懂的几个标签。

---

## 三、召回优先级算法 —— "先打给谁?"

客服一天能打的电话有限。同样是"该召回",500 个候选里先打谁?优先级算法给每个人打一个 **0–100 的分数**,分越高越优先。

### 分数怎么来(6 个因素)

把它想成给每个召回机会打分,像考试加权。**6 个因素 + 它们怎么影响分数的方向**(具体权重值可配,见下注):

| 因素 | 通俗解释 | 方向 | 默认值(可配,作基线对照) |
|---|---|---|---|
| **① 临床基线** | 这种病本身多重要 | 越值钱/越严重 → 起步越高(乘法主项)| 病种 base,见上表(K08=60…K00=25)|
| **② 时间窗因子** | 现在是不是召回最佳时机 | 黄金窗内最高;过早/过晚都打折(乘) | 窗内 ×1.0;过早 0.6→1.0 线性;过晚 1.0→0.4(到 2× 窗末) |
| **③ 患者价值加分** | 高价值患者加分 | 价值档越高 → 加越多(加) | 钻 +20 / 金 +15 / 银 +10 / 普通 +5 / 新客 0 |
| **④ 转化可能加分** | 接电话后真来的可能性 | 越可能转化 → 加越多(加) | `(3−风险档)×2`(0–6)+ 近期回访成功 +4,封顶 +10 |
| **⑤ 临床紧迫加分** | 再拖会出大问题 | 超该病种紧迫临界 → 固定加(加) | 超临界(见上表)→ +5 |
| **⑥ 信号可信因子** | 召回理由多靠谱 | 越可信 → 折扣越接近 1(乘) | 最低 confidence ≥0.9 →×1.0;≥0.7 →×0.9;否则 ×0.75 |

**算法结构**(形状固定,上面"默认值"列可配):
```
raw   = (临床基线 × 时间窗因子 + 价值加分 + 转化加分 + 紧迫加分) × 信号可信因子
score = clamp(round(raw), 0, 100)      ← 取整 + 夹到 0~100,保证不越界
```
- 4 个加分项在括号里**相加**,然后整体乘 ②时间窗 和 ⑥可信度两个**0~1 折扣因子**。
- 所以"再急再值钱,信号不可信(⑥低)也会被打折",避免 AI 误抽的理由冲高分。

> 📐 **真理源**:这些默认值都在 `priority-scorer.ts` 的 `computeXxx()` 函数 + 病种字典,
> **按宿主/集团策略调改这一处即可**;上表列默认值仅作基线对照(算法形状稳定,数值可调)。

### 真实例子:李梦维,91 分(**取自真实召回数据**,用默认权重,换配置则分值随之变)

> 李梦维:46 号牙缺失,128 天前医生诊断,至今未启动种植;钻卡客户(累计 ¥98,220)。
> 下表数字逐项取自该患者 `plan_reasons.breakdown` JSONB(可核)。

| 因素     | 他的情况                        | 贡献         | 对应公式                    |
| ------ | --------------------------- | ---------- | ----------------------- |
| 临床基线   | 缺失牙 K08                     | 60         | base                    |
| 时间窗    | 缺了 128 天 ∈ [30,180] 黄金窗     | ×1.0       | timeWindowFactor        |
| 患者价值   | 钻石卡,累计 ¥98,220(≥¥3 万)      | +20        | valueBonus              |
| 转化可能   | 流失风险=none(距上次 72 天,活跃)→ (3−0)×2 | +6     | likelihoodBonus         |
| 临床紧迫   | 128 > 紧迫临界 120(邻牙倾斜)        | +5         | urgencyBonus            |
| 信号可信度  | 医生明确诊断 confidence=1.0       | ×1.0       | confidenceFactor        |
| **最终** | (60×1.0 + 20 + 6 + 5) × 1.0 | **= 91 分** | clamp(round(raw),0,100) |

### 为什么要"可解释"

每个分数都附带**完整的计算明细**(上面那张表),客服详情页能直接看到。
主管问"为什么他排第一",不是"算法说的",而是**6 个因素一条条摆出来**——这是 PAC 跟黑盒推荐系统的根本区别:**可解释 = 可信任**。

> **本质**:优先级算法把"该召回的一堆人"排成"今天先打这几个"的有序清单,且每个排序理由透明。

---

## 四、治疗链合成算法 —— "这患者的治疗走到哪一步了?"

前面画像的"治疗链状态"标签(进行中/有缺口/已闭环)是**浅层结论**;治疗链合成算法是它**底下的引擎**——把一颗牙(或一组牙)从"发现问题"到"彻底做完"的整条路径还原出来,客服详情页的**治疗链可视化**就是它画的。

### 一条治疗链的 5 个阶段

```
S1 发现     医生诊断出问题(缺牙/龋齿…)            ← 召回算法盯的就是停在这步的
S2 进入     患者同意/挂号/付费,正式启动
S3 执行     真正在做治疗(种植植体、根管…)
S4 复查     术后复查 / 定期检查
S5 闭环     全部做完 + 复查通过 + 没反弹
```

客服看到的 4 种状态:**发现(★机会)/ 进入 / 执行中 / 已闭环**。

### 三个关键设计

**① 按牙位分链**:同一颗牙的所有治疗归一条链,不同牙各自成链(用牙位重叠 union-find,跟召回算法同一套合并逻辑)。

**② 不同治疗"闭环"标准不同**(生命周期模型):

| 生命周期 | 例子 | 怎么才算闭环 |
|---|---|---|
| 一次性 | 补牙、拔牙 | 做完即闭环 |
| 线性多步 | 种植(植体 + 冠) | 几步都做完 |
| 多步 + 冠保护 | 根管(做完还得戴冠,否则牙易裂) | 根管 + 冠都做完 |
| 长周期 | 正畸 | 整个疗程走完 |
| 周期性 | 年度复查类 | 按周期持续 |
| **终身维护** | **牙周病** | **永远不闭环**(只能"在管",牙周需终身维护) |

**③ 闭环条件比"做过"严得多**:不是做过一次治疗就算完,要**全部步骤命中 + 有复查 + 没退费 + 没反弹诊断**才闭环。

> 📐 **证据**(`chain-composer.service.ts` + `canonical-codes.ts`):
> ```
> 阶段:S1 discovered → S2 entered → S3/S4 ongoing → S5 closed
> 分链:同 (chainLabel + 牙位 overlap) union-find,保留 stage 最高的
> 生命周期:TreatmentLifecycles —— one_shot / linear / linear_then_crown /
>           long_term / periodic / lifelong_maintenance(maxStage=4 永不闭环)
> 闭环:全 milestone 命中 + S4 复查 + 无 refund 后置 + 无反弹诊断 + lifecycle.maxStage=5
> 替代闭环:同牙后续有"已治"链 → 当前 discovered/entered 链标记为已闭环(避免重复召回)
> ```
> 各治疗的步骤(milestones)/ 生命周期归属可配,真理源在 `TreatmentMilestones` 字典。

> **本质**:治疗链合成把零散的治疗记录,还原成一条条"从发现到闭环"的路径——既给画像贴"治疗链状态"标签,也给客服画出可视化的进度条。

---

## 五、AI 话术生成算法 —— "电话该怎么打?"

排好了打给谁,客服还需要"开口说什么"。AI 话术生成根据**这个患者的具体情况**,生成一份定制的通话脚本(开场 / 切入话题 / 异议应对 / 结束确认 4 段)。

### 不是套模板,是"搭积木"

PAC 把话术拆成一块块**话术包**(技能 / skill),按患者特征**动态拼装**,再交大模型润色成自然话术:

```
按 4 个维度挑话术包:
  ① 召回场景  —— 新链召回(发现没做)
  ② 诊断病种  —— K00–K09 各有专属话术包(种植怎么聊 ≠ 补牙怎么聊)
  ③ 人群      —— 儿童(对家长说)/ 青少年 / 成人 / 老人(语速慢、复述)
  ④ 新老客    —— 新客(详细自报家门)/ 老客(可家常、引用历史)
  + 异议库 + 安全规则
→ 拼成给大模型的指令 → 大模型生成 4 段话术
```

举例:一个**70 岁老人**的**缺牙**召回,会自动拼上"种植话术包 + 老人沟通包 + (老客/新客)包",生成的话术就会**语速提示慢、主动提家属商量、不催**。

### 三道安全护栏(不让 AI 乱说)

| 护栏 | 防什么 |
|---|---|
| **只认提纲事实** | 只能用系统给的诊断/医生/时间,**不许编**(医生没说的不准说) |
| **禁词 + 不承诺** | 不许"保证治好"、不许报价、不许写死时间(诊所没排班接口,时间只能是"示例") |
| **失败降级** | 大模型出错或违规 → 自动退回安全的模板话术,客服永远有东西可用 |

### 可追溯

每次生成都记录:**用了哪些话术包 + 大模型版本 + 花了多少 token**,出问题能复盘归因。

> 📐 **证据**(`skill-composer.ts` + `skills/*/SKILL.md` + `call.ts`):
> ```
> 话术包 = SKILL.md(Anthropic Skills 标准格式),按 frontmatter.applies 声明式匹配:
>   scenario / diagnosisCodePrefix / ageMin-ageMax / relationship
> 装配 = base 通用铁律 + 匹配到的 skill 正文 → system prompt(同患者命中相同包 → 命中缓存)
> 安全 = call.ts safetyRules(禁词 / close 段无加粗时间 / 无承诺式表述)+ 失败走 fallback 模板
> 落账 = agent_invocations(promptVersion / skills_used / token / cost)
> ```
> 话术包是 MD 文件,**运营可改文案不动代码**;新增病种/人群话术 = 加一个 SKILL.md。

> **本质**:话术生成不是"一个大模板",而是按患者特征搭积木 + 大模型润色 + 安全兜底,让每通电话的话术都贴合这个具体患者。

---

## 六、五者怎么配合(端到端)

用李梦维(真实召回患者,91 分)走一遍完整流程:

```
① 召回算法
   扫全院 → 发现李梦维 128 天前诊断"46 缺失",至今没种植,46 号牙也没拔没约
   → 通过合规红线(在世/可联系)→ 进候选池,理由="缺失牙未启动修复 · 牙位 46"

② 画像算法(+ ④ 治疗链合成在背后)
   给李梦维贴标签:价值=钻石卡(累计 ¥98,220)/ 流失风险=none(距上次 72 天,活跃)/ 可联系
   ④ 治疗链合成算出他 46 号牙这条链停在"S1 发现"(种植已发现未启动)→ 喂"治疗链状态"标签 + 画进度条
   → 客服打电话前就知道"高价值活跃客户 + 这颗牙治疗卡在最开头,值得好好聊"

③ 优先级算法
   综合 6 因素算出 91 分 → 在今天的电话清单里排最前
   → 客服优先打给他,且看得到"为什么是 91 分"(逐项明细)

⑤ AI 话术生成
   客服点"生成话术" → 按"种植(K08) + 老客 + 成人"装配话术包 + 大模型生成
   → 出来一份贴合李梦维的开场白 / 异议应对 / 结束确认(且不编事实、不报价、不写死时间)
```

**一句话总括**:
> **召回**筛出"该回来的人",**画像 + 治疗链**告诉客服"他是谁、治疗走到哪",**优先级**排好"先打谁",**话术生成**定制"怎么打"——五个算法串成一条流水线,把诊所散乱的就诊数据,变成客服每天一份**排好序、带背景、可解释、带脚本**的电话清单。

---

## 附:常见疑问

**Q:算法会不会乱召回、打扰患者?**
A:不会。三道过滤网(合规红线 + 时间冷静期 + 已做就不打)层层把关;且只认医生写下的诊断,不自己编理由。

**Q:为什么有的患者分高有的分低?**
A:6 个因素综合算,每个患者的明细都能在详情页看到,完全透明。

**Q:画像标签准吗?会不会 AI 算错?**
A:画像目前全是明确规则算的(不是 AI 猜),比如"消费多少算高价值"都是写死的,可解释可追溯。

**Q:换一家诊所/集团,算法还能用吗?**
A:能。算法逻辑跟具体诊所无关,新诊所只需要把自己的数据按格式对接进来,算法直接复用。

**Q:治疗链为什么有的"永远不闭环"?**
A:不是 bug。牙周病这类需要终身维护(每 3–6 个月复查),临床上没有"治好了"的终点,所以算法让它最高停在"复查管理中(S4)",永远不标"闭环"——这是临床事实,不是算法缺陷。

**Q:AI 话术会不会瞎编、乱承诺?**
A:有三道护栏:① 只能用系统给的事实(医生没说的不许说);② 禁词 + 不许报价 + 不许写死时间;③ 出错/违规自动退回安全模板。且每段话术都按患者真实情况(病种/年龄/新老客)定制,不是套一个大模板。
