Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
13 KiB
13 KiB
TASK · 生时校正采集流程重设计:用户先说、系统再从已说的事往下追、永远给结果(2026-09-10)
- 基线:
origin/staging@3cd7a95d(代码52db714b,已部署;含 BUG-635~645) - 分支:
codex/rectification-collection-redesign-20260910,基于origin/staging - 本单取代
TASK-rectification-exhausted-gate-exit-20260910.md(BUG-646/647)的决策 1、2、3;那份不再单独领取,其 BUG 编号并入本单。BUG-641/642 里「采集题带年龄段年份线索」的口径被本单推翻。 - 执行方:coding agent;验收:Claude
- BUG 编号起点:BUG-648(646/647 已占用,含在本单)
- Skill:本单改方法覆盖顺序与采集口径,升 10.0.23(部署后先验旧版本历史可打开,BUG-621)。
- 不改引擎计分、不改确认门(唯一分钟仍关)。
0. 产品口径(产品负责人 2026-09-10,原话要点)
- Agent 一上来就按领域一个个追问,年份还是拿生日推出来的;应该先等用户说出有价值的经历,再从这些经历往下问。
- 追问时不得说「任何领域」「领域不限」——用户不知道有哪些领域;要给具体例子。
- 生时校正必须走到结果:要么直接成功,要么细节不全就问用户补全、补完拿结果。不允许流程走完告诉用户「做不了」。
- (第二轮补充)材料不够门槛时继续收集,不出结果;不要固定题数,按收集到的信息达到阈值来收口。
1. 现状为什么违反这三条
| 现状 | 代码位置 | 违反 |
|---|---|---|
开场问完第一件,立刻按 DATED_COLLECT_ORDER(家人→学业→财务→迁居→健康→事业→感情)逐领域轮转 |
method-followup.ts nextDatedCollectFollowup(L1405 在覆盖链之前) |
口径 1:没等用户说完就开始盘问 |
轮转题干带「2020 年前后 / 2023 年前后」——这些年份来自 _age_band_year:出生年 + 该领域典型年龄段中点 |
event_probes.py DOMAIN_CATALOG.age_lo/age_hi、BUG-642 的 COLLECT_QUESTION_YEAR_CUE |
口径 1:「干凭生日来推」。这是我今天上午写进 BUG-642 的口径,错了,本单撤回 |
| 六个领域全「没有」后,出口只写「还差 1 件带月份的经历,领域不限」,不落焦点、无按钮 | persistExhaustionCollect、BUG-558/627 决策 |
口径 2、3:说了「领域不限」,且流程到此为止 |
| 三件带年月经历里一件被留作 holdout,训练门要 3 件,于是要 4 件才出第一道选择题;没有一句文案说明 | split-holdout.ts MIN_EVENTS_TO_RESERVE_HOLDOUT=2、MIN_ACCEPTANCE_EVENTS=3 |
口径 3:用户按要求给了 3 件,得到的是「还差 1 件」 |
门关时区间不可采用(selection_allowed = acceptance_allowed) |
decision_policy.py L681 |
口径 3:门关 = 没有结果 |
2. 目标流程(状态机,全部用阈值收口,没有固定题数)
S0 开场 ─▶ S1 收集(用户先说 → 系统按「信息价值」追问,直到 训练门开)─▶ S2 区分选择题(直到 收敛门开 或 增益见底)─▶ S3 交付
▲ │
└────────── 用户任何时候再补一件带年月的事,重算并回到 S2/S3 ◀────────────────────────────┘
两道门都是信息阈值,不是题数:
| 门 | 标准(现有常量,本单不改数值) | 达标前做什么 | 达标后做什么 |
|---|---|---|---|
| 训练门(能不能开始筛) | 带月经历 ≥ MIN_ACCEPTANCE_EVENTS=3 且种类 ≥ MIN_ACCEPTANCE_DOMAINS=2(trainingScoreableGate) |
继续收集(S1) | 进 S2 |
| 收敛门(能不能收口) | convergence-evaluator:领先 ≥ CONVERGENCE_LEAD=0.2、头部占比 ≥ CONVERGENCE_TOP_SHARE=0.7、稳定 STABLE_WINNER_ROUNDS=2;或剩余探针最高信息增益 < HIGH_INFORMATION_GAIN=0.08(连续 low_information 轮,即平台期) |
继续出选择题(S2) | 进 S3 交付 |
S0 开场(文案基本不变)
一句说明窗口与做法,一句「最后给区间和代表分钟」,一句邀请:「先说你最容易想起的一两件,比如上大学、第一份工作、搬到别的城市、谈恋爱或结婚、家里添丁或长辈住院、生病受伤,年月大概就行。」不写年份。
S1 收集:用户先说,系统按价值追问,问到训练门开为止
收集问题池由服务端按「信息价值」排序,每轮只问价值最高的一条;价值 = 这条问题能补上训练门缺口的概率 × 缺口权重。没有固定题数,停止条件只有两个:训练门开,或池空。
- 自由邀请(价值最高,只要还在产出就一直排第一):用户每说一批,batch 写入、复述,然后问一句「还有吗」,例子只列还没提过的种类、最多 4 个、用具体事物:「还有吗?比如第一份工作、搬到别的城市、谈恋爱或分手、家里添丁或长辈住院。」邀请的价值随产出衰减:上一轮邀请用户给了新事 → 价值不变;上一轮邀请用户答「没有了 / 就这些 / 记不清」→ 价值降到 0,从池里移除。
- 锚定追问(从用户已说的事派生,年份只来自用户):每个已记事件按
kind派生 1–2 条,题干必须带那件事的年份或名字,例如「2020 年毕业后第一份工作大概哪年开始?」「2016 年上大学是搬到别的城市住吗?」「后来是分开了还是结婚了,大概哪年?」「2024 年 10 月那次之后,工作或住处有没有变?」派生规则与模板放服务端(anchoredFollowups(evidence)),Agent 只做口语润色,不得改年份、不得加生日推出来的年份。价值 = 派生概率(预设表,例如「毕业→第一份工作」0.8,「上大学→搬城市」0.6)× 缺口权重(补新种类 1.0,补同种类 0.5)。答「没有 / 记不清」→ 该条移除,不重问。 - 通用补问(价值最低,只在前两类都空了才轮到):按种类问、不带年份、每种只问一次、例子具体:「工作上还记得哪年入职或换过工作吗?」用户已拒答的种类不再出现。
池空且训练门仍关(用户确实只有一两件):不出结果卡,也不说「做不了」。写一句精确缺口:「现在记下的是 2016 年上大学和 2020 年毕业两件,都是上学的事。再来一件不是上学的、记得大概年月的事就能开始筛,比如第一份工作哪年开始、哪年搬到别的城市、家里哪年添丁。」缺口句里的例子必须来自未覆盖的种类;Case 保持开放,输入框占位「再说一件带年月的事」,用户补一件即自动继续。这是唯一允许的「停在收集」状态,文案禁词见 §2 末。
S2 区分选择题:直到收敛门开或增益见底
- 现有逻辑(信息增益排序、±2/±1 计分、淘汰、平台期)不动,只把停止条件明确成:
converged为真,或剩余可问探针最高增益 <HIGH_INFORMATION_GAIN且连续low_information轮 ≥STABLE_WINNER_ROUNDS,或探针池空。DEFAULT_MAX_DISCRIMINATION_ROUNDS=8保留为兜底上限(防模型循环),不是设计目标。 - holdout 只在不影响筛选时保留:带月事件 ≥4 才留 1 件作 holdout;恰好 3 件时全部参与训练,回执
holdout.status="not_reserved_min_events",报告 Real Case Calibration 行写not reserved。确认门不变。 - S2 中途若用户补了新事 → 重算,回到 S2 顶部。
S3 交付结果
- 触发:收敛门开,或增益见底 / 探针池空(训练门已开是前提)。
- 内容:现有区间卡(
range_delivery三列)+ 一句定性 + 一句具体的「再补什么能收窄」:从收集池里价值最高的未答条目取,写成「如果还记得毕业后第一份工作是哪年开始的,范围还能再收一截」。绝不写「任何领域」「领域不限」「做不了」「材料不够」。 - 交付后 Case 不关:用户随时再补一件带年月的事,重算并更新卡片。
- 不做「暂定采用」(第二轮口径撤回):训练门没开就不出卡,采用门维持原样。
文案硬规则
- 用户可见文案禁止:领域、任何领域、领域不限、方法覆盖、分盘、探针、训练门、holdout、做不了、材料不够、还差 N 件。
- 每个「还有吗」「再补什么」「精确缺口句」都必须带 ≥2 个具体例子,例子只从未覆盖的种类里选。
- 静态词表合同(
agent-voice-copy-contract)加以上禁词。
3. 决策记录
- 撤回 BUG-642「采集题带年龄段年份线索」:
COLLECT_QUESTION_YEAR_CUE删除;evidence_collection_probes的 age_band 年份只用于内部排序,不进任何题干。 - 撤回 BUG-646 单决策 2(终态 = 门槛句 + 先这样按钮)与决策 3(穷尽后释放 holdout):终态改为 S3 交付卡;holdout 改为「≥4 件才留」的静态规则。
- 撤回本单第一版的「≤2 轮邀请 / ≤4 问」固定题数与「暂定采用」:收口只看训练门与收敛门两个阈值;
DEFAULT_MAX_DISCRIMINATION_ROUNDS只作防循环兜底。BUG-558 的防复发(不得无限重复同一句)由「池内每条只问一次、答否即移除」保证,不再靠题数。 - 训练门没开不出结果卡,也不出「做不了」:只出精确缺口句并保持开放。
- Skill 10.0.23:把「先走完方法覆盖(感情 → 事业 → 家人 → 职业 → 占问),再对已覆盖领域做精度追问」改为本单 S1→S2→S3。
4. 硬红线
- 不得用出生年推年份写进题干(
grep -n "年前后" user-copy.ts只允许出现在带用户年份的锚定模板里)。 - 收集池「每条只问一次、答否即移除」必须是代码不变量并有测试;停止条件只能引用 §2 表里的门与常量,不得新增题数常量。
- 训练门关时不得写
range_delivery、不得出卡、不得出现禁词。 - 既有测试总数不降;被本单推翻的断言(BUG-642 年份线索、BUG-558「不落焦点」)逐条写原值 / 新值 / 原因。
- Bug 历史与测试不得含真实用户资料。
5. 任务分解
- T1 收集问题池与价值排序(BUG-648):
method-followup.ts新增collectionQuestionPool(evidence, declinedTopics, inviteHistory)→ 按价值排序的{kind: "invite" | "anchor" | "generic", value, prompt, key};next_followup取首条;invite_history与已答键落 conversation summary。验收:用户连续两轮给新事 → 邀请仍在首位;一轮答「没有了」→ 邀请移除、首条变锚定;锚定答「没有」→ 不重问;池空且门关 → 无焦点、精确缺口句含 ≥2 未覆盖种类例子、无禁词。 - T2 锚定追问模板与派生表(BUG-648):
anchoredFollowups(evidence)+ 概率表;焦点 idcollect:anchor:<kind>:<year>。验收:学业两件 → 首条「2020 年毕业后第一份工作」;题干含用户年份、不含 age_band 年份。 - T3 删年份线索,通用补问去年份(撤回 BUG-642):删
COLLECT_QUESTION_YEAR_CUE;USER_COLLECT_QUESTION只在 generic 层使用。验收:rectification-spoken-collect.test.ts相关断言改写并注明。 - T4 holdout 规则(BUG-647):训练 ≥3 后才留;回执状态;报告行。验收:3 件全训练、4 件留 1。
- T5 S2 停止条件与 S3 交付(BUG-646):
decideRectification的交付触发改为收敛门 / 增益见底 / 探针池空;persistExhaustionCollect在训练门关时只写精确缺口句(preciseGapNarration(evidence, pool)),门开时走交付卡 + 「再补什么」句;客户端对「停在收集」状态不显示「没有拿到下一个问题」,显示缺口句与占位「再说一件带年月的事」。验收:2 件学业 + 全拒答 → 缺口句、无卡、无禁词、无缺口提示;3 件 2 类 + 探针增益见底 → 交付卡 + 再补句。 - T6 Skill 10.0.23 + 词表合同 + 记录:SKILL.md、registry、
case-status.ts、CHANGELOG、BUG-646~648 历史、docs/testing/真人清单三条(只有学业路径、正常路径、中途补事路径)。
6. 让步顺序
T2 的概率表可先用常量 0.5 统一(写进度记录);T1 + T3 + T4 + T5 不可省。
7. 开工前置命令
git fetch origin --prune
git worktree add -b codex/rectification-collection-redesign-20260910 .worktrees/rectification-collection-redesign-20260910 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | tail -1
cd frontend && npm test -- tests/rectification-spoken-collect.test.ts tests/rectification-exhaustion-exit-20260906.test.ts tests/rectification-surface-state.test.ts tests/skill-registry.test.ts
8. 验收口径
- 前端
tsc --noEmit0 错、npm run lint0 error、相关套件 fail=0、测试总数 ≥ 基线;next build后/仍 Static、首屏 gzip ±2%。 - Python 定向 +
run_quality_gate.py --profile quick。 - 部署后先打开一个绑定 10.0.22 的历史校正,再做三条真人清单。