Files
Jyotisha/docs/tasks/TASK-rectification-quality-probe-dedupe-20260904.md
T

82 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TASK · 校正候选区分阶段同一道"上大学"题问两次(2026-09-04
- 基线:`origin/staging` @ `a75929c1`(含 `d4627a50` 采用后核对线修复;本任务书不依赖其验收结论)
- 分支:`codex/rectification-quality-probe-dedupe-20260904`
- 执行方:coding agent;验收:Claude
- 涉及文件:`scripts/rectification/event_probes.py``frontend/src/lib/rectification-agentic/v9/choice-card.ts`;与 `TASK-rectification-post-adopt-verify-20260904.md`(已实现 `d4627a50`)无文件冲突,可并行。
- BUG 编号起点:**BUG-540**(开工时用 `grep -o "BUG-5[0-9][0-9]" docs/BUG_HISTORY.md | sort -u | tail -1` 复核)
## 1. 事故实证(staging2026-09-04,候选区分阶段)
用户在同一案例里连续两次看到同一张选择卡:"**YYYY 年 M 月那次上大学,更接近如愿、将就调剂、发挥失常还是说不清**",四个选项完全相同。用户两次都作答,第二次答完范围继续收窄。案例 dump 中可对上的事实(不写具体值):
| # | 事实 | 位置 |
| --- | --- | --- |
| A | 该案例学业域有两条已核实事件:**入学**(`education_start`)与**毕业**`education_completion`),相隔四年 | 案例 `evidence[]` |
| B | 引擎对这两条事件各发了一条 `known_event_quality` 探针;两条的 `expected_outcomes` 的 yes/no 候选分钟集合**完全相同**(同一 D24 星座切分),只有 `semantic_key``education.<入学年>.known_event_quality` / `education.<毕业年>.known_event_quality`)、`target_evidence_id``candidate_split_hash` 不同 | `scripts/rectification/event_probes.py::_quality_distinguish_probes`L901997)、`candidate_contrast.py::candidate_split_hash`hash 把 `year/month` 掺进去,所以同分组不同年 ≠ 同一 hash) |
| C | 第二张卡的 `question_id` / `probe_id` / 评分都指向**毕业**探针,但卡面题干与日期标签是**入学**探针的 | `choice-card.ts::pickProbe`L233251):`followup.choice_kind === "event_quality"` 分支取 `pool` 里**第一条**质量探针就返回,`semantic_key` 匹配写在它后面,永远轮不到;`periodFor`L254)与 `eventQuestionPrompt`L280)都从 `pickProbe` 拿探针,所以日期与题干一起错 |
| D | 即便 C 修好,毕业事件的题干仍会是"YYYY 年 6 月那次**上大学**……"、选项仍是"发挥明显失常 / 只是将就调剂 / 基本如愿录取 / 当时说不清楚" | `event_probes.py::_quality_user_meaning`L887)只按 `domain == "education"` 套"上大学"模板;`QUALITY_DISTINGUISH_OPTIONS["education"]`L92)同样不看事件 `kind` |
## 2. 根因
1. **前端选探针的优先级反了**`pickProbe` 先按"种类"再按"键"。同域只有一条质量探针时看不出来;一旦引擎给了两条,第二次 followup 的卡面永远渲染第一条。用户第二次实际是在**对着入学题、给毕业探针打分**——答案被记到了错的证据上。
2. **引擎对学业域所有事件一律发"高考发挥"题**:质量探针的题干与选项是 BUG-390 时按"高考/录取"设计的,那时只有 `education_start` 一种学业事件会到这里;现在采集线会产生 `education_completion`(毕业)等其它 kind,模板没有跟着分。毕业没有"调剂/发挥失常"这种体验,这道题对毕业事件**没有可回答的语义**。
3. **同分组质量探针没有去重**:两条探针的 yes/no 分钟集合一模一样,问第二道不带来任何新信息(信息增益相同、支持/冲突集合相同),却占了 `MAX_QUALITY_DISTINGUISH_PROBES = 2` 的全部名额和用户一轮回答。`candidate_split_hash` 因为掺了年份而不相等,`askedDiscriminatorKeys` 的 split-hash 去重拦不住。
## 3. 决策记录
1. **一道题只能绑定一条探针,且必须是 followup 指定的那条**`pickProbe` 的匹配顺序改为 `probe_id``semantic_key` → 种类兜底。找不到 `semantic_key` 对应探针时不得退回"同种类第一条",改为返回 `null`(让上游按无探针路径走,不出卡),并在进度记录里说明这种情况是否在测试中出现过。
2. **质量探针只对有"体验词表"的事件种类发**:学业域仅 `education_start``education_change``education_interruption` 可发("如愿 / 调剂 / 失常"对这三类都有意义);`education_completion` 与其它 kind **不发**质量探针,而不是新造一套"毕业顺不顺"的模板。理由:产品要求"多余入口宁可删除也不修";毕业是否顺利本身对 D24 切分没有独立证据价值(与入学事件是同一切分)。
3. **同域同分组只发一条**`_quality_distinguish_probes` 内对 `(domain, frozenset(yes_times), frozenset(no_times))` 去重,保留时间最早(或 `information_gain` 最高、并列取最早)的一条;被去掉的事件不计入 `MAX_QUALITY_DISTINGUISH_PROBES` 名额。
4. **已经答过的错绑答案不追溯修正**。已有案例的历史轮次不回写;只保证新的轮次不再出错。若要为受影响案例做补救,另开任务书。
5. 本任务书不推翻 BUG-389/390/391/392/393 的任何防复发措施;BUG-390 的"质量探针只对学业发出、不得把高考题干套到事业/感情"仍然成立,本次是把它进一步收紧到学业内的特定 kind。
## 4. 硬红线
- 不得改 `QUALITY_DISTINGUISH_OPTIONS` 四选项的文案与 `answer_class` 映射(BUG-390 / 四选项合同 `tests/test_probe_question_contract.py``frontend/tests/rectification-probe-question-contract.test.ts`)。
- 不得新增第二套"毕业体验"模板或选项。
- 不得改 `candidate_split_hash` 的算法(其它模块靠它做 asked-key 去重与回执投影);去重在 `_quality_distinguish_probes` 内部按分组集合做。
- `pickProbe` 改动不得影响 `varga_style` / `existence` 探针的现有选取(`rectification-choice-card.test.ts` 既有断言原样通过;如需改断言写"原值 / 新值 / 原因")。
- 任务书、进度记录、Bug 历史、测试 fixture 一律用抽象年份与虚构事件;不得出现真实 case_id / session_id / 出生候选分钟。
## 5. 任务分解
### 5.1 `pickProbe` 按键选探针(前端)
-`choice-card.ts::pickProbe`:顺序 `followup.probe_id``ChoiceCardFollowup` 需补 `probe_id?: string`,由 `method-followup.ts` 的 followup 透传,见其 L172 已有字段)→ `followup.semantic_key` → 无键时才按 `choice_kind` 兜底;有键但找不到 → `null`
- 验收:`frontend/tests/rectification-choice-card.test.ts` 新增用例——同域两条 `known_event_quality` 探针(不同 `semantic_key` / `target_evidence_id` / `display_date_label`,相同 expected_outcomes),followup 指向第二条时,`buildChoiceFrame``question_id`、题干日期标签、`user_meaning` 全部来自第二条;反向再断言指向第一条时不受影响。
### 5.2 质量探针按事件 kind 发(引擎)
- `_quality_distinguish_probes` 只对 `kind ∈ {education_start, education_change, education_interruption}` 的学业事件生成;`education_completion` 及其它 kind 跳过。事件记录里的 kind 字段名以 `contracts.py::EVENT_KINDS` 为准,不要凭 `domain` 猜。
- 验收:`tests/test_rectification_event_probes.py` 新增:同域一条 `education_start` + 一条 `education_completion` → 只产出一条质量探针且 `target_evidence_id` 指向入学;单独一条 `education_completion` → 零条质量探针,且该事件仍参与普通时间探针(不得因此少问它本身)。
### 5.3 同域同分组去重(引擎)
- 在 5.2 之后再做:同域两条可发事件(如 `education_start` + `education_change`)若 yes/no 集合相同,只保留一条。
- 验收:同文件新增用例,两条 `education_start` 类事件、相同分组 → 一条探针;不同分组 → 两条(`MAX_QUALITY_DISTINGUISH_PROBES` 上限不变)。
### 5.4 记录
- `docs/BUG_HISTORY.md` 新增 BUG-540(前端错绑)、BUG-541(引擎 kind 与去重);关联 BUG-390。
- `docs/tasks/PROGRESS-rectification-quality-probe-dedupe-20260904.md``CHANGELOG.md` 一条。
- `docs/testing/rectification-quality-probe-dedupe-20260904.md`:真实环境清单——新建案例录入"入学 + 毕业"两条学业事件后,候选区分阶段最多出现一次"上大学"题,且题干年份 = 入学年份。
## 6. 让步顺序
5.1 > 5.2 > 5.3 > 5.4。5.3 若与既有 `information_gain` 排序测试冲突,允许先只做 5.1 + 5.2 并把 5.3 写进 `BLOCKED.md`
## 7. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-quality-probe-dedupe-20260904 .worktrees/rectification-quality-probe-dedupe-20260904 origin/staging
cd .worktrees/rectification-quality-probe-dedupe-20260904
ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules
grep -o "BUG-5[0-9][0-9]" docs/BUG_HISTORY.md | sort -u | tail -1
.venv/bin/python -m pytest tests/test_rectification_event_probes.py tests/test_probe_question_contract.py -q
cd frontend && npx tsx --test tests/rectification-choice-card.test.ts tests/rectification-probe-question-contract.test.ts
```
收尾:`tsc --noEmit``npm run lint``npm test`(与 `origin/staging` 基线失败清单逐条比对)、`.venv/bin/python scripts/run_quality_gate.py --profile quick`