docs(tasks): brief BUG-633/634 evidence-turn empty answer and choice narration
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
719ff55a09
commit
31f2a722aa
@@ -94,6 +94,7 @@
|
||||
| `TASK-rectification-domain-alias-audit-20260909.md` | `PROGRESS-rectification-domain-alias-audit-20260909.md` | 领域命名审计:健康线 `health`(账本/焦点)与 `health_pressure`(计划/引擎)在十处比较里六处未归并(holdout declined、reverse-verify、conflict probes、probeYearAlreadyCovered、引擎 oos_blind_prompts / _event_years / volunteered);职业线焦点存为 `other` 只靠 questionId 兜底。其余六领域三层同名无问题。决策:TS/Python 各一个归并函数 + 源码合同禁字面量比较 | 待执行 | `codex/rectification-domain-alias-audit-20260909`(BUG-628) |
|
||||
| `TASK-rectification-yearless-probe-downgrade-20260909.md` | `PROGRESS-rectification-yearless-probe-downgrade-20260909.md` | 产品拍板:无年月性格题(D9/D10 风格、月宿边界)降级为平局裁决——带年月题问完且候选仍分不开才问,分值减半(±1)、不计淘汰,报告标「参考」;三列卡性格描述不动;新增离线命中率测量脚本 + 导出 SQL(有出生证用户,聚合);Skill 10.0.21 | 已验收通过 | `fef54b6e`(BUG-629,Skill 10.0.21) |
|
||||
| `TASK-rectification-followups-20260909.md` | `PROGRESS-rectification-followups-20260909.md` | 验收补漏:申报时段拦截只看钟点样式,带钟点的经历(『20:00 左右分手』『3 点到 5 点被车撞』)会被吞(BUG-631);by_time 只算引擎前 9 个候选,一小时窗 17 个候选时卡片列写『还没对照』(BUG-632) | 待验收 | `codex/rectification-followups-20260909`(BUG-631~632) |
|
||||
| `TASK-rectification-evidence-turn-empty-answer-20260910.md` | `PROGRESS-rectification-evidence-turn-empty-answer-20260910.md` | 证据轮模型无正文被判整轮失败:证据、评分、下一问都已落库却只剩『没有拿到下一个问题』(BUG-633);答题旁白只说『范围没变』、时间线写死『还在收窄』(BUG-634) | 待执行 |
|
||||
|
||||
### 聊天主链路与首页
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# TASK · 证据轮模型无正文被判整轮失败:证据与下一问都已落库,却只剩"没有拿到下一个问题";答题旁白只会说"范围没变"(2026-09-10)
|
||||
|
||||
- 基线:`origin/staging` @ `719ff55a`(BUG-631/632 已合入并部署)
|
||||
- 分支:`codex/rectification-evidence-turn-empty-answer-20260910`,基于 `origin/staging`
|
||||
- 串行:在 `TASK-rectification-skipped-health-deadend-20260909.md`(BUG-626/627,改 `answer-choice.ts` / `turn-exit.ts` / 客户端"重新加载")之后开工;若那份尚未领取,本单先做,那份的决策 5 改为在本单的客户端改动上叠加。
|
||||
- 执行方:coding agent;验收:Claude
|
||||
- 涉及文件:`frontend/src/lib/rectification-agentic/v9/agent-run.ts`(`RETRYABLE_ERROR_CODES` L144、`streamAttempt` 收尾 L960–995、成功收尾 L560–620)、`v9/run-diagnostic.ts`、`v9/answer-choice.ts`(`composeChoiceNarration` 调用 L551/582/603)、`v9/choice-action.ts`(`composeChoiceNarration` L98–123)、`v9/probe-explain.ts`(`explainScoreMovement` L215)、`frontend/src/components/rectification-agentic-chat.tsx`(`run.failed` 分支 L888、失败收尾 L970–985、`liveQuestionOnMessages` L1474、`questionGap` L1486、`RectificationReadonlyRange` L178–187、输入框占位 L1776–1782)、`frontend/src/lib/rectification-surface-state.ts`(`rectificationQuestionGapState` L278)
|
||||
- BUG 编号起点:**BUG-633**(`docs/BUG_HISTORY.md` 当前最大 BUG-632)
|
||||
- 优先级:BUG-633 **P1**(证据轮直接断流,用户只能刷新或新建);BUG-634 **P2**(五题答完每次都是"范围没变",用户以为坏了)
|
||||
|
||||
## 1. 事故实证(staging 2026-09-10 10:04 +0800,Skill 10.0.21;只写结构)
|
||||
|
||||
顺序:开场 → 学业两件 → 感情两件 → 三道带年月选择题(事业 / 迁居 / 事业)→ D9 风格题 → 家人题「记不得」→ 财务一件 → 事业两件 → D10 风格题 → 迁居采集题 → 用户答"某年某月搬到某市" → **用户气泡下面直接是"没有拿到下一个问题。",再下面是"本轮没有生成可展示的回复,状态已记录。"**
|
||||
|
||||
Case JSON 里这一轮:
|
||||
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 用户 turn `status` | `failed`;`phases` = `run.started, skill.bound, case.loaded, intent.classified, evidence.proposed, run.failed`;没有 `answer.composed` |
|
||||
| `tool_activities` | `rectification-read-case` completed、`rectification-record-evidence-batch` completed(22 073 ms,13 个方法);**没有 `rectification-set-focus`** |
|
||||
| `evidence` | 迁居事件已 `confirmed`,`sourceTurnId` = 这一轮 |
|
||||
| `latest_result` | 新的 `resultId`,`createdAt` 在 batch 结束同一秒;`inference_state` 5 轮答题全部重放(BUG-587/594 正常) |
|
||||
| `current_question` | `collect:health_pressure:collect_method_evidence`,`kind=collect_spoken`,`question_source=focus`——**下一问已经落库** |
|
||||
| `turns[].question` | 这一轮为 `null`(没有 assistant 行可挂) |
|
||||
| 客户端 | 失败消息文案对应 `run-diagnostic.ts` 的 `empty_stream`;问题缺口显示"没有拿到下一个问题"+"重新加载";输入框占位是"请回答上面的问题…",而上面没有问题 |
|
||||
|
||||
## 2. 根因
|
||||
|
||||
### 2.1 BUG-633(P1):模型在 batch 之后没写正文,服务端把整轮判失败,不回捞已落库的东西
|
||||
|
||||
1. `record-evidence-batch` 工具内部已完成:写证据 → 重算(带 `previous` 重放)→ `persistPlanFocus` 落下健康采集焦点(`askedTurnId` = 本轮)。工具返回后模型既没调 `set-focus` 写 `spokenPrompt`,也没写"记下了:…"一句,直接以 `stop` 结束。`streamAttempt` L993 `!answerText.trim()` → `empty_stream`。
|
||||
2. `empty_stream` 不在 `RETRYABLE_ERROR_CODES`(`fe87a9ec` 于 2026-08-24 移除,原因是重试会重放证据)。于是 `finalizeTurn("failed", null, …)`,不计费,发 `run.failed`。
|
||||
3. 失败路径不经过 `finalizeSuccessfulTurnExit` / `ensureNonTerminalTurnExit`(路由 L899 只在 `result.ok` 时调),也不会用主持人旁白把 batch 已返回的事件复述写成 host turn。**证据、评分、焦点三样都已提交,唯独用户看不到。**
|
||||
4. 客户端 `run.failed` 后不调 `loadCaseSnapshot`(L975–990 只在 `succeeded` 时刷新);即便刷新,`liveQuestionOnMessages` 要求某条 settled assistant 消息带同 `focus_id` 的 `question`,失败轮没有 assistant 行 → `questionGap` 走 `preparing` → 两次轮询 → `unavailable`。"重新加载"只是再取一次快照,结果相同。整页刷新同理。
|
||||
5. 模型为什么不写正文,staging 没有本轮的 run diagnostic 可查(`RectificationRunDiagnostic` 只在内存,未落库),无法从代码侧确定;前三轮同形状(read-case → batch → set-focus → 正文)都成功。已知的候选原因:thinking 打开后模型把承接句只写进 `reasoning-delta`(BUG-359 的镜像);或模型把正文写在调 batch 的同一 step 里,`step-answer.ts` 在非 set-focus 的公开工具调用时 `retractLive` 丢掉了它。两种情况都不该让用户承担。
|
||||
|
||||
### 2.2 BUG-634(P2):答题旁白只会说"范围没变",时间线说"还在收窄",两句都没有信息
|
||||
|
||||
本例五道选择题答完,旁白五次"已记录,范围没变。",时间线一直"目前范围 04:45–05:15,还在收窄"。算术上这是正确的,但不是用户能理解的:
|
||||
|
||||
| 簇 | 答题前 | 答题后 | 强冲突数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 04:53 | 11 | 15 | 1 |
|
||||
| 04:50 | 9 | 11 | 1 |
|
||||
| 05:00 | 13 | 11 | 2 |
|
||||
| 04:54 | 10 | 10 | 2 |
|
||||
| 05:06 | 12 | 10 | 2 |
|
||||
| 05:15 | 10 | 10 | 1 |
|
||||
| 04:47 | 11 | 9 | 2 |
|
||||
| 05:08 | 12 | 8 | 2 |
|
||||
| 05:14 | 12 | 8 | 2 |
|
||||
|
||||
- 可信区间规则(`credible-range.ts::unionStillValidRange`):落后峰值不足 `MIN_SEPARATION_LEAD = 8` 的簇都保留。峰 15,最低 8,差 7,九个簇全留 → 区间等于整个搜索窗。
|
||||
- 淘汰规则(`apply-probe-outcome.ts`):`STRONG_CONFLICT_ELIMINATION_COUNT = 3`;带年月题 ±2,性格题 ±1 且不计冲突。三道带年月题的答案两两指向不同区段(第一题支持 04:48–04:59+05:15,第二题支持 04:45–04:53,第三题支持 05:00–05:15),没有任何一分钟同时满足三题,每簇最多 2 次冲突,谁也淘汰不了。
|
||||
- 引擎给的带年月探针一共只有这三道,全部答完;其余全是无年月的分盘对照题(`yearless_ungrounded_contrast`,BUG-629 已降级)。**从此刻起,选择题已经不可能再收窄区间,只有新的带年月经历能改变分数。** 这不是 bug,是数据本身分不开,但产品必须把这件事说出来。
|
||||
- `choice-action.ts::composeChoiceNarration` 收了 `deltasByCluster`,正文却只用 `explainRangeChange`;`probe-explain.ts::explainScoreMovement` 能生成"04:52–04:53 领先,05:08–05:12 落后",从没被说出来。`RectificationReadonlyRange` 的"还在收窄"是写死的。
|
||||
|
||||
## 3. 决策记录(产品负责人 2026-09-10 授权诊断;以下为 Claude 建议,执行前默认生效,用户可否决)
|
||||
|
||||
1. **证据轮"模型无正文"不是失败。** 只要本 attempt 内 `rectification-record-evidence-batch` 已 `completed`(`toolTerminalStatus`),`empty_stream` / `max_steps` / `answer_truncated` 三种收尾都改走**主持人兜底**:服务端用 batch 已返回的事件复述(`display_date_label` + 事件短语)组成"记下了:…。",作为本轮 assistant 正文 `finalizeTurn("completed", …)`,正常结算计费(评分和下一问都已经算出来了),再走 `finalizeSuccessfulTurnExit`。焦点已由工具落库,`askedTurnId` 就是本轮,问题自然挂上。
|
||||
2. **没有任何公开写工具完成时,`empty_stream` 允许重试一次。** `RETRYABLE_ERROR_CODES` 不整体放开;在 `streamAttempt` 收尾处判断"本 attempt 没有 batch / set-focus / compare 任何一个 completed"才返回 `retryable`,否则维持 `failed`。这样不会重放证据(`fe87a9ec` 的顾虑仍然成立)。
|
||||
3. **失败也刷新快照,问题不靠消息承载。** 客户端 `run.failed` 分支之后照样 `loadCaseSnapshot`;`rectificationQuestionGapState` 新增输入 `questionPersisted`(`current_question` 非空且 `question_source === "focus"`):为真时不进 `preparing / unavailable`,改渲染一条主持人问题行(复用 collect_spoken 的问题段样式,`focus_id` 取 `current_question`),输入框占位才允许写"请回答上面的问题…"。这一条同时替代 BUG-626 决策 5 里"修复"按钮的大半场景;那份任务书的 `repair-exit` 路由仍保留给"穷尽且无载体"的情况。
|
||||
4. **答题旁白必须带进度。** `composeChoiceNarration` 在 `appliedInference` 且范围不变时,输出 `explainScoreMovement(deltasByCluster)` 的领先/落后句,格式"已记录,范围没变;04:52–04:53 领先,05:08–05:12 落后。";连领先/落后都没有(全零)才只说"范围没变"。
|
||||
5. **时间线不得写死"还在收窄"。** 文案由快照决定:`discriminating_event_probes` 为空且 `candidate_contrast_packet.probes` 里没有带年月项 → "目前范围 X–Y,选择题已问完,再补带年月的经历才会变";否则 "目前范围 X–Y,还在核对"。不写"收窄"。
|
||||
6. **`RectificationRunDiagnostic` 落日志。** 每个 attempt 收尾把 diagnostic(finishReason、stepCount、lastCompletedTool、stateMutationCommitted、tokens)写一条 `console.info` JSON,便于下次从 staging 容器日志定位模型侧原因。不落库、不加迁移。
|
||||
7. 不动采用门、确认门、`MIN_SEPARATION_LEAD`、`STRONG_CONFLICT_ELIMINATION_COUNT`、`PROBE_WEIGHT`、`minute_step=1` 指纹;Skill 不 bump。
|
||||
|
||||
## 4. 硬红线
|
||||
|
||||
- 主持人兜底正文只能来自 batch 工具的返回(事件复述),不得让服务端另起模型调用补写,不得引用用户原话之外的年份。
|
||||
- 兜底轮的 receipt 必须能看出是兜底:`phases` 加 `answer.host_fallback`,公开 receipt 里 `answer_origin = "host_fallback"`(`message-origin.ts` 已有 origin 枚举,扩一个值)。
|
||||
- 重试只在"零公开写工具完成"时发生;有任何一个写工具完成而重试,即视为 BUG-186 复发。
|
||||
- 不得回到"问题靠正则从正文猜"的路子;主持人问题行的 `focus_id` 只能来自 `current_question`。
|
||||
- 时间线与旁白的数字只来自快照 / receipt,不得自算。
|
||||
|
||||
## 5. 任务分解
|
||||
|
||||
### 5.1 BUG-633 服务端(决策 1、2、6)
|
||||
- 验收(`frontend/tests/rectification-v9-stream.test.ts` 扩):(a) 模拟 stream:read-case → batch completed(返回两件事件复述)→ `finish(stop)` 无 text → 结果 `ok=true`,assistant 正文等于"记下了:<复述>。",`phases` 含 `answer.host_fallback`,计费 `complete` 被调用一次;(b) 同形状但 batch 未调用 → 第一 attempt `retryable`,第二 attempt 正常;(c) batch completed 后 `finish(length)` → 同 (a);(d) 现有三条 `errorCode === "empty_stream"` 断言逐条写"原值 / 新值 / 原因"。
|
||||
- `rectification-agentic-entry.test.ts` L273 的源码断言按新路径改写,说明三栏。
|
||||
|
||||
### 5.2 BUG-633 客户端(决策 3)
|
||||
- 验收:`rectification-surface-state` 用例——`questionPersisted=true`、无消息承载 → 状态 `persisted_question`,不是 `preparing`;组件合同——失败轮之后快照带 `current_question(kind=collect_spoken)` 时,DOM 出现主持人问题行且输入框占位为"请回答上面的问题…";快照无问题时占位不得写"上面的问题"。
|
||||
|
||||
### 5.3 BUG-634(决策 4、5)
|
||||
- 验收:`composeChoiceNarration` 用例——范围不变、deltas 非零 → 含"领先";全零 → "已记录,范围没变。";`RectificationReadonlyRange` 合同——探针空时文案含"选择题已问完",否则含"还在核对",两种都不含"收窄"。
|
||||
- `agent-voice-copy-contract` 把"还在收窄"列入禁用词。
|
||||
|
||||
### 5.4 记录
|
||||
- `docs/BUG_HISTORY.md` BUG-633(关联 BUG-186、BUG-359、BUG-558、BUG-627;说明 `fe87a9ec` 为何把 `empty_stream` 移出可重试,以及本次为何只在零写工具时放回)、BUG-634(关联 BUG-593、BUG-629);`CHANGELOG.md`;`PROGRESS-rectification-evidence-turn-empty-answer-20260910.md`;`docs/testing/rectification-scenarios-20260907.md` 加"证据轮无正文时必须出现『记下了』兜底句和下一问"。
|
||||
|
||||
## 6. 让步顺序
|
||||
|
||||
5.1 决策 1 先做、当天部署;5.2 必做;5.1 决策 2 与 5.3 可后置一天;5.4 不可省。
|
||||
|
||||
## 7. 开工前置命令
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/rectification-evidence-turn-empty-answer-20260910 .worktrees/rectification-evidence-turn-empty-answer-20260910 origin/staging
|
||||
cd .worktrees/rectification-evidence-turn-empty-answer-20260910
|
||||
ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules
|
||||
cd frontend && ./node_modules/.bin/tsc --noEmit; npm run lint 2>&1 | tail -1
|
||||
ls tests/rectification-*.test.ts tests/agent-voice-copy-contract.test.ts | grep -v database | xargs npx tsx --test 2>&1 | grep -E "^# (tests|pass|fail)"
|
||||
```
|
||||
Reference in New Issue
Block a user