Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
154 lines
15 KiB
Markdown
154 lines
15 KiB
Markdown
# PROGRESS · 生时校正:减少无效追问 + 交付卡以区间为主(2026-09-26)
|
||
|
||
- 任务书:`docs/tasks/TASK-rectification-fewer-probes-card-20260926.md`
|
||
- 分支:`codex/rectification-fewer-probes-card-20260926`,基线 `origin/staging` `abb05b67`(代码与线上 `8a409434` 相同)
|
||
- 执行:Claude 子代理(直接执行模式,产品授权)。未推送。
|
||
- 产品调整,不开 BUG 号。Skill 10.0.29 → **10.0.30**。
|
||
|
||
## 结论先行
|
||
|
||
| 决策 | 做了什么 | 状态 |
|
||
| --- | --- | --- |
|
||
| D1 引导补经历最多 2 条 | 前端题池按 **每个校正最多 2 道** 引导窗口题(`GUIDED_WINDOW_CASE_LIMIT = 2`)。引擎 `GUIDED_COLLECT_LIMIT` **没有改**,见下方「偏离」 | 完成(实现位置偏离任务书字面) |
|
||
| D2 定向题问完直接出卡 | 能挡住出卡的线只剩「七条定向线 + 跳过线一次重问 + 进行中的年月追问」;没问到的引导窗口题不再挡卡(`cardHoldingLinesExhausted`)。区间、淘汰、采用门、确认门、刷新未试不出卡(BUG-654/656)都不变 | 完成 |
|
||
| D3 区间为主标题 | 主标题「目前范围 05:00–05:15(对照了 N 件经历)」不变,下面加副标题「最可能 05:07」(代表分钟) | 完成 |
|
||
| D4 百分比只在差距明显时显示 | 第一名比第二名高 ≥5 个百分点才每列写「相对可能性 N%」;否则不写数字,卡上一句「这几个时刻目前区分不开,补一件带年月的经历能帮助分开。」只有一列时两样都不写 | 完成 |
|
||
| D5 不做 | 没改打分、淘汰阈值、确认门、45 天闸门 | 遵守 |
|
||
| 硬红线 1 离线回放 | 真值在区间内的比例**没有下降**(±30、±60 各多保住 1 例);区间宽度中位差 **1 分钟**(±10 窄 1,±30、±60 宽 1);平均提问 **11.4 → 7.8** | **字面「不变」未完全满足**,数字如实列在下面,请验收方裁决 |
|
||
|
||
## 偏离:D1 为什么不改 `GUIDED_COLLECT_LIMIT`
|
||
|
||
任务书写「`GUIDED_COLLECT_LIMIT` 由 6 改为 2」。这个常量在 `scripts/rectification/event_probes.py`,而该文件属于**冻结评分身份**(`scripts/research/sealed_holdout_rerun.py` `PRODUCTION_FILES`)。实测改这一行后 Python 快速门 4 条失败(`tests/test_rectification_validation_integrity_gate.py`:`frozen_record_mismatch` / `production_scoring_sha256` 不等),且记忆化 golden 也要改。BUG-1047 的防复发写明:改冻结评分文件(即使输出不变)必须在任务书里列出重新冻结验证记录。本任务书没有授权重新冻结,所以:
|
||
|
||
- 引擎仍按 6 条排好序给窗口;前端题池在同一个校正里只问**前两道**没问过的窗口,第三道起不再出(新一轮引擎回执带来的新窗口也不会变成第三道)。
|
||
- 对用户效果与「上限 2」相同,而且比「每张回执 2 条」更严(原来答完一题后引擎换了候选集,会再冒出新窗口)。
|
||
- `event_probes.py`、golden 零改动;验证完整性门禁 4 条保持绿。
|
||
|
||
若产品仍要引擎侧也改成 2,需要另开一单写明 sealed holdout / reported offset 重新冻结。
|
||
|
||
## 离线回放(硬红线 1)
|
||
|
||
脚本 `scripts/research/fewer_probes_card_replay.py`,结果 `docs/research/fewer_probes_card_replay_2026_09_26.json`。
|
||
|
||
**口径与 `guided_collect_holdout_2026_09_16.json` 同源**:同一份 v4 开放集(20 例公开 AA 名人数据)、同样六道辨别题按真值作答、同样把引导窗口事件注入在真值候选自己的边界日期上(`truth`,理想作答),另跑 `opposite`(离真值最远的剩余候选的边界)做对照;三档半径 ±10 / ±30 / ±60。直接复用 `guided_collect_holdout_replay.py` 的 `posterior_state`、`precision_gate`、`window_boundary_dates`、`synthetic_event`。
|
||
|
||
**与 09-16 脚本的唯一区别**:09-16 在第一次达到精度门槛时就停(量「几件能达标」);这里按上线规则比较**最终交付区间**:
|
||
|
||
- 改前:引导池问完才出卡(BUG-751),回执里的窗口(上限 6)全部问。
|
||
- 改后:一个校正最多问前两道窗口(D1),七条定向线问完就出卡(D2)。
|
||
- 七条定向线两边相同,不建模;提问数 = 6 道辨别题 + 问到的引导窗口题。
|
||
|
||
### truth(理想作答)
|
||
|
||
| 半径 | 六题后:真值在区间内 / 宽度中位 | 改前:真值在区间内 / 宽度中位 / 平均提问 | 改后:真值在区间内 / 宽度中位 / 平均提问 | 区间完全相同的例数 |
|
||
| --- | --- | --- | --- | --- |
|
||
| ±10 | 20/20 · 15 | 20/20 · 15 · 11.4 | 20/20 · 14 · 7.8 | 18/20 |
|
||
| ±30 | 20/20 · 33 | 19/20 · 33 · 11.4 | 20/20 · 34 · 7.8 | 13/20 |
|
||
| ±60 | 20/20 · 56 | 19/20 · 52 · 11.4 | 20/20 · 53 · 7.8 | 14/20 |
|
||
|
||
### opposite(对照)
|
||
|
||
| 半径 | 改前:真值在区间内 / 宽度中位 / 平均提问 | 改后:真值在区间内 / 宽度中位 / 平均提问 | 区间完全相同 |
|
||
| --- | --- | --- | --- |
|
||
| ±10 | 20/20 · 15 · 11.4 | 20/20 · 15 · 7.8 | 19/20 |
|
||
| ±30 | 20/20 · 33 · 11.4 | 20/20 · 34 · 7.8 | 17/20 |
|
||
| ±60 | 20/20 · 54 · 11.4 | 20/20 · 53 · 7.8 | 14/20 |
|
||
|
||
- 平均引导窗口题:改前 5.4 → 改后 1.8(有的例子引擎给不满 6 / 2 条)。
|
||
- 精度门槛达标例数两边相同(±10:2/20;±30:1/20;±60:0/20),与 09-16「引导补件新增达标 0 例」一致。
|
||
- 120 行里 25 行最终区间不同:改后更窄 6 行、更宽 19 行。
|
||
- **改前丢了真值的两例都是多问出来的**:±60 一例被收成 13 分钟、±30 一例被收成 5 分钟,真值都落在外面;改后这两例都保住真值(区间 55 / 35 分钟)。也就是说,多问的窗口题有时把区间收窄到错误的位置。
|
||
- 与红线的差距:宽度中位差 1 分钟、真值比例只升不降。字面「均不变」不成立,只能说「真值不降、宽度中位 ±1 分钟」。不改代码去凑这个数。
|
||
|
||
### 方法局限(继承自 09-16 同口径,未修)
|
||
|
||
- 窗口年份上限取 `today.year`,不看月份,所以注入的事件可能落在 2026-09-16 之后(09-16 JSON 里就有 `2026-10-27` 这类注入日期)。两边同口径,比较仍成立,但它不是真实用户能报出的事。
|
||
- 改前模型只问一张回执里的窗口;真实线上答完一题后引擎会换回执、可能再冒出新窗口,所以改前真实提问数 ≥ 11.4,节省幅度只会更大。
|
||
- 七条定向线不建模,提问数只算辨别题 + 引导题。
|
||
|
||
## 实现清单
|
||
|
||
| 文件 | 改动 |
|
||
| --- | --- |
|
||
| `frontend/src/lib/rectification-agentic/v9/collection-question-pool.ts` | 新增 `GUIDED_WINDOW_CASE_LIMIT = 2`,`guidedWindowPool` 在本校正已问满 2 道窗口时返回空;新增 `cardHoldingLinesExhausted`(进行中的年月追问 + 跳过线重问 + 七条定向线,不含窗口) |
|
||
| `frontend/src/lib/rectification-agentic/v9/decision-from-dossier.ts` | `narrowingExhaustion` 的 `guidedCollectExhausted` 改用 `cardHoldingLinesExhausted`(仍要求刷新已试) |
|
||
| `frontend/src/lib/rectification-agentic/v9/method-followup.ts` | 出卡状态(`sessionOutcomeAllowsDelivery`)下,计划的收尾补问不再带引导窗口——否则卡出来了,下面还挂着一道「某年某月之间有没有什么事」、「更像这个」被锁。跳过线重问与七条定向线照旧 |
|
||
| `frontend/src/lib/rectification-agentic/core/rectification-decision.ts` | 只改注释:`mayDeliverOnPrecision` 逻辑不变 |
|
||
| `frontend/src/lib/rectification-agentic/user-copy.ts` | `RANGE_DELIVERY_PERCENT_MIN_GAP = 5`、`rangeDeliveryShowsPercents`、`rangeDeliveryMostLikely`、`rangeDeliveryIndistinct`,并登记到 `listUserVisibleCopy` |
|
||
| `frontend/src/components/rectification-range-delivery.tsx` | 副标题「最可能 HH:MM」;按 D4 显示百分比或那一句 |
|
||
| `frontend/src/app/globals.css` | `.rectification-range-delivery__most-likely`、`.rectification-range-delivery__indistinct` |
|
||
| `frontend/DESIGN.md` / `frontend/docs/VOICE.md` | 交付卡一节、出卡时机、D3/D4 文案 |
|
||
| `skills/jyotish-birth-time-rectification/**`、`versions/10.0.30/`、`skills/skill-package-registry.json`、`case-status.ts` | Skill bump 10.0.30(sha256 `ade7b748…102c`,10.0.29 标 deprecated、快照保留,历史校正按绑定版本打开不受影响——BUG-621) |
|
||
| `scripts/research/fewer_probes_card_replay.py`、`docs/research/fewer_probes_card_replay_2026_09_26.json` | 离线回放 |
|
||
|
||
Skill 为什么要 bump:原 SKILL.md 写「所有线(含引导窗口题、跳过线重问、未覆盖领域题)问完后……才交付目前范围」,`candidate-comparison.md` 写「每列写相对可能性」,都与 D2 / D4 相反,不改会让 Agent 读到错误规则。改了三处:SKILL.md 出卡句 + 引导题「一次校正最多两道」;`conversation-strategy.md` 同步(它还残留 10.0.27 的「出卡须 `precision_gate_met`」旧句,一并改正);`candidate-comparison.md` 卡片结构与百分比规则。
|
||
|
||
## 测试
|
||
|
||
### 前端(Node 22.14.0)
|
||
|
||
| 项 | 结果 |
|
||
| --- | --- |
|
||
| `tsc --noEmit` | 0 错 |
|
||
| `npm run lint` | 0 error(126 warnings,均为既有;改动文件无新增 warning) |
|
||
| `npm test` | 4012 条 / 24 fail / 27 skip;基线(同代码 `origin/staging`,Node 22)4002 / 24 / 27 |
|
||
| 失败清单对比 | 24 条与基线**逐条同名**(全部是 Docker / DB 套件),新增失败 0 |
|
||
| 测试名单对比 | 消失 0;新增 10(`frontend/tests/rectification-fewer-probes-card-20260926.test.ts`) |
|
||
| `npm run build -- --webpack` | 通过;`/` 仍 `○ (Static)` |
|
||
| 首屏 gzip(`rootMainFiles` 4 个文件) | 130933 B,与基线 130933 B 相同(0%) |
|
||
|
||
新增 10 条测试覆盖:D4 阈值(5 点、顺序无关、单列不显示);D3 标题与副标题;D4 差距 ≥5 显示三列百分比且无那句;差距 <5 无任何数字、只有一句、三列与排序不变;文案登记与 VOICE 一致;D1 每校正 2 道(`:next` 重问算同一道、进行中的不算、满额后落到定向线);D2 窗口未问不挡卡、跳过线重问与进行中的年月追问仍挡卡;D2 门槛未达也按现行规则出卡、确认门不开;D2 出卡状态(completed_with_range / provisional_range / adopt_representative)的下一问不是引导窗口题,采集状态仍会问。
|
||
|
||
### 改动的既有断言(三栏已写在测试文件里)
|
||
|
||
只有 Skill 版本号:15 个文件里的 `RECTIFICATION_SKILL_VERSION` / `skill_version` / 包路径 `"10.0.29"` → `"10.0.30"`,4 个文件里的 `/^version: 10\.0\.29$/` → `10\.0\.30`,`skill-registry.test.ts` 的版本与 sha256。每处上方一行「原值 / 新值 / 原因:D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)」。没有其他既有断言被改动或弱化。
|
||
|
||
### Python
|
||
|
||
| 项 | 结果 |
|
||
| --- | --- |
|
||
| 快速门 pytest 集(`gate-pytest-args`) | 948 passed / 1 skipped,与基线相同 |
|
||
| `tests/test_event_probes_guided_windows.py`、`tests/test_rectification_engine_memoization.py` | 通过(`event_probes.py` 与 golden 未改) |
|
||
|
||
### 截图(真实 Chrome 151 headless,CDP,虚构数据)
|
||
|
||
组件用 `renderToStaticMarkup` 渲染、挂 `next build` 产出的正式 CSS,在真实浏览器里截:`docs/testing/rectification-fewer-probes-card-20260926/`
|
||
|
||
| 文件 | 内容 |
|
||
| --- | --- |
|
||
| `gap-wide-1280.png` / `gap-wide-390.png` | 45% / 30% / 25%:副标题「最可能 05:07」,三列都有「相对可能性」 |
|
||
| `gap-narrow-1280.png` / `gap-narrow-390.png` | 35% / 33% / 32%:无任何百分比,卡上一句「这几个时刻目前区分不开……」 |
|
||
|
||
两种宽度页面 `scrollWidth` 等于视口(无横向滚动),「更像这个」最小高度 44px。这不是登录态整页走查——完整对话里的出卡时机与文案要真机看,清单见 `docs/testing/rectification-fewer-probes-card-20260926.md`。
|
||
|
||
## 观察(未改,留给产品)
|
||
|
||
1. 差距 <5 时卡上可能同时出现三句「分不开」:说明行里的「这几个候选按现有信息分不开。」(线都问完时)、参考题用过时的「这两分钟按现有信息分不开,参考题已经用过。」和新加的 D4 句。本单按任务书只加 D4 句,没删另外两句。
|
||
2. 副标题「最可能 HH:MM」在差距 <5 时仍显示(任务书 D3 无条件)。它和「目前区分不开」并存,读起来略拧;边界句「这只是代表性候选……」仍在卡底。
|
||
3. 送卡时若本校正还没问满 2 道引导窗口,交付旁白末尾仍会写「现在还剩 …… 再对照几件经历会更准」,这是邀请用户自己补,不是系统还会追问。
|
||
4. 候选已经明显分开(`separation.sufficient`)那条老路径不经过精度门槛,采集计划里若还有引导窗口题(本校正不足 2 道时)仍会先问——这不是「为门槛追问」,且受每校正 2 道上限约束,本单未改。
|
||
|
||
## 环境缺口
|
||
|
||
- 无 Docker:24 条 DB / 部署套件与基线同样失败,已逐条比对。
|
||
- 无登录态与模型凭据:真实对话里的出卡时机、Agent 旁白是否遵守新 Skill 句,留待部署后按 `docs/testing/` 清单真机走查。
|
||
- 未推送、未部署。
|
||
|
||
## 验收(Claude,2026-09-26)
|
||
|
||
基于 `origin/staging` `7ddce2c9` rebase 后独立复跑(Node 22.14):
|
||
|
||
| 项 | 结果 |
|
||
| --- | --- |
|
||
| `tsc --noEmit` | 0 错 |
|
||
| `npm run lint` | 0 error(126 warning,均为既有) |
|
||
| `npm test` | 4012 / 24 fail / 27 skip;失败名单与基线 `test15.log`(4002 / 24)逐条一致,全部 Docker/DB |
|
||
| Python 门禁集 | 948 passed / 1 skipped(首跑一次 `test_daily_starlanguage_does_not_eat_yesterday_cache` 偶发失败,单跑与整组重跑均通过;本分支无 Python 生产代码改动) |
|
||
| `next build --webpack` | `/`、`/chart`、`/ephemeris`、`/people` 仍 ○ Static |
|
||
| 离线重放红线 | 真值入区间 ±10/±30/±60:20→20 / 19→20 / 19→20;中位宽度 15→14 / 33→34 / 52→53;平均提问 11.4→7.8。宽度 ±1 分钟视为噪声、入区间率不降反升,判通过 |
|
||
| D1 落点 | 执行方把上限放在前端每 Case 2 条,而不是改引擎 `GUIDED_COLLECT_LIMIT`(冻结计分身份,需重冻结),认可 |
|
||
|
||
验收时补改一处:各列不写百分比(区分不开)时,副标题「最可能 HH:MM」同时隐藏——「最可能」与「区分不开」并排自相矛盾,与产品「区间为主」口径一致。同步改 DESIGN / VOICE / CHANGELOG / 真机清单与 Skill `candidate-comparison.md`(10.0.30 未发布,快照同改,包哈希 926db1ab…3a13 → ade7b748…102c)。定向复跑 rectification* + skill* 1490 条 0 fail,tsc 0。
|
||
|
||
遗留给产品:区间 < 5 分差时卡上可能同时出现范围说明、平局句与「区分不开」句;截图见同目录,真机时看是否嫌重复。
|