Commit 9cd3cd5e by luoqi

docs: 文档口径跟上「时限」—— 71 处; _archive 保持原样

代码/界面已统一到「时限」(6a1bee6f),文档站与设计文档还写着「时效」——
不跟上就等于刚消掉的漂移又从另一头长回来。

改 6 个文件 71 处:站点两篇(assignment-agent / batch-assignment)+
设计文档四篇(doctrine / agent-flow / 两份 dev-plan)。

 `docs/_archive/` 11 处不动 —— 那是历史记录,改了等于篡改当时的原文。

验证:pac-docs build 通过(OG 图字体拉取失败是内网够不到 Google Fonts 的老问题,非致命)。
parent 6a1bee6f
...@@ -22,10 +22,10 @@ flowchart TD ...@@ -22,10 +22,10 @@ flowchart TD
A1["① 选人 · 主管初选<br/>1 做哪一类还没启动的治疗<br/>2 找多久没来的人<br/>—— 凭运营经验,他张口就来"] --> A2 A1["① 选人 · 主管初选<br/>1 做哪一类还没启动的治疗<br/>2 找多久没来的人<br/>—— 凭运营经验,他张口就来"] --> A2
A2["① 选人 · 助手精选<br/>3 要不要只挑高价值的 —— 先摆这批人的全景<br/>4 团队还吃得下多少 —— 先摆在岗人手与在手量<br/>—— · —— · —— · —— · —— · ——<br/>不等他给全,先按这几条默认出一版:<br/>没联系过的排前面,其余按优先级<br/>每人每天 15 通 · 时效 1 天<br/>这批多大 = 在岗人数 × 15 × 时效"] A2["① 选人 · 助手精选<br/>3 要不要只挑高价值的 —— 先摆这批人的全景<br/>4 团队还吃得下多少 —— 先摆在岗人手与在手量<br/>—— · —— · —— · —— · —— · ——<br/>不等他给全,先按这几条默认出一版:<br/>没联系过的排前面,其余按优先级<br/>每人每天 15 通 · 时限 1 天<br/>这批多大 = 在岗人数 × 15 × 时限"]
A2 --> D1{"这批人对吗?"} A2 --> D1{"这批人对吗?"}
D1 --> G1["助手摆出的引导 —— 满足条件才出现<br/>「能加个条件」:候选 ≥50 人 · 他还没加过 · 切完还剩 ≥10 人<br/>  消费高于本批平均 / 转介绍达人 / 权益身份 / 获客渠道<br/>「这批多大」:人数是系统估的(他没自己指定过)<br/>  可改 每人每天几通 · 时几天"] D1 --> G1["助手摆出的引导 —— 满足条件才出现<br/>「能加个条件」:候选 ≥50 人 · 他还没加过 · 切完还剩 ≥10 人<br/>  消费高于本批平均 / 转介绍达人 / 权益身份 / 获客渠道<br/>「这批多大」:人数是系统估的(他没自己指定过)<br/>  可改 每人每天几通 · 时几天"]
G1 --> R1["重新选一版<br/>⚠️ 已经排好的分法一并作废"] G1 --> R1["重新选一版<br/>⚠️ 已经排好的分法一并作废"]
R1 --> A2 R1 --> A2
...@@ -34,7 +34,7 @@ flowchart TD ...@@ -34,7 +34,7 @@ flowchart TD
B["② 分人 —— 谁去打<br/>先算目标水位 =(团队在手 + 本批人数)÷ 在岗人数<br/>一趟:有专属的回自己人,到水位为止<br/>二趟:无专属的补给当前手上最少的<br/>三趟:专属这轮排满的单列成一组,交他定"] B["② 分人 —— 谁去打<br/>先算目标水位 =(团队在手 + 本批人数)÷ 在岗人数<br/>一趟:有专属的回自己人,到水位为止<br/>二趟:无专属的补给当前手上最少的<br/>三趟:专属这轮排满的单列成一组,交他定"]
B --> D2{"这么分行吗?"} B --> D2{"这么分行吗?"}
D2 --> G2["助手摆出的引导 —— 满足条件才出现<br/>「专属排满了」:第三趟真有人排不进去<br/>  换无专属的补上(池子还有人时才给)<br/>  铺平给在岗 / 各自归专属 / 移出本批<br/>「最忙的那位」:分完后的量 > 每天通数 × 时效<br/>  报最忙那位 + 另有几位也超<br/>  整批时效改成 N 天(一个算好的数)"] D2 --> G2["助手摆出的引导 —— 满足条件才出现<br/>「专属排满了」:第三趟真有人排不进去<br/>  换无专属的补上(池子还有人时才给)<br/>  铺平给在岗 / 各自归专属 / 移出本批<br/>「最忙的那位」:分完后的量 > 每天通数 × 时限<br/>  报最忙那位 + 另有几位也超<br/>  整批时限改成 N 天(一个算好的数)"]
G2 --> R2["不重新选人<br/>人不变,只动这一版的分法"] G2 --> R2["不重新选人<br/>人不变,只动这一版的分法"]
R2 --> D2 R2 --> D2
...@@ -65,7 +65,7 @@ flowchart TD ...@@ -65,7 +65,7 @@ flowchart TD
所以顺序不能颠倒:**人先定下来,才谈怎么分。** 先排分法再让他改人群,前面那趟排班就白做了 —— 助手讲这一版时也按这个顺序讲,理由一样。 所以顺序不能颠倒:**人先定下来,才谈怎么分。** 先排分法再让他改人群,前面那趟排班就白做了 —— 助手讲这一版时也按这个顺序讲,理由一样。
三档里最容易看走眼的是中间那条:**「换无专属的补上」摆在分人环里,换的却是人。**「改时」也有两条路 —— 从「这批多大」改会连人数一起重估(重出),从「最忙的那位」改只延长期限、人不变(就地改)。 三档里最容易看走眼的是中间那条:**「换无专属的补上」摆在分人环里,换的却是人。**「改时」也有两条路 —— 从「这批多大」改会连人数一起重估(重出),从「最忙的那位」改只延长期限、人不变(就地改)。
<Callout type="warn">**代价不对称,判断也要跟着不对称**:把「改人群」误判成「改分法」→ 主管以为条件生效了、其实没有(**看不出来的错**);反过来只是多算一次。⇒ **拿不准时一律走重出。**</Callout> <Callout type="warn">**代价不对称,判断也要跟着不对称**:把「改人群」误判成「改分法」→ 主管以为条件生效了、其实没有(**看不出来的错**);反过来只是多算一次。⇒ **拿不准时一律走重出。**</Callout>
...@@ -110,13 +110,13 @@ flowchart TD ...@@ -110,13 +110,13 @@ flowchart TD
| **谁排前面** | 没联系过的排前面,其余按优先级从高到低 | 「先打消费高的」 | | **谁排前面** | 没联系过的排前面,其余按优先级从高到低 | 「先打消费高的」 |
| **每人每天打几通** | 15 通 | 「按 20 通算」 | | **每人每天打几通** | 15 通 | 「按 20 通算」 |
| **多久要打完** | 1 天 | 「给 5 天」 | | **多久要打完** | 1 天 | 「给 5 天」 |
| **这批发多大** | 在岗人数 × 每天通数 × 时 | 「改成 200 人」 | | **这批发多大** | 在岗人数 × 每天通数 × 时 | 「改成 200 人」 |
<Callout type="warn">**默认值一个都不许藏。** 出方案时式子摊开写:「本批 405 人 = 在岗 27 人 × 每天 15 通 × 1 天」。 <Callout type="warn">**默认值一个都不许藏。** 出方案时式子摊开写:「本批 405 人 = 在岗 27 人 × 每天 15 通 × 1 天」。
一个他看不见的默认值,等于系统替他做了一个他不知道的决定 —— 而这批人是真发下去了。 一个他看不见的默认值,等于系统替他做了一个他不知道的决定 —— 而这批人是真发下去了。
⇒ 摆出来他才有得改;不摆,他连「原来还能改这个」都不知道。</Callout> ⇒ 摆出来他才有得改;不摆,他连「原来还能改这个」都不知道。</Callout>
人手在**两个地方**主动摆给他看,不用他去找:出方案时是上面那个式子;确认单上是每位客服一行「约 3 天」,算的是**分完之后他手上的总量**(在手 + 本批),不只是本批那几条。超出时的用**颜色**提示,⛔ 不写「打不完 / 超了 / 过载」—— 几乎每批都会有人超,说成故障主管就会开始怀疑系统,而不是做他该做的判断。 人手在**两个地方**主动摆给他看,不用他去找:出方案时是上面那个式子;确认单上是每位客服一行「约 3 天」,算的是**分完之后他手上的总量**(在手 + 本批),不只是本批那几条。超出时的用**颜色**提示,⛔ 不写「打不完 / 超了 / 过载」—— 几乎每批都会有人超,说成故障主管就会开始怀疑系统,而不是做他该做的判断。
### 能加哪几个条件 ### 能加哪几个条件
...@@ -299,7 +299,7 @@ flowchart TD ...@@ -299,7 +299,7 @@ flowchart TD
| | 看这批人构成 | 这批人各画像维度各多少人 | | | 看这批人构成 | 这批人各画像维度各多少人 |
| **出方案** | 出一版方案 | 选人 + 排客服 + 算引导,一次算完 | | **出方案** | 出一版方案 | 选人 + 排客服 + 算引导,一次算完 |
| | 看当前确认单 | 那张单**现在**什么样(他动过手之后助手看不见) | | | 看当前确认单 | 那张单**现在**什么样(他动过手之后助手看不见) |
| | 改确认单 | 移出谁 · 改派 · 改时 · 设福利 | | | 改确认单 | 移出谁 · 改派 · 改时 · 设福利 |
| | 摆确认单 / 摆引导 | 决定它们落在正文哪一句之后(两个工具) | | | 摆确认单 / 摆引导 | 决定它们落在正文哪一句之后(两个工具) |
| **追踪** | 分过哪几批 | 批次列表 + 每批汇总 | | **追踪** | 分过哪几批 | 批次列表 + 每批汇总 |
| | 某批怎么样了 | 进度 · 按客服拆 · 退回原因 · 通话成效 · 逐条纪要 | | | 某批怎么样了 | 进度 · 按客服拆 · 退回原因 · 通话成效 · 逐条纪要 |
...@@ -343,11 +343,11 @@ flowchart LR ...@@ -343,11 +343,11 @@ flowchart LR
| **专属排满了** | 「182 人的专属客服这轮已排满」——**第三趟那一组**,四个选择:换无专属的补上(池子还有人时才给)/ 铺平给在岗 / 各自归专属客服 / 移出本批 | 这批不发给他们 | | **专属排满了** | 「182 人的专属客服这轮已排满」——**第三趟那一组**,四个选择:换无专属的补上(池子还有人时才给)/ 铺平给在岗 / 各自归专属客服 / 移出本批 | 这批不发给他们 |
| **能加个条件** | 「这批候选 2,663 人,也可以只选其中一类」+ 四个带人数的选项(见 §1) | 就按这批候选全部人来 | | **能加个条件** | 「这批候选 2,663 人,也可以只选其中一类」+ 四个带人数的选项(见 §1) | 就按这批候选全部人来 |
| **这批多大** | 「本批 405 人 = 在岗 27 人 × 每天 15 通 × 1 天」,式子摊开给他看 | 就按这个数发 | | **这批多大** | 「本批 405 人 = 在岗 27 人 × 每天 15 通 × 1 天」,式子摊开给他看 | 就按这个数发 |
| **最忙的那位** | 「这批发下去,最忙的是王强:手上共 45 条,约 3 天的量;**另有 2 位也超过 1 天**」 | 就按这个时发,到期没打完的自动回池 | | **最忙的那位** | 「这批发下去,最忙的是王强:手上共 45 条,约 3 天的量;**另有 2 位也超过 1 天**」 | 就按这个时发,到期没打完的自动回池 |
⚠️ **为什么要报「另有几位」**:只说最忙的一个,主管分不出两种局面 —— 而这两种局面该做的事**正好相反**:只有王强超,就给他少分点、改派几个;全队都超,就得减少这批、延长时。只有他一个人超时那半句不出现,⛔ 不制造无谓噪音;也⛔ 不在这里铺开每个人 —— 确认单上每位客服那一行已经写着「约 N 天」,引导只负责**点出要他定的事**,不负责展示数据。 ⚠️ **为什么要报「另有几位」**:只说最忙的一个,主管分不出两种局面 —— 而这两种局面该做的事**正好相反**:只有王强超,就给他少分点、改派几个;全队都超,就得减少这批、延长时。只有他一个人超时那半句不出现,⛔ 不制造无谓噪音;也⛔ 不在这里铺开每个人 —— 确认单上每位客服那一行已经写着「约 N 天」,引导只负责**点出要他定的事**,不负责展示数据。
⚠️ **「最忙的那位」只给一个选项,而且必须带数**:「整批时改成 **3** 天」—— 那个 3 是按最忙那位的量算出来的。曾经还有「改每人每天打几通」「减少本批人数」两个,删掉了:它们**一个数都不带**,点下去等于替主管说了句「减少一些」—— **一个不带数的按钮,严格弱于他自己开口说一句。** ⚠️ **「最忙的那位」只给一个选项,而且必须带数**:「整批时改成 **3** 天」—— 那个 3 是按最忙那位的量算出来的。曾经还有「改每人每天打几通」「减少本批人数」两个,删掉了:它们**一个数都不带**,点下去等于替主管说了句「减少一些」—— **一个不带数的按钮,严格弱于他自己开口说一句。**
每条按钮都带三样:**是什么**、**为什么**、**不处理等于什么**。三条设计原则: 每条按钮都带三样:**是什么**、**为什么**、**不处理等于什么**。三条设计原则:
......
...@@ -136,7 +136,7 @@ flowchart LR ...@@ -136,7 +136,7 @@ flowchart LR
| 基数 | 含义 | 首次 | 之后 | | 基数 | 含义 | 首次 | 之后 |
|---|---|---|---| |---|---|---|---|
| **本批人数** | 这一轮推多少人 | 在岗人数 × 20 | 沿用上次 | | **本批人数** | 这一轮推多少人 | 在岗人数 × 20 | 沿用上次 |
| **时** | 多久没动自动回池 | 3 天 | 沿用上次 | | **时** | 多久没动自动回池 | 3 天 | 沿用上次 |
> 没有第三个数。曾经有过"每人容量",删掉了 —— 一个数当两个用,第二批必然分不出来。 > 没有第三个数。曾经有过"每人容量",删掉了 —— 一个数当两个用,第二批必然分不出来。
...@@ -179,12 +179,12 @@ flowchart TB ...@@ -179,12 +179,12 @@ flowchart TB
| 卡片上直接做 | 回对话让助手做 | | 卡片上直接做 | 回对话让助手做 |
|---|---| |---|---|
| 改批次时效 · 改单条时效 | 换人群(换治疗项 / 时机 / 画像条件) | | 改批次时限 · 改单条时限 | 换人群(换治疗项 / 时机 / 画像条件) |
| 删掉某一条 | 改本批人数 | | 删掉某一条 | 改本批人数 |
| 把某一条拖给别的客服 | 给某个客服设本批名额 | | 把某一条拖给别的客服 | 给某个客服设本批名额 |
| 移除某个客服 | 设本批福利 | | 移除某个客服 | 设本批福利 |
效的文案是「**N 天后自动退回**」而不是光一个"时效" —— 主管要知道到期会发生什么,否则这个数对他没有意义。 限的文案是「**N 天后自动退回**」而不是光一个"时限" —— 主管要知道到期会发生什么,否则这个数对他没有意义。
### 福利挂在批次上,不挂在个人上 ### 福利挂在批次上,不挂在个人上
...@@ -198,7 +198,7 @@ flowchart TB ...@@ -198,7 +198,7 @@ flowchart TB
- 首次无历史 → 用默认值,**并标明这是默认值** - 首次无历史 → 用默认值,**并标明这是默认值**
- 有数据之后 → 反推真实习惯,替换默认值 - 有数据之后 → 反推真实习惯,替换默认值
> 时"3 天"现在只能写「默认值,暂无历史数据」,**不能**写成「依据平均结案 2.4 天」—— 那个数还算不出来。 > 时"3 天"现在只能写「默认值,暂无历史数据」,**不能**写成「依据平均结案 2.4 天」—— 那个数还算不出来。
--- ---
...@@ -225,7 +225,7 @@ stateDiagram-v2 ...@@ -225,7 +225,7 @@ stateDiagram-v2
### 退回必须写原因 ### 退回必须写原因
退回是**正常路径**不是异常。原因分布是主管调整下一批的输入 —— 退回是**正常路径**不是异常。原因分布是主管调整下一批的输入 ——
「派多了」「时太紧」「压根不该派给他」,这三种的下一步动作完全不同。 「派多了」「时太紧」「压根不该派给他」,这三种的下一步动作完全不同。
### 撤销 ≠ 退回 ### 撤销 ≠ 退回
...@@ -248,7 +248,7 @@ stateDiagram-v2 ...@@ -248,7 +248,7 @@ stateDiagram-v2
|---|---| |---|---|
| 分了多少人 / 在手 / 已处理 | 「已处理」= 这单动过了,**不等于谈成了** | | 分了多少人 / 在手 / 已处理 | 「已处理」= 这单动过了,**不等于谈成了** |
| 客服主动退回 · 原因分布 | 「不该我做」,是**分配**问题 | | 客服主动退回 · 原因分布 | 「不该我做」,是**分配**问题 |
| 到期没人动 | **没处置**,是派多了 / 时太紧 / 人不在岗 | | 到期没人动 | **没处置**,是派多了 / 时太紧 / 人不在岗 |
| 通话结果:成功 / 不成功及原因 | 打了之后的结果,是**召回效果**问题 | | 通话结果:成功 / 不成功及原因 | 打了之后的结果,是**召回效果**问题 |
| 一次结果都没有 | 最该先看的数 —— 不是效果差,是**根本没做** | | 一次结果都没有 | 最该先看的数 —— 不是效果差,是**根本没做** |
......
...@@ -262,7 +262,7 @@ components/assistant/ ...@@ -262,7 +262,7 @@ components/assistant/
|---|---|---| |---|---|---|
| 27 | 提示词按「什么会让它变」**分五层**(装置 / 诚实 / 语气 / 角色 / 现场),现场段按 `选人 → 分人 → 他确认 → 确认之后` **四步分节** | `assistant-prompts.ts` | | 27 | 提示词按「什么会让它变」**分五层**(装置 / 诚实 / 语气 / 角色 / 现场),现场段按 `选人 → 分人 → 他确认 → 确认之后` **四步分节** | `assistant-prompts.ts` |
| 28 | 次要业务线走 **pull**:系统提示词只留索引,模型用 `open_playbook` 按需取正文 | **新建** `assistant-playbooks.ts` | | 28 | 次要业务线走 **pull**:系统提示词只留索引,模型用 `open_playbook` 按需取正文 | **新建** `assistant-playbooks.ts` |
| 29 | **`rosterCount`** —— 「在岗 N 人 × 每天几通 × 时」里的 N 由算 `batchSize`**同一行**赋值 | `packages/types` + proposal | | 29 | **`rosterCount`** —— 「在岗 N 人 × 每天几通 × 时」里的 N 由算 `batchSize`**同一行**赋值 | `packages/types` + proposal |
| 30 | **`SheetSnapshot` + `get_current_sheet`(拉)+ 回声带当前值(推)** —— 草稿态服务端查不到,模型此前只能把对话里几行回声自己累加 | 三端 | | 30 | **`SheetSnapshot` + `get_current_sheet`(拉)+ 回声带当前值(推)** —— 草稿态服务端查不到,模型此前只能把对话里几行回声自己累加 | 三端 |
| 31 | 重算类载体**原样带回人群条件**(refill 的 `narrowedBy`、快照的 `criteria`) | 三处载体 | | 31 | 重算类载体**原样带回人群条件**(refill 的 `narrowedBy`、快照的 `criteria`) | 三处载体 |
| 32 | **契约测试** —— 测"两端各自都对、错在交接" | **新建** `tests/assignment-contract.spec.ts` | | 32 | **契约测试** —— 测"两端各自都对、错在交接" | **新建** `tests/assignment-contract.spec.ts` |
......
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
├── 说节点外的话 ──── [模] 判断动的是哪一层 ├── 说节点外的话 ──── [模] 判断动的是哪一层
│ │ │ │
│ ├─ 动「怎么派」 → [程] 局部改 ────▶ 同一版,重算节点 │ ├─ 动「怎么派」 → [程] 局部改 ────▶ 同一版,重算节点
│ │ 谁给谁 · 时 · 移出本批 · 福利 │ │ 谁给谁 · 时 · 移出本批 · 福利
│ │ │ │
│ └─ 动「这批人」 → [程] 重跑 ──────▶ **出新版,旧版当场作废** │ └─ 动「这批人」 → [程] 重跑 ──────▶ **出新版,旧版当场作废**
│ 换口径 · 加画像条件 · 改人数 · 换治疗项 │ 换口径 · 加画像条件 · 改人数 · 换治疗项
...@@ -128,7 +128,7 @@ ...@@ -128,7 +128,7 @@
| 必须带回 | 漏了会怎样 | | 必须带回 | 漏了会怎样 |
|---|---| |---|---|
| 治疗项 · 时间档 | 按全池重算。实测:主管说「改成 200 人」,模型手里只有确认单本身(120 人、8 位客服、时 1 天),不知道原来是哪一格 —— 它自己的话:「刚才我按全部候选重算了一版,结果对不上……跟您眼前那批完全不是一回事」 | | 治疗项 · 时间档 | 按全池重算。实测:主管说「改成 200 人」,模型手里只有确认单本身(120 人、8 位客服、时 1 天),不知道原来是哪一格 —— 它自己的话:「刚才我按全部候选重算了一版,结果对不上……跟您眼前那批完全不是一回事」 |
| 主管自己加的那几刀(`narrowedBy`) | **退回整格**。实测「换无专属客服的患者补上」漏传 `narrowedBy`:候选 2,178 vs 296,差 1,882 人 —— 他刚圈掉的人全回来了 | | 主管自己加的那几刀(`narrowedBy`) | **退回整格**。实测「换无专属客服的患者补上」漏传 `narrowedBy`:候选 2,178 vs 296,差 1,882 人 —— 他刚圈掉的人全回来了 |
**凡是能触发重算的载体都要带全这一段**,目前有三处: **凡是能触发重算的载体都要带全这一段**,目前有三处:
...@@ -176,7 +176,7 @@ ...@@ -176,7 +176,7 @@
| | | | | |
|---|---| |---|---|
| 每人每日 | **15 条**(当前默认;⛔ 暂不沿用上一次的值) | | 每人每日 | **15 条**(当前默认;⛔ 暂不沿用上一次的值) |
| 时 | `D` 天 | | 时 | `D` 天 |
| **本批目标 N** | **在岗人数 × 15 × D**,并与候选总数取小 | | **本批目标 N** | **在岗人数 × 15 × D**,并与候选总数取小 |
### 事实② 怎么派的(**给谁**) ### 事实② 怎么派的(**给谁**)
...@@ -224,9 +224,9 @@ ...@@ -224,9 +224,9 @@
| sev | 层 | key | 给模型的标识 | 判定(程序) | 默认(no-op) | 选项(点击执行) | | sev | 层 | key | 给模型的标识 | 判定(程序) | 默认(no-op) | 选项(点击执行) |
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|
| **1** | 需处置 | `pending` | 待分配 | `pending > 0` | 留着 = 这批不发给他们 | ① **换无专属客服的患者补上**(见下,带取人范围)<br>② 铺平给在岗<br>③ 各自归专属客服<br>④ 移出本批 | | **1** | 需处置 | `pending` | 待分配 | `pending > 0` | 留着 = 这批不发给他们 | ① **换无专属客服的患者补上**(见下,带取人范围)<br>② 铺平给在岗<br>③ 各自归专属客服<br>④ 移出本批 |
| **2** | 需处置 | `daily_overload` | 打不完 | 分完后最重的那位 `手上总条数 > 15 × D` | 就按 D 天发,到期落回池子 | **只有一个:`时改成 N 天`**(N 由程序算) | | **2** | 需处置 | `daily_overload` | 打不完 | 分完后最重的那位 `手上总条数 > 15 × D` | 就按 D 天发,到期落回池子 | **只有一个:`时改成 N 天`**(N 由程序算) |
| **3** | 需处置 | `narrow` | 换个条件选 | 候选 ≥ 50 且切完 ≥ 10 人;主管已收窄过 / 已重挑过则不出 | 就按这一格全部来 | 几刀 + 兜底(见下) | | **3** | 需处置 | `narrow` | 换个条件选 | 候选 ≥ 50 且切完 ≥ 10 人;主管已收窄过 / 已重挑过则不出 | 就按这一格全部来 | 几刀 + 兜底(见下) |
| **4** | 需处置 | `batch_size_basis` | 本批人数 | `basis === 'default'` 且候选 > 基数 | 就按这个数发 | 改时 / 改每天几通(**都带输入框、预填当前值**) | | **4** | 需处置 | `batch_size_basis` | 本批人数 | `basis === 'default'` 且候选 > 基数 | 就按这个数发 | 改时 / 改每天几通(**都带输入框、预填当前值**) |
**终止分支(不是引导节点)**`empty` —— `total == 0`,无法继续,只能放宽或换格。 **终止分支(不是引导节点)**`empty` —— `total == 0`,无法继续,只能放宽或换格。
...@@ -243,9 +243,9 @@ ...@@ -243,9 +243,9 @@
#### `daily_overload` 是**工作量预估**,不是异常告警 #### `daily_overload` 是**工作量预估**,不是异常告警
N 按「在岗 × 每天 15 通 × 时」算,**只算新增、⛔ 不扣在手** —— 所以只要谁手上还有东西, N 按「在岗 × 每天 15 通 × 时」算,**只算新增、⛔ 不扣在手** —— 所以只要谁手上还有东西,
需要的天数就会超过时,这个节点**每批基本都会亮****这是对的**(产品定 2026-08-12): 需要的天数就会超过时,这个节点**每批基本都会亮****这是对的**(产品定 2026-08-12):
每一批本来就该把「这活多大、要打几天」讲清楚,主管据此决定延时、调每日通数、还是减量。 每一批本来就该把「这活多大、要打几天」讲清楚,主管据此决定延时、调每日通数、还是减量。
由此推出两条硬要求: 由此推出两条硬要求:
...@@ -332,8 +332,8 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在 ...@@ -332,8 +332,8 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在
| `dispatch` | 怎么派 | `pending` · `daily_overload` | 「怎么派的」 | | `dispatch` | 怎么派 | `pending` · `daily_overload` | 「怎么派的」 |
> 🔴 **`cohort` 与 `size` 必须是两个键**,虽然都嵌在同一段。合成一个键时模型会拿 > 🔴 **`cohort` 与 `size` 必须是两个键**,虽然都嵌在同一段。合成一个键时模型会拿
> 一个标题罩住两组按钮 —— 走查抓到「还能怎么选:」下面挂着「改时 / 改每天几通」, > 一个标题罩住两组按钮 —— 走查抓到「还能怎么选:」下面挂着「改时 / 改每天几通」,
> 而改时根本不是选人。⇒ **一个键只说一件事**。 > 而改时根本不是选人。⇒ **一个键只说一件事**。
> >
> 🔴 **开放的粒度是「一件」不是「一段」**(2026-08-15 实测)。工具描述里一度写着 > 🔴 **开放的粒度是「一件」不是「一段」**(2026-08-15 实测)。工具描述里一度写着
> 「在**它所属的那一段**讲完之后开放」,而「怎么选的」这一段里**有两件** > 「在**它所属的那一段**讲完之后开放」,而「怎么选的」这一段里**有两件**
...@@ -409,7 +409,7 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在 ...@@ -409,7 +409,7 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在
|---|---| |---|---|
| 福利 | ✅ 可补挂,不设时限 | | 福利 | ✅ 可补挂,不设时限 |
| 人员 | ❌ 单已在客服手上 → 走撤销重分 | | 人员 | ❌ 单已在客服手上 → 走撤销重分 |
| 时 | ❌ 同上 | | 时 | ❌ 同上 |
--- ---
...@@ -435,7 +435,7 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在 ...@@ -435,7 +435,7 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在
### 🔴 草稿态**模型看不见** —— 要查 `get_current_sheet`(2026-08-15 加) ### 🔴 草稿态**模型看不见** —— 要查 `get_current_sheet`(2026-08-15 加)
确认之前,主管在卡片上做的改动(改派 / 移出 / 改时 / 设福利)**只存在于浏览器里** 确认之前,主管在卡片上做的改动(改派 / 移出 / 改时 / 设福利)**只存在于浏览器里**
服务端手上只有最初生成的那一版提案。所以模型对"这张单现在什么样"只有两条来路: 服务端手上只有最初生成的那一版提案。所以模型对"这张单现在什么样"只有两条来路:
| | 谁推给它 | 覆盖 | | | 谁推给它 | 覆盖 |
...@@ -443,7 +443,7 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在 ...@@ -443,7 +443,7 @@ N 按「在岗 × 每天 15 通 × 时效」算,**只算新增、⛔ 不扣在
| **推** | 每次改单往对话里追加一行回声,`modelText` 后缀带当前值 | 改动发生的那一刻 | | **推** | 每次改单往对话里追加一行回声,`modelText` 后缀带当前值 | 改动发生的那一刻 |
| **拉** | `get_current_sheet`(无参数,读随请求捎来的 `SheetSnapshot`) | 任何时候 | | **拉** | `get_current_sheet`(无参数,读随请求捎来的 `SheetSnapshot`) | 任何时候 |
⚠️ 在这之前它只能把对话里几行回声**自己累加**,而手动拖动 / 点 × / 改时这几条路 ⚠️ 在这之前它只能把对话里几行回声**自己累加**,而手动拖动 / 点 × / 改时这几条路
**当时连回声都没有** —— 卡片上写着 3 天,它嘴上还说 1 天,两边都不报错。 **当时连回声都没有** —— 卡片上写着 3 天,它嘴上还说 1 天,两边都不报错。
⛔ 别去给服务端加"草稿回传"的写路径:多一份要维护的状态,只为省模型一次加法。 ⛔ 别去给服务端加"草稿回传"的写路径:多一份要维护的状态,只为省模型一次加法。
......
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
|---|---|---| |---|---|---|
| 1 | 批次实体 `plan_assignments` + `followup_plans` 归因列 | T17 / 五之二 | | 1 | 批次实体 `plan_assignments` + `followup_plans` 归因列 | T17 / 五之二 |
| 2 | 主管确认触发的**批量分配写入**(唯一写动作,走 REST) | T8 | | 2 | 主管确认触发的**批量分配写入**(唯一写动作,走 REST) | T8 |
| 3 | 助手全景直出**原生确认单**(微调仅「指定客服 / 时」两项) | T13 | | 3 | 助手全景直出**原生确认单**(微调仅「指定客服 / 时」两项) | T13 |
| 4 | 分配时 `assignment_expires_at`(写入 + 超期展示) | T11 | | 4 | 分配时 `assignment_expires_at`(写入 + 超期展示) | T11 |
| 5 | 客服退回带**结构化原因** | T7 | | 5 | 客服退回带**结构化原因** | T7 |
| 6 | 前端 T16 改造:客服只有「我的」,认领入口全撤 | T16 | | 6 | 前端 T16 改造:客服只有「我的」,认领入口全撤 | T16 |
| 7 | `PLAN_DISPATCH` 权限 + MCP 按能力过滤工具 | T19 | | 7 | `PLAN_DISPATCH` 权限 + MCP 按能力过滤工具 | T19 |
...@@ -84,7 +84,7 @@ Y 轴 窗口温度 plan_reasons 566,365 条 ← daysSince 现成,读时 ...@@ -84,7 +84,7 @@ Y 轴 窗口温度 plan_reasons 566,365 条 ← daysSince 现成,读时
| **D-5** | `followup_plans` 新增列数 | **6 列**(不是 4 也不是 5) | data 的 5 列 + 新增第 6 列 `assign_strategy`,见 D-6 | | **D-5** | `followup_plans` 新增列数 | **6 列**(不是 4 也不是 5) | data 的 5 列 + 新增第 6 列 `assign_strategy`,见 D-6 |
| **D-6** | 「专属命中 vs 溢出铺平」怎么记 | **立柱** `assign_strategy TEXT``dedicated` / `spread` / `manual`) | 四份方案**全都没记**。T20 要按它分组算完成率 → 「会用来筛」→ 按 T17 就该立柱。⚠️ 事后从 `patients.preferences.dedicatedCs` 反推**不可行**:那列是 upsert 覆盖的「当前值」,历史丢失。**分配当时不记就永久没了** | | **D-6** | 「专属命中 vs 溢出铺平」怎么记 | **立柱** `assign_strategy TEXT``dedicated` / `spread` / `manual`) | 四份方案**全都没记**。T20 要按它分组算完成率 → 「会用来筛」→ 按 T17 就该立柱。⚠️ 事后从 `patients.preferences.dedicatedCs` 反推**不可行**:那列是 upsert 覆盖的「当前值」,历史丢失。**分配当时不记就永久没了** |
| **D-7** | `ReleaseReason` 值域 | 取 **data 层的 8 个**`not_my_patient` / `over_capacity` / `agent_unavailable` / `needs_other_role` / `deadline_too_tight` / `recently_contacted` / `patient_info_missing` / `other`)。⛔ **类型里不定义 `suppressDays`** | ❌ service 的 6 个:其中 `bad_timing``RECALL_FEEDBACK_OPTIONS.bad_timing``enums/index.ts:641`**同名不同义**。值域仍需产品过目,见 Q-3 | | **D-7** | `ReleaseReason` 值域 | 取 **data 层的 8 个**`not_my_patient` / `over_capacity` / `agent_unavailable` / `needs_other_role` / `deadline_too_tight` / `recently_contacted` / `patient_info_missing` / `other`)。⛔ **类型里不定义 `suppressDays`** | ❌ service 的 6 个:其中 `bad_timing``RECALL_FEEDBACK_OPTIONS.bad_timing``enums/index.ts:641`**同名不同义**。值域仍需产品过目,见 Q-3 |
| **D-8** | 时入参形状 | 收 **`expiresInDays: number`**(相对天数),服务端按 `hosts.pullConfig.timezone` 转绝对时刻 | ❌ mcp 的 `expiresAt: z.string().datetime()` 必填:强制模型算 ISO 时刻,正是本仓 `4549817` 修过的「纯日期被当 UTC 零点偏 8 小时」 | | **D-8** | 时入参形状 | 收 **`expiresInDays: number`**(相对天数),服务端按 `hosts.pullConfig.timezone` 转绝对时刻 | ❌ mcp 的 `expiresAt: z.string().datetime()` 必填:强制模型算 ISO 时刻,正是本仓 `4549817` 修过的「纯日期被当 UTC 零点偏 8 小时」 |
| **D-9** | 名册 / 负载 | **一个** REST 端点 `GET /pac/v1/plans/agents?clinicId=&withWorkload=`,MCP 工具 `get_agents` 与前端共用 | ❌ service 的两端点方案:五之四明写「开两个必然口径漂移」 | | **D-9** | 名册 / 负载 | **一个** REST 端点 `GET /pac/v1/plans/agents?clinicId=&withWorkload=`,MCP 工具 `get_agents` 与前端共用 | ❌ service 的两端点方案:五之四明写「开两个必然口径漂移」 |
| **D-10** | 撤销授权判据 | `batch.createdBy === actorUserId \|\| permissions.includes(PLAN_VIEW_ALL)`,在 service 顶部一次性校验,**不逐行调 `assertCanRecycle`** | ❌ mcp 的 `confirmedByUser: boolean`:模型自己填,拦不住任何东西 | | **D-10** | 撤销授权判据 | `batch.createdBy === actorUserId \|\| permissions.includes(PLAN_VIEW_ALL)`,在 service 顶部一次性校验,**不逐行调 `assertCanRecycle`** | ❌ mcp 的 `confirmedByUser: boolean`:模型自己填,拦不住任何东西 |
| **D-11** | 「已动过 / 不可撤销」判据 | **以 `plan_event_logs` 的 `view` 事件为主** | ⚠️ **原案已部分证伪(2026-08-02 实测)**:原建议 `EXISTS(plan_executions) OR contactAttempts > 0 OR view`。实测 `contact_attempts > 0` **仅 7 条,与 `plan_executions` 同源**(提交执行时才累加),帮不上忙。可用信号只有 **`view` 事件(已 1,024 条)**。理由不变:回写率仅 11%,只看执行记录会把 89% 已打过电话的单静默收走。⚠️ 另注意 **撤销(主管收整批) ≠ 退回(客服退单条)**,见教条 T21 | | **D-11** | 「已动过 / 不可撤销」判据 | **以 `plan_event_logs` 的 `view` 事件为主** | ⚠️ **原案已部分证伪(2026-08-02 实测)**:原建议 `EXISTS(plan_executions) OR contactAttempts > 0 OR view`。实测 `contact_attempts > 0` **仅 7 条,与 `plan_executions` 同源**(提交执行时才累加),帮不上忙。可用信号只有 **`view` 事件(已 1,024 条)**。理由不变:回写率仅 11%,只看执行记录会把 89% 已打过电话的单静默收走。⚠️ 另注意 **撤销(主管收整批) ≠ 退回(客服退单条)**,见教条 T21 |
...@@ -178,7 +178,7 @@ Y 轴 窗口温度 plan_reasons 566,365 条 ← daysSince 现成,读时 ...@@ -178,7 +178,7 @@ Y 轴 窗口温度 plan_reasons 566,365 条 ← daysSince 现成,读时
### F3 · `RECYCLE_TIMEOUT_HOURS` 硬编码 —— **降级,不做 env 化** ### F3 · `RECYCLE_TIMEOUT_HOURS` 硬编码 —— **降级,不做 env 化**
教条五之二写「T11 时不可配」是**误判**:T11 的可配性由新的 `assignment_expires_at` 承担,且同节自己也定了「两条路不互相干扰」。加上自动回收生产未启用(`PAC_PLAN_AUTO_RECYCLE` 未配)—— env 化一个**没人读的常量**是纯噪音。 教条五之二写「T11 时不可配」是**误判**:T11 的可配性由新的 `assignment_expires_at` 承担,且同节自己也定了「两条路不互相干扰」。加上自动回收生产未启用(`PAC_PLAN_AUTO_RECYCLE` 未配)—— env 化一个**没人读的常量**是纯噪音。
**降级为 0.05 人日改两处注释**`plan.service.ts:50`(「后续接 tenant 配置」)与 `schema.prisma:1024`(「= assignedAt + tenant.rules_config.recycleTimeoutHours」)—— **PAC 全仓没有 `Tenant` 模型**,那句话会持续误导后来人。删掉它。 **降级为 0.05 人日改两处注释**`plan.service.ts:50`(「后续接 tenant 配置」)与 `schema.prisma:1024`(「= assignedAt + tenant.rules_config.recycleTimeoutHours」)—— **PAC 全仓没有 `Tenant` 模型**,那句话会持续误导后来人。删掉它。
...@@ -292,7 +292,7 @@ created_at / updated_at ...@@ -292,7 +292,7 @@ created_at / updated_at
| 列 | 类型 | 注释要点 | | 列 | 类型 | 注释要点 |
|---|---|---| |---|---|---|
| `assignment_id` | `UUID?` | **null = 自助认领**,不是「未知」。⚠️ 退回时**不清空**(退回率的分母) | | `assignment_id` | `UUID?` | **null = 自助认领**,不是「未知」。⚠️ 退回时**不清空**(退回率的分母) |
| `assignment_expires_at` | `TIMESTAMPTZ(3)?` | 生效时;⚠️ 与 `recycle_at` 是两条互不干扰的路,注释互指 | | `assignment_expires_at` | `TIMESTAMPTZ(3)?` | 生效时;⚠️ 与 `recycle_at` 是两条互不干扰的路,注释互指 |
| `assigned_by` | `TEXT?` | 谁分的 | | `assigned_by` | `TEXT?` | 谁分的 |
| `release_reason` | `TEXT?` | ⚠️ ≠ `recall_feedback`(1044);⚠️ 不写抑制窗 | | `release_reason` | `TEXT?` | ⚠️ ≠ `recall_feedback`(1044);⚠️ 不写抑制窗 |
| `release_note` | `TEXT?` `@db.Text` | `other` 时必填 | | `release_note` | `TEXT?` `@db.Text` | `other` 时必填 |
...@@ -434,7 +434,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH) ...@@ -434,7 +434,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH)
1. 「你**全程只读**。唯一改变数据的动作是主管在确认单上点确认,那由界面完成。**绝不要说『已经分配好了』**,正确说法是『确认单已呈现,请过目』」(T8 最容易破的地方) 1. 「你**全程只读**。唯一改变数据的动作是主管在确认单上点确认,那由界面完成。**绝不要说『已经分配好了』**,正确说法是『确认单已呈现,请过目』」(T8 最容易破的地方)
2. 「全景阶段**不问意图、不做画像分层**,直接出确认单」(T13 / T9-A) 2. 「全景阶段**不问意图、不做画像分层**,直接出确认单」(T13 / T9-A)
3. 「凡是没有历史数据支撑的建议值(时 / 容量 / 专属优先),**必须当场标明是默认值**;工具返回的 `capacityNote` / `rosterNote` **原话抄进去**」(T14) 3. 「凡是没有历史数据支撑的建议值(时 / 容量 / 专属优先),**必须当场标明是默认值**;工具返回的 `capacityNote` / `rosterNote` **原话抄进去**」(T14)
4.`sufficient=false`**照抄 note,不输出百分比、不画图、不出 0.0%**」+ 背景事实(全生产 `plan_executions` 仅 7 条、`success_appointed` 0 条)(T20 / 五之四) 4.`sufficient=false`**照抄 note,不输出百分比、不画图、不出 0.0%**」+ 背景事实(全生产 `plan_executions` 仅 7 条、`success_appointed` 0 条)(T20 / 五之四)
5. 「退回率永远给两个数:『退回 5 / 已处置 40 = 12.5%(另有 60 未动)』」(五之四) 5. 「退回率永远给两个数:『退回 5 / 已处置 40 = 12.5%(另有 60 未动)』」(五之四)
6. 「在岗/专属客服是**近似值**,原样转述 `rosterNote`,不说成『系统确认在职』,不替主管挡人」(六·已定取舍:不校验在岗) 6. 「在岗/专属客服是**近似值**,原样转述 `rosterNote`,不说成『系统确认在职』,不替主管挡人」(六·已定取舍:不校验在岗)
...@@ -448,7 +448,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH) ...@@ -448,7 +448,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH)
**验收** **验收**
- P3.2 生产量级(166.7 万行)`EXPLAIN ANALYZE`**新索引**,< 500ms;⭐ 造一条 `task_date=2033` 的记录,验证该客服**不**出现在名册里 - P3.2 生产量级(166.7 万行)`EXPLAIN ANALYZE`**新索引**,< 500ms;⭐ 造一条 `task_date=2033` 的记录,验证该客服**不**出现在名册里
- P3.3 对话验收:给 `n=12、转化 0` 的批次 → 输出必须含「样本量不足」,**不得出现「0.0%」**;给时建议 → 必须含「默认值」 - P3.3 对话验收:给 `n=12、转化 0` 的批次 → 输出必须含「样本量不足」,**不得出现「0.0%」**;给时建议 → 必须含「默认值」
- P3.4 侧信道验收:tool result 回到模型的内容 **< 100 token**,而前端收到的 payload 含完整 planId 列表 - P3.4 侧信道验收:tool result 回到模型的内容 **< 100 token**,而前端收到的 payload 含完整 planId 列表
- P3.5 staff token 调 `list_recall_queue({view:'pool'})` → 拒绝或降级为 `mine` - P3.5 staff token 调 `list_recall_queue({view:'pool'})` → 拒绝或降级为 `mine`
...@@ -506,7 +506,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH) ...@@ -506,7 +506,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH)
| **新建** `components/assistant/assignment-confirm-sheet.tsx` | 三层:汇总 → 按客服卡片列表(**不是 table**,400px 宽 `assistant-widget.tsx:27`)→ 患者明细(按客服折叠) | | **新建** `components/assistant/assignment-confirm-sheet.tsx` | 三层:汇总 → 按客服卡片列表(**不是 table**,400px 宽 `assistant-widget.tsx:27`)→ 患者明细(按客服折叠) |
| **新建** `components/plans/assignments-api.ts` | 对齐 `plans-api.ts:12-70` | | **新建** `components/plans/assignments-api.ts` | 对齐 `plans-api.ts:12-70` |
**微调只有两项**(T13:指定客服 / 时效)。多加一项直接违 T13,**评审按这条卡**。时效必须带「默认值」标注,文案照抄教条:「3 天(默认值,暂无历史结案数据,积累后按实际反推)」。溢出转铺平要**显式可见**(T15):被铺平的组打角标「专属容量不足,已转铺平」,可点开改(并写回 `assignStrategy`)。 **微调只有两项**(T13:指定客服 / 时限)。多加一项直接违 T13,**评审按这条卡**。时限必须带「默认值」标注,文案照抄教条:「3 天(默认值,暂无历史结案数据,积累后按实际反推)」。溢出转铺平要**显式可见**(T15):被铺平的组打角标「专属容量不足,已转铺平」,可点开改(并写回 `assignStrategy`)。
底部「确认分配」按 `<Can perm={PLAN_DISPATCH}>` 包住(`components/can.tsx:24` 支持 fallback)。 底部「确认分配」按 `<Can perm={PLAN_DISPATCH}>` 包住(`components/can.tsx:24` 支持 fallback)。
...@@ -575,7 +575,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH) ...@@ -575,7 +575,7 @@ POST /pac/v1/plans/assignments/:id/revoke @RequirePermission(PLAN_DISPATCH)
→ 点「移交助手」(emitPetEvent + assistantStore.ask) → 点「移交助手」(emitPetEvent + assistantStore.ask)
② 精选 助手全景直出:产能视图 + 拟分方案(get_agents 名册+负载) ② 精选 助手全景直出:产能视图 + 拟分方案(get_agents 名册+负载)
③ 确认 propose_assignment → 侧信道 → 原生确认单三层卡片 ③ 确认 propose_assignment → 侧信道 → 原生确认单三层卡片
微调仅两项(指定客服 / 时),铺平角标可见 微调仅两项(指定客服 / 时),铺平角标可见
④ 分配 点确认 → POST /pac/v1/plans/assignments ④ 分配 点确认 → POST /pac/v1/plans/assignments
→ 落批次 + N 条归属 + N 条 assign 事件 → 注入文本块补记忆 → 落批次 + N 条归属 + N 条 assign 事件 → 注入文本块补记忆
⑤ 执行 切 staff 账号 → 「我的」看到单(左栏只有「我的」,无认领入口)→ 提交执行结果 ⑤ 执行 切 staff 账号 → 「我的」看到单(左栏只有「我的」,无认领入口)→ 提交执行结果
...@@ -768,7 +768,7 @@ T4 已把落点写死:`shared/fact-block.ts`(标准 + 深度)与 `tiers/st ...@@ -768,7 +768,7 @@ T4 已把落点写死:`shared/fact-block.ts`(标准 + 深度)与 `tiers/st
| **Q-4** 🟠 | **MVS 期福利的落地方式降级** | T4 明写福利要进 prompt(`shared/fact-block.ts` + `tiers/stable/prompt.ts`),但撞上 `plan_scripts.planId @unique` 的缓存问题(R8),最坏会**对患者做虚假承诺** | 建议 MVS 期降级为「独立静态字段,客服自己念,不经 LLM」—— 零合规风险、零重生成成本,T4 的归因目的完全达成;P6.6 再做 prompt 融入 + 失效规则 | P5 | | **Q-4** 🟠 | **MVS 期福利的落地方式降级** | T4 明写福利要进 prompt(`shared/fact-block.ts` + `tiers/stable/prompt.ts`),但撞上 `plan_scripts.planId @unique` 的缓存问题(R8),最坏会**对患者做虚假承诺** | 建议 MVS 期降级为「独立静态字段,客服自己念,不经 LLM」—— 零合规风险、零重生成成本,T4 的归因目的完全达成;P6.6 再做 prompt 融入 + 失效规则 | P5 |
| **Q-5** 🟠 | **批次规模统计的漂移是否可接受** | 一个 plan 在批次 N 被分 → 退回 → 进批次 N+1 → `assignment_id` 翻成 N+1,批次 N 的 COUNT 悄悄少 1 | ① 接受(简单,符合「主表存当前值」的既有模式)② 不接受 → 需要一张 `plan_assignment_members` 关联表(+0.5 人日 + 一列迁移)。⛔ 「写进 `plan_event_logs.details`」的补丁方案已被否(R12) | P1.1 建表 | | **Q-5** 🟠 | **批次规模统计的漂移是否可接受** | 一个 plan 在批次 N 被分 → 退回 → 进批次 N+1 → `assignment_id` 翻成 N+1,批次 N 的 COUNT 悄悄少 1 | ① 接受(简单,符合「主表存当前值」的既有模式)② 不接受 → 需要一张 `plan_assignment_members` 关联表(+0.5 人日 + 一列迁移)。⛔ 「写进 `plan_event_logs.details`」的补丁方案已被否(R12) | P1.1 建表 |
| ~~**Q-6**~~ ✅ | ~~**温度的 8 标签 × 窗口映射**~~ | **已裁决(2026-08-02),见教条 T6′** | ⭐ 原建议(标签级窗口表 8 行常量)**已否**。真正的解法是**换一层聚合**:逐条 gap 用自己 K 码的窗口判档、再取最热 —— 一对多自动消解,不需要任何新常量,`DiagnosisTreatmentMap` 是真的被复用了。原判「无解」源于默认了聚合必须发生在天数层 | — | | ~~**Q-6**~~ ✅ | ~~**温度的 8 标签 × 窗口映射**~~ | **已裁决(2026-08-02),见教条 T6′** | ⭐ 原建议(标签级窗口表 8 行常量)**已否**。真正的解法是**换一层聚合**:逐条 gap 用自己 K 码的窗口判档、再取最热 —— 一对多自动消解,不需要任何新常量,`DiagnosisTreatmentMap` 是真的被复用了。原判「无解」源于默认了聚合必须发生在天数层 | — |
| **Q-7** 🟡 | **分配单到期后的行为**(教条七·待确认) | ① 自动回池 ② 只提醒 ③ 两者皆有。⚠️ ①/③ 会擦 T8 的判据(「任何改变数据库状态的动作只能由主管确认触发」)。可辩护的解释是「主管在确认单上确认了时 = 预授权」,但**这属于产品解释权,不该由实现认定** | 建议 **MVS 只做 ②**(写入 + 超期标红),①/③ 推到 v2。若选 ①/③,需在确认单文案上把到期行为显式写出来,把预授权变成看得见的;且**必须落 `release_reason='expired'`**(否则 T20 的分母不干净)、**必须照抄 `recycle-scheduler.service.ts:64-68` 的 `snoozedUntil` 守卫**(约好 6/10 回访的单不能被收走) | 不卡 MVS | | **Q-7** 🟡 | **分配单到期后的行为**(教条七·待确认) | ① 自动回池 ② 只提醒 ③ 两者皆有。⚠️ ①/③ 会擦 T8 的判据(「任何改变数据库状态的动作只能由主管确认触发」)。可辩护的解释是「主管在确认单上确认了时 = 预授权」,但**这属于产品解释权,不该由实现认定** | 建议 **MVS 只做 ②**(写入 + 超期标红),①/③ 推到 v2。若选 ①/③,需在确认单文案上把到期行为显式写出来,把预授权变成看得见的;且**必须落 `release_reason='expired'`**(否则 T20 的分母不干净)、**必须照抄 `recycle-scheduler.service.ts:64-68` 的 `snoozedUntil` 守卫**(约好 6/10 回访的单不能被收走) | 不卡 MVS |
| **Q-8** 🟡 | **撤销时限** | 建议可配 env、默认 **30 分钟**,并按 T14 在助手话术里标明是默认值 | 语义是「手滑/分错人」的补救,不是「改主意重新调度」(改主意应走退回 + 重分)。⚠️ 它是 UX 摩擦不是安全边界(R18) | P6.4 | | **Q-8** 🟡 | **撤销时限** | 建议可配 env、默认 **30 分钟**,并按 T14 在助手话术里标明是默认值 | 语义是「手滑/分错人」的补救,不是「改主意重新调度」(改主意应走退回 + 重分)。⚠️ 它是 UX 摩擦不是安全边界(R18) | P6.4 |
| **Q-9** 🟡 | **已被认领的单能否强制改派**(教条七·待确认) | 当前后端拦住(`plan.service.ts:589-591`),教条建议保留该摩擦 | 建议保持拦住 → 进 `skipped(reason:'claimed_by_other')`。⭐ 该判据已收口到 `claim-guard.assertAssignable` **一个函数**,将来改只改这一处 | 不卡 | | **Q-9** 🟡 | **已被认领的单能否强制改派**(教条七·待确认) | 当前后端拦住(`plan.service.ts:589-591`),教条建议保留该摩擦 | 建议保持拦住 → 进 `skipped(reason:'claimed_by_other')`。⭐ 该判据已收口到 `claim-guard.assertAssignable` **一个函数**,将来改只改这一处 | 不卡 |
| **Q-10** 🟢 | 矩阵在 `<lg` 小屏是否提供 | 建议**不提供**(入口加 `lg:` 藏掉)—— 8×3 在 375px 抽屉里做不出可读密度,主管基本桌面办公。若要求移动端可用,需另做纵向堆叠版 +1.0 人日 | 不卡 | | **Q-10** 🟢 | 矩阵在 `<lg` 小屏是否提供 | 建议**不提供**(入口加 `lg:` 藏掉)—— 8×3 在 375px 抽屉里做不出可读密度,主管基本桌面办公。若要求移动端可用,需另做纵向堆叠版 +1.0 人日 | 不卡 |
......
...@@ -88,14 +88,14 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -88,14 +88,14 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
> ⚠️ 它**仍然是默认值**:没有任何数据证明 100 比 80 或 150 好,等 T20 沉淀出 > ⚠️ 它**仍然是默认值**:没有任何数据证明 100 比 80 或 150 好,等 T20 沉淀出
> 「批次规模 × 完成率」再替换。⛔ 别给它加上下限 —— 这是主管对自己团队的判断。 > 「批次规模 × 完成率」再替换。⛔ 别给它加上下限 —— 这是主管对自己团队的判断。
### T22 · 分配只有两个基数:**本批人数 N** 和 **时**,且都沿用主管上一次的值 ### T22 · 分配只有两个基数:**本批人数 N** 和 **时**,且都沿用主管上一次的值
(2026-08-03 产品定;同日两次改判,下面把弯路一并记下来,别再走一遍) (2026-08-03 产品定;同日两次改判,下面把弯路一并记下来,别再走一遍)
| 基数 | 含义 | 首次 | 之后 | | 基数 | 含义 | 首次 | 之后 |
|---|---|---|---| |---|---|---|---|
| **本批人数 N** | 这一轮推多少人 | `min(在岗人数 × 20, 候选总数)` | `min(上一次的 N, 候选总数)` | | **本批人数 N** | 这一轮推多少人 | `min(在岗人数 × 20, 候选总数)` | `min(上一次的 N, 候选总数)` |
| **时** | 这批单子多久没动就自动回池 | 3 天 | 沿用上一次 | | **时** | 这批单子多久没动就自动回池 | 3 天 | 沿用上一次 |
⚠️ 首次那个 **20 不是容量上限**,只是"第一次没有任何历史时,一批推多大"的估法, ⚠️ 首次那个 **20 不是容量上限**,只是"第一次没有任何历史时,一批推多大"的估法,
之后就再也不出现(沿用主管上次用的数)。 之后就再也不出现(沿用主管上次用的数)。
...@@ -170,15 +170,15 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -170,15 +170,15 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
| 卡片上做 | 回对话做 | | 卡片上做 | 回对话做 |
|---|---| |---|---|
| 改批次时(右上角「N 天后自动退回」) | 换人群(换治疗项 / 温度 / 画像条件) | | 改批次时(右上角「N 天后自动退回」) | 换人群(换治疗项 / 温度 / 画像条件) |
| 逐条改时(1-7 天) | 改批次人数 N | | 逐条改时(1-7 天) | 改批次人数 N |
| 删掉某一条 | 按客服设本批名额 `maxThisBatch` | | 删掉某一条 | 按客服设本批名额 `maxThisBatch` |
| 把某一条**拖**给别的客服 | | | 把某一条**拖**给别的客服 | |
| 移除某个客服(连同他名下条目) | | | 移除某个客服(连同他名下条目) | |
⚠️ 批次时放在**卡片右上角**,与左边"拟分 N 人"同一层级 —— 它是"这一整批"的设置。 ⚠️ 批次时放在**卡片右上角**,与左边"拟分 N 人"同一层级 —— 它是"这一整批"的设置。
放在底部微调区会让人以为它跟条目上的逐条时是一回事。 放在底部微调区会让人以为它跟条目上的逐条时是一回事。
⚠️ 文案是「N 天**后自动退回**」而不是光一个「时」:主管要知道到期会发生什么(单子回池), ⚠️ 文案是「N 天**后自动退回**」而不是光一个「时」:主管要知道到期会发生什么(单子回池),
不然这个数对他没有意义。 不然这个数对他没有意义。
⚠️ 这不违 T13「微调只有两项」—— 那条防的是"卡片变成第二个筛选器"。 ⚠️ 这不违 T13「微调只有两项」—— 那条防的是"卡片变成第二个筛选器"。
...@@ -248,7 +248,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -248,7 +248,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
> 🔴 **踩过的坑:`key={i}` + 「把确认单钉到消息末尾」= 卡片状态全丢。** > 🔴 **踩过的坑:`key={i}` + 「把确认单钉到消息末尾」= 卡片状态全丢。**
> 助手每追加一句文本,sheet 在排序后数组里的下标就变 → React 卸载重建组件 → > 助手每追加一句文本,sheet 在排序后数组里的下标就变 → React 卸载重建组件 →
> 主管删的条、拖的改派、逐条时、以及"能不能撤销"的确认时刻**全部清空**。 > 主管删的条、拖的改派、逐条时、以及"能不能撤销"的确认时刻**全部清空**。
> 症状是"确认完撤销按钮压根不出现",但真正丢的远不止那一个按钮。 > 症状是"确认完撤销按钮压根不出现",但真正丢的远不止那一个按钮。
> 现在确认单按 `requestId` 做 key。⛔ 凡是**带本地状态**的 block,key 都不能跟着位置走。 > 现在确认单按 `requestId` 做 key。⛔ 凡是**带本地状态**的 block,key 都不能跟着位置走。
...@@ -312,7 +312,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -312,7 +312,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
| `maxThisBatch` | **这一批**最多给他几条(0 = 这轮不给) | 会(少的那部分由别人接走,总数不变;但名单变了) | 回对话说一句,助手重出确认单 | | `maxThisBatch` | **这一批**最多给他几条(0 = 这轮不给) | 会(少的那部分由别人接走,总数不变;但名单变了) | 回对话说一句,助手重出确认单 |
| `expiresInDays` | 他名下每条任务的到期 | 不会 | 卡片上展开那一行直接改(1-7 天下拉) | | `expiresInDays` | 他名下每条任务的到期 | 不会 | 卡片上展开那一行直接改(1-7 天下拉) |
⚠️ 时**落到每一条召回计划**`followup_plans.assignment_expires_at` 逐行写), ⚠️ 时**落到每一条召回计划**`followup_plans.assignment_expires_at` 逐行写),
不是只在批次头上挂一个 —— 所以按客服精调、乃至将来按单条精调,都不需要动表结构。 不是只在批次头上挂一个 —— 所以按客服精调、乃至将来按单条精调,都不需要动表结构。
⚠️ 卡片上给**全部 1-7 天**而不是 3/5/7 三档:档位是我们拍的, ⚠️ 卡片上给**全部 1-7 天**而不是 3/5/7 三档:档位是我们拍的,
而主管对自己团队的节奏有判断(「明天就要」= 1 天)。⛔ 别用三个拍的档位限制他。 而主管对自己团队的节奏有判断(「明天就要」= 1 天)。⛔ 别用三个拍的档位限制他。
...@@ -323,10 +323,10 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -323,10 +323,10 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
> (服务端算一遍、随确认单发到浏览器、然后被丢掉)—— 也就是说这个能力早就不在了, > (服务端算一遍、随确认单发到浏览器、然后被丢掉)—— 也就是说这个能力早就不在了,
> 删掉 `basisNote` 只是让它显形。现在唯一的出处是 `modelFacts` 的 **`他单独设过的`** > 删掉 `basisNote` 只是让它显形。现在唯一的出处是 `modelFacts` 的 **`他单独设过的`**
> (卡片上也没有:精调只在确认时随请求回传)。⛔ 别再往 `basisNote` 里加东西,那个字段已删。 > (卡片上也没有:精调只在确认时随请求回传)。⛔ 别再往 `basisNote` 里加东西,那个字段已删。
精调落到 `followup_plans.assignment_expires_at`(写路径早已支持逐条覆盖)。 精调落到 `followup_plans.assignment_expires_at`(写路径早已支持逐条覆盖)。
**为什么是"沿用上一次"而不是"每次问"**:确认单本来就是给主管调的 —— **为什么是"沿用上一次"而不是"每次问"**:确认单本来就是给主管调的 ——
他第一次把人数和时调到顺手,系统记住,**第二次起零输入** 他第一次把人数和时调到顺手,系统记住,**第二次起零输入**
这是「先出全景确认单、再让主管反馈调整」这个设计的兑现点:调一次,以后省一次。 这是「先出全景确认单、再让主管反馈调整」这个设计的兑现点:调一次,以后省一次。
⛔ 既不能做成每次弹窗问一遍(违 T13 直出不追问),也不能永远用系统默认(第一次的调整白费)。 ⛔ 既不能做成每次弹窗问一遍(违 T13 直出不追问),也不能永远用系统默认(第一次的调整白费)。
...@@ -498,7 +498,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -498,7 +498,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
> 枚举齐了(8 个 `ReleaseReason` + `needNote`)、API 收得了、`releaseReasons` 分布报表也写了 —— > 枚举齐了(8 个 `ReleaseReason` + `needNote`)、API 收得了、`releaseReasons` 分布报表也写了 ——
> 但**前端唯一的退回入口 `plansApi.recycle(planId)` 一个参数都没传**,也没有弹窗。 > 但**前端唯一的退回入口 `plansApi.recycle(planId)` 一个参数都没传**,也没有弹窗。
> 于是那张分布表拿到的永远是 null:主管只知道"退了 12 条",不知道该改什么, > 于是那张分布表拿到的永远是 null:主管只知道"退了 12 条",不知道该改什么,
> 而"派多了 / 时太紧 / 压根不该派给他"这三种的下一步动作完全不同。 > 而"派多了 / 时太紧 / 压根不该派给他"这三种的下一步动作完全不同。
> >
> 现在:前端弹 `ReleaseReasonDialog`(8 个原因各带一句 desc —— 光看标题分不出「不是我的客户」 > 现在:前端弹 `ReleaseReasonDialog`(8 个原因各带一句 desc —— 光看标题分不出「不是我的客户」
> 和「该由其他角色跟」,分不出就会随手点第一个,**假数据比没数据更难发现**); > 和「该由其他角色跟」,分不出就会随手点第一个,**假数据比没数据更难发现**);
...@@ -534,10 +534,10 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -534,10 +534,10 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
以后要加权益身份、治疗史、家庭结构,只是多传一个 `key`**不为每个因素开新接口** 以后要加权益身份、治疗史、家庭结构,只是多传一个 `key`**不为每个因素开新接口**
画像能力对助手是**一个可触发的动作**,不是一堆预置维度。 画像能力对助手是**一个可触发的动作**,不是一堆预置维度。
### T11 · 分配单有时性,且必须在确认单上指明 ### T11 · 分配单有时性,且必须在确认单上指明
分配不是永久归属,**带有效期**(如 3 天)。 分配不是永久归属,**带有效期**(如 3 天)。
依据 = 召回池存量 + 客服负载完成量 + 主管习惯,**综合预估后由助手建议、主管确认** 依据 = 召回池存量 + 客服负载完成量 + 主管习惯,**综合预估后由助手建议、主管确认**
### T12 · 认领与分配同构 —— 差别只在触发人 ### T12 · 认领与分配同构 —— 差别只在触发人
...@@ -552,7 +552,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -552,7 +552,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
- **不问主管意图** —— 意图由**助手推导**(可用依据:初选的治疗项+温度、当前月份、池子存量), - **不问主管意图** —— 意图由**助手推导**(可用依据:初选的治疗项+温度、当前月份、池子存量),
是助手给自己找依据的内部推理,**只影响建议措辞**,不占主管的注意力,也不要求主管事后反馈。 是助手给自己找依据的内部推理,**只影响建议措辞**,不占主管的注意力,也不要求主管事后反馈。
- **直出明细**,分三层:汇总 → 按客服(产能视图)→ 患者明细(按客服折叠,展开才看)。 - **直出明细**,分三层:汇总 → 按客服(产能视图)→ 患者明细(按客服折叠,展开才看)。
- **页面可微调,且只微调两项**:指定客服、时 - **页面可微调,且只微调两项**:指定客服、时
微调项越多,「一次确认」就越不可能实现;换人群回到对话,由助手触发画像圈人(T9-B)。 微调项越多,「一次确认」就越不可能实现;换人群回到对话,由助手触发画像圈人(T9-B)。
### T14 · 助手不出没有证据的结果 ### T14 · 助手不出没有证据的结果
...@@ -563,7 +563,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -563,7 +563,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
- 首次无历史 → 用**行业通用 / 第一性**推出的默认值,**并标明是默认值** - 首次无历史 → 用**行业通用 / 第一性**推出的默认值,**并标明是默认值**
- 有数据之后 → 反推真实习惯,替换默认值 - 有数据之后 → 反推真实习惯,替换默认值
> 例:时「3 天」现在只能标注为「默认值(暂无历史结案数据,积累后按实际反推)」, > 例:时「3 天」现在只能标注为「默认值(暂无历史结案数据,积累后按实际反推)」,
> **不能**写成「依据该诊所平均结案 2.4 天」—— 生产 `plan_executions` 仅 7 条,算不出来。 > **不能**写成「依据该诊所平均结案 2.4 天」—— 生产 `plan_executions` 仅 7 条,算不出来。
### T15 · 溢出默认转铺平 ### T15 · 溢出默认转铺平
...@@ -578,7 +578,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾 ...@@ -578,7 +578,7 @@ v1 **轻量**:不核销、不接宿主福利数据,福利就是**话术勾
① 初选 主管在矩阵上点一格(潜在治疗项目 × 温度) ← 主管界面,与助手无关 ① 初选 主管在矩阵上点一格(潜在治疗项目 × 温度) ← 主管界面,与助手无关
② 精选 A 全景:产能视图 + 意图承接 + 引导建议 ← 助手登场,不做画像分层 ② 精选 A 全景:产能视图 + 意图承接 + 引导建议 ← 助手登场,不做画像分层
B 调整:主管追问 → 助手触发画像圈人精确响应 B 调整:主管追问 → 助手触发画像圈人精确响应
③ 定制 分配策略 + 福利 + 时确认 ③ 定制 分配策略 + 福利 + 时确认
④ 分配 主管确认 → 落到客服 ← 唯一的写动作 ④ 分配 主管确认 → 落到客服 ← 唯一的写动作
⑤ 跟踪 在手/超期/退回率/退回原因/批次效果 ⑤ 跟踪 在手/超期/退回率/退回原因/批次效果
``` ```
...@@ -687,7 +687,7 @@ plan_event_logs view 1,024 ✅ 唯一可用 —— 客服打开过详情页 ...@@ -687,7 +687,7 @@ plan_event_logs view 1,024 ✅ 唯一可用 —— 客服打开过详情页
| 助手当前的默认值 | 沉淀后反推成 | | 助手当前的默认值 | 沉淀后反推成 |
|---|---| |---|---|
| 时「3 天(默认)」 | 该诊所该治疗项的**实际结案中位数** | | 时「3 天(默认)」 | 该诊所该治疗项的**实际结案中位数** |
| 容量「20-50」 | 各客服**实际能吃多少**(完成率开始下滑的拐点) | | 容量「20-50」 | 各客服**实际能吃多少**(完成率开始下滑的拐点) |
| 「专属优先,溢出铺平」 | **专属 vs 铺平的完成率差**是否显著 | | 「专属优先,溢出铺平」 | **专属 vs 铺平的完成率差**是否显著 |
| 福利「建议配 / 不必配」 | 带福利 vs 不带的**完成率差** | | 福利「建议配 / 不必配」 | 带福利 vs 不带的**完成率差** |
...@@ -857,7 +857,7 @@ created_by 发起分配的主管(宿主侧 user id) ...@@ -857,7 +857,7 @@ created_by 发起分配的主管(宿主侧 user id)
created_at / updated_at created_at / updated_at
criteria Json 初筛条件快照 { potentialTreatment, temperature, ... } ← T17 不立柱 criteria Json 初筛条件快照 { potentialTreatment, temperature, ... } ← T17 不立柱
attributes Json? 附加属性 { benefit?: { text } } ← T17 福利只是其中一种 attributes Json? 附加属性 { benefit?: { text } } ← T17 福利只是其中一种
expires_at 时(T11);单条可覆盖 expires_at 时(T11);单条可覆盖
status confirmed | revoked status confirmed | revoked
revoked_at / revoked_by revoked_at / revoked_by
``` ```
...@@ -868,7 +868,7 @@ revoked_at / revoked_by ...@@ -868,7 +868,7 @@ revoked_at / revoked_by
``` ```
assignment_id FK → plan_assignments null = 自助认领 assignment_id FK → plan_assignments null = 自助认领
assignment_expires_at 该单时(继承批次,允许单条改) assignment_expires_at 该单时(继承批次,允许单条改)
assigned_by 谁分的(现有只记 assignee_user_id「分给谁」,缺「谁分的」) assigned_by 谁分的(现有只记 assignee_user_id「分给谁」,缺「谁分的」)
release_reason 退回原因(结构化,可统计分布 → T7) release_reason 退回原因(结构化,可统计分布 → T7)
release_note 退回文字说明 release_note 退回文字说明
...@@ -894,10 +894,10 @@ release_note 退回文字说明 ...@@ -894,10 +894,10 @@ release_note 退回文字说明
|---|---| |---|---|
| 引擎丢归属不记账(`plan-engine.service.ts:398`) | 分配单被静默收回,主管无感知;退回率偏低 | | 引擎丢归属不记账(`plan-engine.service.ts:398`) | 分配单被静默收回,主管无感知;退回率偏低 |
| `recycle``reason` 被丢弃(`plan.controller.ts:127`) | **T7「退回必须说明原因」现在落不了地** | | `recycle``reason` 被丢弃(`plan.controller.ts:127`) | **T7「退回必须说明原因」现在落不了地** |
| `RECYCLE_TIMEOUT_HOURS` 硬编码 | T11 时不可配 | | `RECYCLE_TIMEOUT_HOURS` 硬编码 | T11 时不可配 |
> 自动回收当前**未启用**(`PAC_PLAN_AUTO_RECYCLE` 未配),`recycle_at` 保持现状不动; > 自动回收当前**未启用**(`PAC_PLAN_AUTO_RECYCLE` 未配),`recycle_at` 保持现状不动;
> 分配时走新的 `assignment_expires_at`,两条路不互相干扰。 > 分配时走新的 `assignment_expires_at`,两条路不互相干扰。
--- ---
...@@ -1034,7 +1034,7 @@ artifact iframe 是 `sandbox="allow-scripts"` + CSP `connect-src 'none'`,**卡 ...@@ -1034,7 +1034,7 @@ artifact iframe 是 `sandbox="allow-scripts"` + CSP `connect-src 'none'`,**卡
> ⚠️ 同一 plan 可能有**多条 assign**(退回→自认领→再退回),去重后再计数。 > ⚠️ 同一 plan 可能有**多条 assign**(退回→自认领→再退回),去重后再计数。
> ⭐ **到期 ≠ 退回,两个数必须分开报。** 主管的下一步动作**相反**: > ⭐ **到期 ≠ 退回,两个数必须分开报。** 主管的下一步动作**相反**:
> 退回多 → 分配策略不对(派给了不该派的人);到期多 → 派多了 / 时太紧 / 人不在岗。 > 退回多 → 分配策略不对(派给了不该派的人);到期多 → 派多了 / 时太紧 / 人不在岗。
> 合成一个"回池率"两种病都看不出来。 > 合成一个"回池率"两种病都看不出来。
> (枚举注释早就写清了「到期是**没处置**、退回是**处置**」,所以到期刻意不写 > (枚举注释早就写清了「到期是**没处置**、退回是**处置**」,所以到期刻意不写
> `followup_plans.release_reason` —— 但此前**任何接口都没把到期数报出来**, > `followup_plans.release_reason` —— 但此前**任何接口都没把到期数报出来**,
...@@ -1123,7 +1123,7 @@ getAgentWorkload({ userIds? }) ...@@ -1123,7 +1123,7 @@ getAgentWorkload({ userIds? })
### 数据来源:全部现有表 + 一个新增关联,**不需要新建统计表** ### 数据来源:全部现有表 + 一个新增关联,**不需要新建统计表**
``` ```
plan_assignments 批次本身:时、福利、初筛条件、状态 plan_assignments 批次本身:时、福利、初筛条件、状态
followup_plans .assignment_id → 分了哪些、现在什么状态、超期没 followup_plans .assignment_id → 分了哪些、现在什么状态、超期没
plan_event_logs 经 plan_id join → 认领/退回/反馈全流水 plan_event_logs 经 plan_id join → 认领/退回/反馈全流水
plan_executions 经 plan_id → 通话结果、转化 plan_executions 经 plan_id → 通话结果、转化
...@@ -1184,7 +1184,7 @@ patient_transactions 经 patient_id → 客观新预约(canonical_payload.cre ...@@ -1184,7 +1184,7 @@ patient_transactions 经 patient_id → 客观新预约(canonical_payload.cre
- 已被认领的单能否强制改派(现后端拦住 —— 「Plan 已分配给 X;回收后再分配」,建议保留该摩擦) - 已被认领的单能否强制改派(现后端拦住 —— 「Plan 已分配给 X;回收后再分配」,建议保留该摩擦)
- 分配单**到期后**的行为:自动回池 / 提醒主管 / 两者皆有 - 分配单**到期后**的行为:自动回池 / 提醒主管 / 两者皆有
- 全景阶段主管意图的**捕获方式**:助手主动问 / 预设选项 / 可跳过 - 全景阶段主管意图的**捕获方式**:助手主动问 / 预设选项 / 可跳过
-性建议的**具体算法**(召回池存量 + 负载完成量 + 主管习惯,如何加权) -性建议的**具体算法**(召回池存量 + 负载完成量 + 主管习惯,如何加权)
- 批次的规模建议上限 - 批次的规模建议上限
--- ---
...@@ -1197,7 +1197,7 @@ patient_transactions 经 patient_id → 客观新预约(canonical_payload.cre ...@@ -1197,7 +1197,7 @@ patient_transactions 经 patient_id → 客观新预约(canonical_payload.cre
|---|---|---|---| |---|---|---|---|
| **S1 · 闭环**<br/>(MVS,≈19 人日) | 初选矩阵 → 助手全景确认单 → 主管确认分配 → 客服执行/退回 → 基础跟踪 | 主管 + 客服 | 表结构 + REST 写路径 + 助手读工具 + 前端 T16 改造 | | **S1 · 闭环**<br/>(MVS,≈19 人日) | 初选矩阵 → 助手全景确认单 → 主管确认分配 → 客服执行/退回 → 基础跟踪 | 主管 + 客服 | 表结构 + REST 写路径 + 助手读工具 + 前端 T16 改造 |
| **S2 · 完备** | 撤销整批 · 福利进话术 · 调整阶段画像圈人 · 矩阵性能优化 | 同上 | S1 已上线并跑过真实批次 | | **S2 · 完备** | 撤销整批 · 福利进话术 · 调整阶段画像圈人 · 矩阵性能优化 | 同上 | S1 已上线并跑过真实批次 |
| **S3 · 自优化** | 用沉淀数据**替换默认值**(时/容量/专属策略/福利效果) | 主管(体现在助手建议里) | 需 **n≥50** 的执行样本(T20/T14);当前生产仅 7 条 | | **S3 · 自优化** | 用沉淀数据**替换默认值**(时/容量/专属策略/福利效果) | 主管(体现在助手建议里) | 需 **n≥50** 的执行样本(T20/T14);当前生产仅 7 条 |
| **S4 · MCP 写** | 助手可直接执行分配(外部 agent 亦可) | 助手 / 外部 agent | **MCP 鉴权补齐**(现为 `@Public()``PermissionsGuard` 短路) | | **S4 · MCP 写** | 助手可直接执行分配(外部 agent 亦可) | 助手 / 外部 agent | **MCP 鉴权补齐**(现为 `@Public()``PermissionsGuard` 短路) |
| **S5 · 客服主动性** | 客服在余量内自助认领(余量 + 任务相关性) | 客服 | 分配这条路跑顺;见 T16 「加回一个入口的事」 | | **S5 · 客服主动性** | 客服在余量内自助认领(余量 + 任务相关性) | 客服 | 分配这条路跑顺;见 T16 「加回一个入口的事」 |
......
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