# 分配 · Agent 行为决策树

> 主管发起一批召回分配时，**人 / 模型 / 程序**各自在哪一步做什么。
> 通用原则见 [agent-doctrine.md](./agent-doctrine.md)（引用其编号）；业务教条见 [plan-assignment-doctrine.md](./plan-assignment-doctrine.md)（引用 T 编号）。

| | |
|---|---|
| **状态** | 定稿（Locked） |
| **性质** | Agent 行为规格 —— 实现按此对齐 |
| **⚠️ 立场** | **这是状态图，不是流程脚本。** 描述「有哪些状态、哪些是确定性的」，⛔ 不描述「模型必须按什么顺序调工具」。⛔ 不得据此做工具门禁（B1 / C4） |

---

## 一、状态机

**主管只在两个地方停下来**：看草稿、看已确认的批次。其余全是转移。

```
              ┌──────────────┐
              │   无草稿      │  起点
              └──────┬───────┘
                     │
                     │  [人] 圈定：矩阵点格 / 中间表 / 直接说
                     │  [程] 一次算完（见 §二）→ 生成新草稿
                     │       ⭐ 新草稿一生成，前一版立即作废
                     ▼
    ┌────────────▶ ┌──────────────┐
    │              │  草稿 active  │  ← 系统在等主管
    │              └──┬──┬──┬──┬──┘
    │                 │  │  │  │
    │   改派法 ───────┘  │  │  └──── 放弃 ──▶ 无草稿
    │   同一版重算节点    │  │
    │                    │  └─────── 确认 ──▶ ┌──────────────┐
    │   改人群 ──────────┘                    │   已确认      │
    └───  出新版，旧版 [superseded]           └──────┬───────┘
                                                     │
                                          补福利 / 撤销 / 跟踪
                                          （主管在等客服）
```

### 三个状态

| 状态 | 谁在等谁 | 能做什么 |
|---|---|---|
| **无草稿** | 系统等主管 | 圈定 |
| **草稿 `active`** | 系统等主管 | 处置引导节点 · 改派法 · 改人群 · 确认 · 放弃 |
| **已确认** | 主管等客服 | 补福利 · 撤销（限时）· 跟踪 |

### ⚠️ 「圈定 → 排人 → 算引导节点」是**一次转移**，不是三个状态

服务端一次算完、一次往返。把它拆成三步画会让人以为中间有停顿——**没有**。
（`total == 0` 也不是独立状态：草稿照样生成，只是它是空的，唯一能做的是改人群。）

### 入口的三种来源

| | | 模型要做什么 |
|---|---|---|
| A | 矩阵点一格 | **什么都不用做**，参数确定 |
| B | 中间表确定 | 同上 |
| C | 对话里直接说 | 理解成 治疗项 · 时间档 · 口径；拿不准就反问，⛔ 不猜 |

⚠️ A/B 是主路径。**大多数情况下模型在入口这一步是零参与的**。

---

## 二、圈定这一次转移里，服务端算了什么

按顺序，全部确定性，一次返回：

```
   1. 候选集     口径（治疗项 · 时间档 · 锚点）→ total · 可切维度
                 排序：没被分过的优先

   2. 基数       每人每日 15 条 × 在岗人数 × D  →  N（与候选总数取小）

   3. 落人       专属优先（封顶在目标水位）
                 → 无主补空（给手上最少的）
                 二者共用水位法（= 负载均衡）
                 落不下去的 → 待分配残留

   4. 引导节点   基于 1–3 的结果做确定性判定（§五）
                 待分配残留 = 第一个节点

   5. 两段事实   怎么选的（选谁）/ 怎么派的（给谁）（§四）
```

**产出一个草稿对象**：明细 + 两段事实 + 引导节点。前端直渲，模型只拿事实和节点的摘要。

---

## 三、草稿 active 状态下的四条出路

```
   草稿 active
        │
        ├── 不动 ─────────────────────────────▶ 确认
        │      每个节点默认都是 no-op，所以这条路**永远安全**
        │
        ├── 点节点上的选项 ── [程] 执行 intent ──▶ 同一版，重算节点
        │
        ├── 说节点外的话 ──── [模] 判断动的是哪一层
        │      │
        │      ├─ 动「怎么派」 → [程] 局部改 ────▶ 同一版，重算节点
        │      │    谁给谁 · 时效 · 移出本批 · 福利
        │      │
        │      └─ 动「这批人」 → [程] 重跑 ──────▶ **出新版，旧版当场作废**
        │           换口径 · 加画像条件 · 改人数 · 换治疗项
        │
        └── 放弃 ──────────────────────────────▶ 无草稿
```

⚠️ 前三条都回到 `active`，只是**回到哪一版**不同：改派法回同一版，改人群回新版。

### 「派法」还是「人群」—— 模型在本流程中最实质的一次判断

| | 动的是 | 走 | 例 |
|---|---|---|---|
| **派法** | 这批人**怎么分** | 局部改，不重跑 | 「王强移出这批」「待分配各自归专属」「给 5 天」「带个福利」 |
| **人群** | 这批人**是谁** | 重跑，出新版 | 「只要商保直付的」「改成 200 人」「换成 1–2 年那档」「按末诊算」 |

**代价不对称**：把「人群」误判成「派法」→ 主管以为条件生效了、其实没有（**静默错**）；反过来只是多算一次。
⇒ **拿不准时一律走重跑。**

### ⛔ 「怎么改这张单」不由模型讲

改单的操作方式（拖拽、点选、可改哪几项）写在**卡片的操作说明**里常驻。
模型只在主管开口之后**翻译成 intent**，⛔ 不主动教学、⛔ 不每轮复述（G3）。

---

## 四、事实两段 + 引导节点

草稿的可读产出只有两类：**已经确定的事实**（两段）和**还没确定的引导节点**（§五）。

> ⚠️ **不要写成「三段事实」** —— 第三段「还没定的」本来就是引导节点，列成事实等于把同一份数据说两遍。
> 同理，**落人不是四段**：专属优先与无主补空是两段，水位法是二者共用的**机制**，待分配是落不下去的**残留**（= `pending` 节点）。

服务端产出**结构化事实**（⛔ 不是成品句子，G6），模型据此组织语言。

**呈现顺序锁定为：怎么选的 → 怎么派的 → 引导节点**（即「**已排好的在前，要他定的在后**」，2026-08-06 产品定）。
理由：主管要先知道**这批本身是什么样**，再看还剩什么要他处理。
⚠️ `assistant-prompts.ts` 第 0.5 条里那句「要他动手的那块永远排第一」与此冲突，**以本文为准**，实现时一并改掉。

### 事实① 怎么选的（**选谁**）

- 口径：治疗项 · 时间档 · 锚点模式（诊断 / 末诊）
- 排序：没被分过的优先
- 候选总数 → 取本批 N

| | |
|---|---|
| 每人每日 | **15 条**（当前默认；⛔ 暂不沿用上一次的值） |
| 时效 | `D` 天 |
| **本批目标 N** | **在岗人数 × 15 × D**，并与候选总数取小 |

### 事实② 怎么派的（**给谁**）

**两段落人**，按顺序：

| | 段 | 规则 |
|---|---|---|
| ① | **专属优先** | 有专属且在名册内 → 分给他，**封顶在目标水位** |
| ② | **无主补空** | 无专属 / 专属已离岗 → 给当前手上最少的人 |

**共用机制 · 水位法**：每一条都给当前手上最少的那个，⛔ 不是「每人加一样多」——起点不齐时那样终点还是不齐。
**派生**：每人本批多少条 · **折合每日多少条**（供 `daily_overload` 判定）。

> ⚠️ 「负载均衡」不是独立一段，就是这个水位法。它决定的是**给谁**，⛔ 不影响**选谁**。
> （目标水位封顶会把一部分专属患者挤进待分配、从而减少实际发出量——那是分配结果回头改变了发出量，候选集本身没变。）

### 落不下去的残留 → 引导节点

专属客服本轮已排满的那些人 **不分**，单列成 `pending` 引导节点交主管定
（T：关系层面的决定只有主管能做）。
⚠️ 有待分配就**必须说出来**，⛔ 不许省、不许弱化成「另有若干」。

⚠️ **但「还没定的」不只是待分配** —— 它是所有未处置的引导节点（另有每日超阈值、候选不足、画像亮点）。待分配只是 severity 最高的那一个。

---

## 五、引导节点表

引导节点的通用设计（数据形状、五条规则、三方各取所需）见 [agent-architecture.md §12](./agent-architecture.md)。

### ⛔ 不截断 —— 命中的全部呈现

**凡是有实际后果的节点，一条都不能省。** 藏起任何一条，主管就不知道有东西卡着 —— 那正是待分配当初要解决的问题。

防噪音靠**分层**，不靠丢弃：

| 层 | 内容 | 呈现 |
|---|---|---|
| **需处置** | 不管就会有后果 | 全部展开，按 severity 排序 |
| **提示** | 纯信息，不管也没事 | 默认折叠成一行，可展开 |

### 确认前

| sev | 层 | key | 判定（程序） | 默认（no-op） | 选项（点击执行） |
|---|---|---|---|---|---|
| **1** | 需处置 | `pending` | `pending > 0` | 留着 = 这批不发给他们 | ① 各自归专属客服<br>② 铺平给在岗<br>③ **换无主患者补上**（见下）<br>④ 移出本批 |
| **2** | 需处置 | `daily_overload` | 分完后最重的那位 `手上总条数 > 15 × D` | 就按 D 天发，到期落回池子 | 延长时效 / 改每人每天几通 / 减少本批人数 |
| **2** | 需处置 | `short_supply` | 候选数 < N | 全给 | 换口径 / 放宽一档 |
| **3** | 需处置 | `highlight` | 画像维度偏离基线 ≥ 2×，取 Top 2–3 | 不特殊处理 | 见下 |
| **4** | 提示 | `expiry_default` | D 是默认值 | 用默认 | 改天数 |
| **4** | 提示 | `anchor_nondefault` | 口径非默认 | 保持 | 换口径重出 |

**终止分支（不是引导节点）**：`empty` —— `total == 0`，无法继续，只能放宽或换格。

#### `daily_overload` 是**工作量预估**，不是异常告警

N 按「在岗 × 每天 15 通 × 时效」算，**只算新增、⛔ 不扣在手** —— 所以只要谁手上还有东西，
需要的天数就会超过时效，这个节点**每批基本都会亮**。**这是对的**（产品定 2026-08-12）：
每一批本来就该把「这活多大、要打几天」讲清楚，主管据此决定延时效、调每日通数、还是减量。

由此推出两条硬要求：

1. **措辞不许像报错。** ⛔ 不出现「打不完 / 超了 / 过载」——它常亮，说成故障主管就会
   开始怀疑系统而不是做决定。
2. **数字必须拆开**：「手上共 60 条」要写成「本批 45 + 原本在手 15」。
   不拆开主管会读成"这批一下压了 60 条给他"（增量与总量分不开是同类问题里最常犯的）。

> ⚠️ 曾考虑把 N 改成 `Σ max(0, 15D − 在手)`（扣掉在手）让这个节点不再常亮。
> ⛔ **不要这么做** —— 那会踩回 2026-08-03 删掉的坑：第一批把所有人填满后，第二批恒为 0。

#### `pending` 的第 ③ 个选项：换无主患者补上

待分配的人不发出去，这批**实发量就少了**。从池子里取**等量的无主患者**补进来：
既不动任何专属关系（T：不许替主管把患者从专属客服手里挪走），又把产能填满。

#### `highlight` 展开

判定统一为「本批占比 ÷ 基线 ≥ 2」；**基线取同治疗项的池子**，⛔ 不取全池
（种植的重要价值天然高于补牙，用全池会让种植永远命中）。

| 亮点 | 选项 |
|---|---|
| 重要价值 | 只做这些 / 优先给专属 |
| 转介绍 | 话术带上介绍人 |
| 权益身份 | 权益到期未用当由头 |
| 折扣锚点 | 福利定价参考上次折扣 |
| 获客渠道集中 | 统一话术口径 |

### 交互形态：点击为主，自由输入兜底

**每个选项都是一个可直接点击执行的动作**，⛔ 不是让主管照着打字。

```
   ┌──────────────────────────────────────────────────┐
   │  34 人的专属客服这轮排满了                        │
   │  不处理 = 这批不发给他们                          │
   │  [各自归专属] [铺平] [换无主患者补上] [移出本批]   │
   └──────────────────────────────────────────────────┘
             ⋮
   也可以直接告诉我要怎么做。          ← 兜底：一句文字说明
```

**为什么点击优先**：主管打字表达 → 模型理解 → 翻译成 intent，这条链每一环都可能错（实测栽过 `owner`/`balance` 选反，18 人被散给 17 位别人）。点击是**确定性**的，intent 直接给出，没有理解环节。

**为什么仍要留自由输入**：引导节点覆盖不了的需求必须有出口（B1 / F8），否则助手只能回「做不到」。

⭐ **两条路收敛到同一个 `intent` 契约** —— 这样「按钮能做的、说话也能做」是结构保证，⛔ 不是提示词里的一句叮嘱。

### 确认后

| key | 判定 | 默认 | 选项 |
|---|---|---|---|
| `no_benefit` | 已确认 且 `benefit == null` | 不带 | 带一个（填原文） |

⚠️ **福利引导刻意放在确认之后**：它只影响**此后生成**的话术，所以不设时限；
而客服从确认那一刻起陆续打开这批单，**每过一会儿能用上的人就少一个** —— 这是提醒的最佳时机。
⛔ **只提一次**，主管说不用就不再提。
⛔ **绝不许说「带福利成功率更高 / 转化率提升」** —— 本系统不统计成功，那个结论编不出来（A2）。

---

## 六、确认之后

```
   已确认
        ├─ 福利引导   见上（唯一还能改的东西）
        ├─ 撤销说明   限时；⚠️ 客服**已经打开过**的单不会被收回
        │             ⛔「已撤销」四个字只能出现在撤销工具真的返回之后（A2）
        └─ 跟踪说明   → 另一条时间线，解读规范随工具返回值下发（F10）
```

**能改与不能改**：

| | 确认后 |
|---|---|
| 福利 | ✅ 可补挂，不设时限 |
| 人员 | ❌ 单已在客服手上 → 走撤销重分 |
| 时效 | ❌ 同上 |

---

## 七、草稿的生命周期

```
   出第 1 版 ──▶ [active]
        │
   出第 2 版 ──▶ 第 1 版立即 [superseded]   ⭐ 作废发生在**新版生成时**
        │                                     ⛔ 不是在确认时
   出第 3 版 ──▶ 第 2 版立即 [superseded]
        │
   点确认   ──▶ 第 3 版 [confirmed] → 转成批次
```

**规则**

1. **任一时刻只有一版 `active`。** `edit` 一律作用于它，⛔ 不用倒着扫消息找「最后一张」。
2. **作废时机 = 新草稿生成时**，不是确认时。
3. **作废的卡片必须在页面上明确标注**：显示「已作废」，**确认按钮禁用**。
   ⛔ 不许静默变灰、不许直接消失 —— 主管要能看见自己出过几版、以及哪一版才是当前的。
4. 刷新页面丢草稿是可接受的（重新出一版即可），**暂不落库**。

---

## 八、模型的自主权边界

| ✅ 模型决定 | ⛔ 模型不许决定 |
|---|---|
| 理解自由输入（入口 C、节点外的话） | 哪个引导节点命中 |
| 要不要先查分布再圈 | 任何数字（A2） |
| 判断动的是「派法」还是「人群」 | 谁分给谁 |
| 组织语言（G6） | 引导节点的默认值 |
| 拿不准时反问（B2） | 是否落库（A3） |

---

## 九、不在本树内

| | 去哪 |
|---|---|
| **跟踪线** | 同会话，但解读规范随工具返回值加载（F10）；节奏与分配完全不同 |
| **客服线** | 另一个身份 → 另一套工具清单与数据范围（D1–D4） |
| **患者个案查询 / 闲聊** | 自由通道，不进本树 |

---

## 十、随时可答：「下一步做什么」

主管在任何一步问「接下来呢 / 我现在该干嘛」，**答案是确定性的**，⛔ 不由模型即兴发挥：

```
   下一步 = 当前状态 + 未处置的引导节点 + 默认路径
```

| 当前状态 | 下一步 |
|---|---|
| 还没圈定 | 在矩阵上点一格，或直接说要哪一类人 |
| 候选为 0 | 放宽一档 / 换治疗项 / 不限时间档 |
| 草稿 `active`，有需处置的节点 | 逐条列出未处置的节点 + 各自的默认；并说明「都不动也可以直接确认」 |
| 草稿 `active`，无需处置的节点 | 直接确认 |
| 草稿 `superseded` | 提示这版已作废，当前有效的是最新那版 |
| 已确认，无福利 | 可补挂福利；否则等客服处理，随时可跟踪 |
| 已确认，有福利 | 等客服处理；可跟踪；限时内可撤销 |

**这是 G1 的直接应用**：候选由程序算（状态机 + 引导节点是确定性的），措辞由模型给，决定权在人。
⚠️ 由于它完全由状态推出，**「下一步」永远不会指向系统做不到的事**（G2）。
