Files
Jyotisha/docs/tasks/TASK-rectification-explain-layer-20260906.md
T

8.2 KiB
Raw Blame History

TASK · 生时校正过程解释层:每题"为什么问、答了会怎样",每轮"现在第几步、为什么、下一步"2026-09-06

  • 基线:origin/staging @ b938c76a(文档头 211f9bb2
  • 分支:codex/rectification-explain-layer-20260906
  • 执行方:coding agent;验收:Claude
  • 串行:排在 TASK-rectification-convergence-exit-fix-20260906.md 之后、TASK-rectification-range-reading-20260906.md 之前(都改 answer-choice.ts / rectification-agentic-chat.tsx
  • 涉及文件:frontend/src/lib/rectification-agentic/v9/choice-card.tsprobe-question-contract.tschoice-action.tsanswer-choice.tsmethod-followup.tscase-service.ts(快照投影)、frontend/src/lib/rectification-candidate-result.tsfrontend/src/components/rectification-choice-card.tsxrectification-agentic-chat.tsxfrontend/src/lib/rectification-agentic/user-copy.ts
  • 不改:Python 探针契约(contracts/probe-question-v1.json 字节不变)、任何门、SKILL.md
  • BUG 编号:本单是能力补齐,不预留;执行中发现缺陷再按当时最大号续

0. 为什么做这件事

产品负责人 9-06 实测的原话是"用户也不知道怎么做,就卡在这里"。修复单堵的是出口,这一单补的是过程可见性:上游 yinduzhanxing 旧校时工作台的 buildRectificationAIContext()(当前阶段 → priority action → reason → next step)、题库里的 why_this_question / answer_impact,是它相对我们唯一明显更强的业务层。我们的数据都有,只是没投影给用户。

1. 现状实证

# 事实 位置
1 点选卡的 why = probe.user_meaning(引擎给模型的作题简报,如"时间范围锁定…领域锁定…语义目标是…"),经 engineMeaningToDisplayCopy 过滤后基本为空,用户看不到"为什么问" choice-card.ts::hypothesisFor L73rectification-choice-card.tsx L63
2 答完只回固定句"已记录你的选择,并更新了候选比较",虽然 inference_state.rounds[].score_deltascredible_range 前后值都在 choice-action.ts::composeChoiceNarration L94116build-state.ts rounds
3 每轮只有一句"本轮对照了 D1、D9…"(技法清单),没有"现在在哪一步 / 为什么 / 下一步" rectification-varga-sentence.ts L33receipt methods
4 服务端其实已经知道阶段:methods 覆盖表、decision.nextActionprecision_stage.currentstopReason method-followup.ts plan、rectification-decision.ts
5 看盘板(rectification-board)已经是"专业视图"(宫位表、换升时刻),不需要再做双模式开关 rectification-board.tsx

2. 决策记录

  1. 解释文案全部服务端生成,模型只写题干;不得让模型解释分数或候选变化(防止编造)。
  2. "为什么问这题":从探针数据生成一句:{period} 这段经历能把当前 {n} 段候选分成两组({tracks 中文})varga_style 卡写"这题对照 {D9/D10} 的类型差异"。挂在卡片题干下方,默认折叠为"为什么问这题",点开显示。
  3. "答了会怎样":每个选项一句,来自 expected_outcomesA/B → "会让 {supports 段落} 领先、{conflicts 段落} 落后"C → 反向;D → "不计分,换一题"。段落用可信区间口径("04:3104:39 这段"),不用概率词。放在卡片底部一行小字,随选项高亮。
  4. 答后旁白改为三段式(服务端拼):"{已记录}。{哪段升/降,来自 score_deltas 聚合到簇}。{范围从 X 收到 Y / 范围没变}。" unsure 保持"这题先不计分,换一件事问"。
  5. 步骤条case 快照新增 step_state = { stage: "collect"|"discriminate"|"deliver"|"post_adopt", index: 1..4, headline, reason, next },服务端从 methods / decision.nextAction / precision_stage / stopReason 派生(纯函数,放 frontend/src/lib/rectification-agentic/v9/step-state.ts)。UI 固定显示在输入框上方、与口述态停止按钮同一行左侧;每轮重算。文案例:"第 2 步·区分候选 — 带年月的经历已经够了,现在用几道选择题分开相邻的候选 — 下一步:答完当前这题,或者按'先这样'看结果"。
  6. 不做 guided/professional 双视图;看盘板就是专业视图。

3. 硬红线

  • BUG-390 四选项契约不变;contracts/probe-question-v1.json 字节不变;新增字段只在 TS 的 RectificationChoiceFrame / RectificationChoiceCardwhy_useranswer_impact),Python 不改。
  • 不得在用户可见文案里出现"概率 / 置信度 / 确定"字样(agent-voice-copy-contract 已有守卫,新文案要过它)。
  • 答后旁白的段落只能来自 score_deltascredible_range 前后值;范围没变时必须如实说"范围没变"。
  • page.tsx 不增长;新逻辑进 lib/ 或组件。
  • 既有断言改动写三栏。

4. 任务分解

B1 卡片解释字段

  • choice-card.ts::buildChoiceFrame 新增 why_user(决策 2)与 answer_impact: {A,B,C,D}(决策 3);生成函数放 probe-explain.ts(纯函数,输入 probe + 活跃候选 + 簇范围)。
  • rectification-choice-card.tsx:折叠"为什么问这题";选项 hover/选中时显示对应 impact 行。DESIGN.md 记录。
  • 验收:rectification-choice-card.test.ts 新增——existence 探针生成的 why_user 含年月与"分成两组"answer_impact.A 含 supports 段落的时间;varga_style 卡 why_user 含分盘名;文案守卫通过。

B2 答后旁白

  • composeChoiceNarration 接收 {deltasByCluster, rangeBefore, rangeAfter}persistApplied 在应用推断后计算并传入(scoreDeltas + clusterRangeFor 已有)。
  • 验收:rectification-answer-choice.test.ts 新增——答 A 后旁白含"领先 / 落后"与"范围从 … 收到 …";范围不变时含"范围没变";答 D 不变。

B3 步骤条

  • step-state.ts 纯函数 + 单测(四个阶段各一例,含 probe_pool_exhausteduser_uncertainty_too_high 的 reason 文案);case-service.ts 快照投影加 step_staterectification-agentic-chat.tsx 渲染;rectification-candidate-result.ts 解析。
  • 验收:快照契约测试(rectification-v9-contracts.test.ts)含 step_state;组件源扫描断言存在 rectification-step-state

B4 记录

  • CHANGELOG.mdPROGRESS-rectification-explain-layer-20260906.mddocs/testing/rectification-explain-layer-20260906.md(真实环境:每张卡能展开"为什么问";答完一题看到哪段升降与范围变化;每轮顶部有第 N 步 / 原因 / 下一步);frontend/DESIGN.mdfrontend/docs/VOICE.md

5. 让步顺序

B3 步骤条最便宜、最直接回应"不知道下一步",先做;B1、B2 其次;B4 不可省。

6. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-explain-layer-20260906 .worktrees/rectification-explain-layer-20260906 origin/staging
cd .worktrees/rectification-explain-layer-20260906
ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules
cd frontend && 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)"

验收(Claude2026-09-07origin/staging @ 814c924e

结论
B1 卡片 why_user / answer_impact 通过。服务端生成,折叠展示,选项 hover/选中显示影响句
B2 答后旁白 未通过(P1BUG-569applyRectificationChoice 传给 composeChoiceNarrationrangeBefore/rangeAfter 取的是 InferenceState.range_start/range_end——那是候选搜索窗口,答题从不改变;真正收窄的是 credible_range。线上每一题都会说「范围没变」,与顶部只读范围矛盾。单测之所以过,是因为直接给 composeChoiceNarration 喂了两个不同的范围
B3 步骤条 通过。四段派生纯函数 + 快照投影 + UI
B4 记录 通过
文案 P2why_user 里把 Vimshottari / Narayana 写成「毗湿奴多利、那罗延」,产品其他位置(rectification-varga-sentence.ts)一律用 Vimshottari / Narayana,见修复单