- R1: flipping 1 answer keeps truth in range 98-100% but cuts head hit by a third or more; 2 flips squeeze truth out in 7-10% of ±30/±60 replays (two flips = 8 points = SEPARATION_LEAD). - R2: weights do apply (research scorer == production at V0); V1/V2 are identity at ±30/±60 by construction and leave six-question metrics unchanged at ±10 -> no_benefit (measured). Supplementary V1n does not pass the gate. - R3: boundary shift is ~3.8 days/minute (1.3-5.9), not 1.1; the 45-day gate is ~8-34 minutes. The _representative_pairs hypothesis is refuted (all-pairs adds no dated probes); the bottleneck is monthly evaluation. New finding recorded as BUG-1048 (investigating): _boundary_windows year-straddle exemption and positional zip misalignment bypass the gate. - Dated errata appended (no deletions) to the 09-14/09-16 briefs and research docs; README board row -> 待验收. No production code, scoring, thresholds, gates or Skill changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
20 KiB
生时校正三项离线研究(2026-09-26)
- 任务书:
docs/tasks/TASK-rectification-offline-research-20260926.md;进度:docs/tasks/PROGRESS-rectification-offline-research-20260926.md - 分支:
codex/rectification-offline-research-20260926,测量在origin/staging@7ddce2c9上完成(引擎代码与任务书基线abb05b67相同),提交已变基到7203d94e(其间只新增fewer_probes_card_replay.py与文档,引擎与打分代码未变)。 - 性质:离线测量。不改线上代码、打分、阈值、出题闸门、Skill。 研究脚本读引擎内部函数,个别测量在一次调用内临时替换模块属性,调用结束立即恢复,脚本末尾断言已恢复。
- 数据:
references/real_case_calibration/minute_rectification_holdout_v4.json,20 例公开 Rodden-AA 名人。R3a 只用随机虚构时刻。这是开放集回放,不是盲测,下面的数字不是准确率。 - 口径:ayanamsa
raman,node modemean,步长 2 分钟,半径 ±10 / ±30 / ±60。交付区间 = 未淘汰、且落后头名不足 8 分的簇的并集(线上unionStillValidRange)。六题回放沿用 09-14 研究:同一份六道题,不随作答重新出题。 - 同机 A/B:R1、R3a 在
PYTHONHASHSEED不同的两次运行下 JSON 逐字节一致;R2、R3c 换PYTHONHASHSEED复跑,汇总与已存 JSON 完全一致(见进度记录)。不与其他机器的浮点结果比对(BUG-985)。
结论
| 项 | 一句话结论 | 是否建议立实现单 |
|---|---|---|
| R1 答错容错 | 答错 1 题,真值几乎都还在区间里(98–100%),但头名命中降三到四成。答错 2 题,±30 / ±60 上约 7–10% 的回放会把真值挤出区间,半数以上的例子至少有一种两题组合会挤出。原因是两道反答正好造成 8 分差,等于淘汰线 | 不立实现单。给产品一条口径:区间变窄不等于更准(±10 反答后宽度反而从 15 收到 11) |
| R2 V1/V2 分盘配权 | 权重确实生效了:研究计分器在 V0 与线上逐分相同。V1/V2 在 ±30 / ±60 上按定义就是恒等,0 个候选分数变化。在 ±10 上改了约 3/4 候选的分数(最多 0.4–0.6 分),六题后指标与基线完全相同。判定由 not_measured 改为 no_benefit(已实测) |
不立实现单。补充的 V1n(按敏感度归一化重新分配权重)头名 +1/+1/+2 例,但宽度没降,±60 宽度 56→73,不过门 |
| R3 算术改正 | 1 分钟对应大运边界位移中位 3.8 天,不是 1.1 天(p10 1.6、p90 5.0,范围 1.3–5.9),按出生星宿主星分两档。45 天闸门对应 8–34 分钟(中位 12,p10–p90 为 9–29),不是 40 分钟。_representative_pairs 推断被推翻:改成全部两两配对后,带日期的题一道都没多。真正的卡点在逐月评估:只有 3%(刷新阶段 0.4%)的边界月份能让代表候选分到两边 |
不立实现单。另记一个新发现:_boundary_windows 有两处绕过闸门的漏洞(跨 1 月 1 日豁免、按位置 zip 错位),记为 BUG-1048 investigating,是否修要等产品决定 |
R1 · 答错 1–2 题的容错
方法。 每例每个半径用线上 G0 闸门出题,取前六题。按真值给出最优是 / 否,再从能作答的题里随机选 1 题或 2 题反过来答(固定种子 20260926,按例、半径、题数、重复序号生成随机数,每格重复 30 次)。另外对所有组合穷举一遍作对照。能作答的题少于 k 的例子不进该格;所以每格另列「同一批例子不答错」的基线。
复跑:python3 scripts/research/answer_flip_tolerance.py(约 1 分钟)→ docs/research/answer_flip_tolerance_2026_09_26.json
| 半径 | 答错题数 | 例数 | 回放次数 | 真值在区间 | 头名命中 | 宽度中位(分钟) | 至少一次挤出真值的例子 | 同批例子不答错:命中 / 宽度 |
|---|---|---|---|---|---|---|---|---|
| ±10 | 0 | 20 | 20 | 1.000 | 0.80 | 15 | 0 | — |
| ±10 | 1 | 16 | 480 | 1.000 | 0.58 | 13 | 0 | 0.94 / 12 |
| ±10 | 2 | 15 | 450 | 0.996 | 0.40 | 11 | 1 | 0.93 / 11 |
| ±30 | 0 | 20 | 20 | 1.000 | 0.55 | 33 | 0 | — |
| ±30 | 1 | 18 | 540 | 0.989 | 0.33 | 35 | 1 | 0.61 / 32 |
| ±30 | 2 | 18 | 540 | 0.926 | 0.08 | 33 | 9 | 0.61 / 32 |
| ±60 | 0 | 20 | 20 | 1.000 | 0.40 | 56 | 0 | — |
| ±60 | 1 | 18 | 540 | 0.981 | 0.24 | 73 | 2 | 0.44 / 54 |
| ±60 | 2 | 18 | 540 | 0.898 | 0.06 | 71 | 12 | 0.44 / 54 |
穷举对照(每例所有 1 题 / 2 题组合):真值在区间 ±10 1.000 / 0.990,±30 0.991 / 0.919,±60 0.981 / 0.907;头名 ±10 0.66 / 0.43,±30 0.33 / 0.09,±60 0.23 / 0.06。随机抽样与穷举一致。
不答错的基线与 09-14 精度闸门研究的 G0 完全一致(0.80 / 0.55 / 0.40;15 / 33 / 56;20/20),说明回放链路没有漂移。
读法。
- 真值从未被淘汰。 强冲突淘汰要 3 次;答错 2 题最多给真值 2 次冲突。
- 挤出真值靠的是 8 分线。 一道反答让真值 −2、另一侧 +2,相对差 4 分;两道反答正好 8 分,等于
SEPARATION_LEAD = 8。所以 k=2 开始挤出真值,k=1 只有真值原本就贴线的例子才会被挤出。 - ±10 上答错反而让区间更窄(15 → 13 → 11),头名却从 0.94 掉到 0.40。窄是因为错误答案把一个假头名推高了,不是因为更准。产品口径:不要把「范围变窄」讲成「更确定了」。
- 回放的六道题不随作答重出,这一点对答对和答错两边都一样。真人答错后,后续题目可能被带偏,实际影响可能更大,本回放测不到。
是否建议立实现单:不建议。 容错与淘汰线(8 分、3 次冲突)是同一组常数,要改就会同时影响答对时的宽度,需要另立研究。可选方向(均未测):答题之间出现矛盾时放宽交付并集;在交付卡上允许「改一道答案」。
R2 · V1/V2 分盘敏感度配权重跑
背景。 09-14 精度闸门研究的 M1b 里,V1/V2 与 V0 数字完全一样。BUG-692 把判定改成 not_measured,文中怀疑「权重没真正打上」。本轮先证明权重有没有生效,再重跑。
复跑:python3 scripts/research/varga_sensitivity_rerun.py(约 16 分钟)→ docs/research/varga_sensitivity_rerun_2026_09_26.json
第一步:权重是否改了分数
| 半径 | 研究计分器 V0 vs 线上 | V1 vs V0:分数变化的候选 | V1 最大分差 | V1 头名变化例 | V2 vs V0:分数变化的候选 | V2 最大分差 | V2 头名变化例 | V2 丢掉某领域分盘的例子 |
|---|---|---|---|---|---|---|---|---|
| ±10 | 0 / 220 不同 | 168 / 220(17 例) | 0.428 | 0 | 159 / 220(17 例) | 0.625 | 1 | 12 |
| ±30 | 0 / 620 不同 | 0 / 620 | 0 | 0 | 0 / 620 | 0 | 0 | 0 |
| ±60 | 0 / 1220 不同 | 0 / 1220 | 0 | 0 | 0 / 1220 | 0 | 0 | 0 |
V1 系数 min(窗宽 / 分盘换一次上升的分钟数, 1):
| 窗宽 | D2 | D3 | D5 | 其余 8 张(D4/D7/D9/D10/D11/D12/D24/D30) |
|---|---|---|---|---|
| 20 分钟(±10) | 0.33 | 0.50 | 0.83 | 1.00 |
| 60 分钟(±30) | 1.00 | 1.00 | 1.00 | 1.00 |
| 120 分钟(±60) | 1.00 | 1.00 | 1.00 | 1.00 |
结论:权重生效了,是 V1/V2 的定义本身在宽窗上等于恒等。 11 张计分分盘里最慢的 D2 每 60 分钟换一次上升,窗宽 ≥ 60 分钟时 V1 每个系数都截断到 1。V2 只丢掉窗内一次都不变的分盘,窗宽 ≥ 60 分钟时每张都会变。±10 上两者确实改了分数,但幅度小于候选之间的分差。09-14「与 V0 相同」的数字是真实结果,不是没打上权重。
第二步:按 closure 口径重跑(G0 闸门、六题回放)
| 半径 | 方案 | 头名命中 | 真值在区间 | 宽度中位 | 引擎头名(答题前) | 六题后再出 | 判定 |
|---|---|---|---|---|---|---|---|
| ±10 | V0 | 0.80 | 20/20 | 15 | 0.30 | 3.25 | 基线 |
| ±10 | V1 | 0.80 | 20/20 | 15 | 0.30 | 3.25 | no_benefit |
| ±10 | V2 | 0.80 | 20/20 | 15 | 0.35 | 3.25 | no_benefit |
| ±10 | V1n(补充) | 0.85 | 20/20 | 15 | 0.45 | 3.25 | no_benefit(宽度未降) |
| ±30 | V0 | 0.55 | 20/20 | 33 | 0.20 | 5.45 | 基线 |
| ±30 | V1 / V2 | 0.55 | 20/20 | 33 | 0.20 | 5.45 | no_benefit(恒等) |
| ±30 | V1n(补充) | 0.60 | 20/20 | 33 | 0.15 | 5.45 | no_benefit(宽度未降) |
| ±60 | V0 | 0.40 | 20/20 | 56 | 0.05 | 5.75 | 基线 |
| ±60 | V1 / V2 | 0.40 | 20/20 | 56 | 0.05 | 5.75 | no_benefit(恒等) |
| ±60 | V1n(补充) | 0.50 | 20/20 | 73 | 0.10 | 5.90 | no_benefit(宽度变宽) |
判定沿用 precision_gate_lib.gate_verdict:头名不降且宽度下降才算有收益,覆盖不得下降。
V1n 是什么。 这是 09-14 设计里没有的补充方案。每张分盘的系数与「换一次上升的分钟数」成反比,再归一化到 11 张盘的平均系数等于 1。总分量不变,只把权重从慢盘挪到快盘(D2 0.17、D3 0.26、D5 0.43、D7 0.60、D9 0.77、D10 0.85、D11 0.94、D12 1.02、D4 1.36、D24 2.05、D30 2.56)。它改了全部候选的分数,头名多中 1 / 1 / 2 例(20 例里),但宽度没有收窄,±60 反而变宽。样本只有 20 例,+1 例在噪声范围内。
是否建议立实现单:不建议。 V1/V2 结论更新为 no_benefit(已实测)。V1n 若要继续,先补 ±7 天日期偏移和 R1 答错两项稳健性,再谈实现。
R3 · 改正「1 分钟 ≈ 1.1 天」与 _representative_pairs 推断
R3a · 1 分钟对应多少天
复跑:python3 scripts/research/dasha_shift_per_minute.py(1 秒)→ docs/research/dasha_shift_per_minute_2026_09_26.json
推导。 第一段大运的余额是 (1 − 星宿内进度) × 主星年数。之后每一个 MD / AD / PD 的起点都等于「出生 + 余额 + 固定时长」,所以出生时间挪 1 分钟,所有边界整体平移同一个天数:
平移天数 / 分钟 = 月亮每分钟走的度数 ÷ 13.333° × 主星年数 × 365.25
月亮平均每分钟 0.0091°。Vimshottari 120 年覆盖 9 个星宿(120°),平均 1° ≈ 365 天,按主星从 164 天(太阳 6 年)到 548 天(金星 20 年)不等。09-14 文档写的「1° ≈ 122 天」是错的,于是算成 0.0092 × 122 ≈ 1.1 天,把位移低估了约 3.5 倍。
实测(2000 个虚构时刻,种子 7,swisseph 恒星黄道 Raman):
| 测法 | n | 平均 | p0 | p10 | p25 | 中位 | p75 | p90 | p100 |
|---|---|---|---|---|---|---|---|---|---|
| 解析式(月速 × 主星年数) | 2000 | 3.32 | 1.34 | 1.58 | 1.80 | 3.82 | 4.54 | 5.00 | 5.85 |
| 差分(余额在 t 与 t+1 分钟) | 1997 | 3.32 | 1.34 | 1.58 | 1.80 | 3.82 | 4.54 | 5.00 | 5.85 |
仓库 _vim_start_dates(t 与 t+20 分钟,÷20) |
299 | 3.50 | 1.35 | 1.60 | 1.95 | 4.05 | 4.55 | 5.00 | 5.75 |
仓库函数的所有 MD / AD 起点在 20 分钟内平移同一个天数(对齐后差值最大相差 1 天,是日期取整)。它是前 300 个时刻的子样本,中位 4.05 与全样本 3.82 的差异来自抽样。
按出生星宿主星分两档(中位,天 / 分钟):太阳 1.50、火星 1.71、计都 1.77、月亮 2.47 | 木星 3.96、水星 4.18、罗睺 4.54、土星 4.71、金星 4.93。前一档约占 45%。所以「中位 3.8」落在两档之间,单个人的实际值要么约 1.5–2.5,要么约 4–5。
闸门与窗宽换算(改正后)
| 候选相隔 | 边界相差(天,中位 / p10 / p90) | ≥45 天的比例 | ≥30 天的比例 | 旧文档写的 |
|---|---|---|---|---|
| 2 分钟 | 7.6 / 3.2 / 10.0 | 0% | 0% | ≈ 2 天 |
| 4 分钟 | 15.3 / 6.3 / 20.0 | 0% | 0% | — |
| 10 分钟 | 38.2 / 15.8 / 50.0 | 27% | 54% | — |
| 20 分钟 | 76.5 / 31.5 / 100.0 | 66% | 94% | ≈ 22 天、不可出题 |
| 30 分钟 | 114.7 / 47.3 / 149.9 | 94% | 100% | — |
| 40 分钟 | 153.0 / 63.1 / 199.9 | 100% | 100% | ≈ 45 天 |
- 45 天闸门 ↔ 中位 11.8 分钟(p10 9.0,p90 28.5,全范围 7.7–33.5)。
- 30 天刷新闸门 ↔ 中位 7.8 分钟(6.0–19.0,全范围 5.1–22.3)。
不同精度的证据能分开多近的候选
证据日期要落在两个候选的边界之间,才能把它们分到两边。所需边界差约等于证据的日期跨度,再除以每分钟平移天数:
| 证据精度 | 日期跨度 | 稳定可分的候选间距(分钟) 快档 p90 | 中位 | 慢档 p10 | 最慢 |
|---|---|---|---|---|---|
| 确切某一天 | 1 天 | 0.2 | 0.3 | 0.6 | 0.7 |
| 某天 ±3 天 | 7 天 | 1.4 | 1.8 | 4.4 | 5.2 |
| 某天 ±7 天 | 15 天 | 3.0 | 3.9 | 9.5 | 11.2 |
| 记得到月 | 30.4 天 | 6.1 | 8.0 | 19.3 | 22.6 |
| 线上 45 天闸门 | 45 天 | 9.0 | 11.8 | 28.5 | 33.5 |
| 只记得年 | 365 天 | 73 | 95 | 232 | 272 |
低于这个间距时,只有两条边界恰好跨在月初(或年初)两侧才能分开,概率约为「边界差 ÷ 跨度」。例如按中位平移,相隔 2 分钟的一对候选:跨月概率 25%,跨年概率 2%;相隔 20 分钟:跨年概率 21%。
所以: 记得到月的事件,在候选相隔约 8 分钟以上(慢档约 20 分钟以上)时就够用;记得到天(容许几天误差)的事件,能分开相隔 2–5 分钟的候选。只记得年的事件单靠大运边界分不开 ±60 以内的候选,只有跨年的那一小部分边界例外。
R3c · _representative_pairs 推断核实
任务书的推断: 「出不来题」的真正原因可能是 _representative_pairs 只比较相邻代表分钟和首尾一对,相邻代表太近,边界差过不了闸门。
复跑:python3 scripts/research/representative_pairs_probe.py(约 6 分钟)→ docs/research/representative_pairs_probe_2026_09_26.json
对每例每个半径,在「初始」(全部候选)与「刷新」(六题回放后剩余候选,与 09-14 相同)两个阶段,比较三种设置:线上配对;研究补丁返回全部两两配对;研究补丁把 _boundary_windows 改成严格闸门(对所有配对都卡最小间隔、不豁免跨年,并把错位的 zip 对齐)。题目数在公开截断之前统计。
| 半径 · 阶段 | 代表数中位 | 线上配对:过闸对数均 | 全配对:过闸对数均 | 带日期的题(截断前,每例均值)线上 / 全配对 / 严格闸门 | 公开题里没有新的带日期题的例子 线上 / 全配对 / 严格闸门 | 边界月份评估次数均 | 能分开代表的比例 |
|---|---|---|---|---|---|---|---|
| ±10 初始 | 6 | 0.60 | 3.30 | 4.85 / 4.80 / 4.10 | 4 / 4 / 8 | 168 | 3.2% |
| ±10 刷新 | 4 | 0.90 | 1.65 | 3.25 / 3.30 / 3.10 | 3 / 3 / 4 | 809 | 0.4% |
| ±30 初始 | 16 | 1.00 | 76.5 | 6.75 / 6.75 / 6.55 | 2 / 2 / 2 | 226 | 4.2% |
| ±30 刷新 | 5 | 1.80 | 4.95 | 5.30 / 5.25 / 5.30 | 2 / 2 / 2 | 1130 | 0.5% |
| ±60 初始 | 31 | 1.00 | 400.6 | 6.95 / 7.05 / 6.85 | 2 / 2 / 2 | 255 | 4.5% |
| ±60 刷新 | 5 | 1.95 | 5.55 | 5.55 / 5.60 / 5.60 | 2 / 2 / 2 | 1057 | 0.5% |
「没有带日期题」的例子里,每格都有 2 例是 1900 年前出生的(毕加索 1881、弗洛伊德 1856):_birth_year 只接受 1900–2100,出题器对它们直接返回空,与闸门无关。
结论:推断被推翻。
- 全部两两配对没有多出一道带日期的题。 过闸的配对多了好几倍,日期并集也变大,但带日期的题数不变(差异 ≤0.1 道),没有题的例子数完全相同。首尾那一对已经是间距最大的一对,中间补上的配对只贡献更靠内的月份,这些月份照样分不开代表。
- 真正的卡点在逐月评估。
_evaluate_contexts在每个边界月份判断各代表在该领域是否被激活,要求一边激活、一边不激活。初始阶段每例评估约 170–250 个边界月份,只有 3–4.5% 能分开;刷新阶段带上 PD 后评估 800–1100 个,只有约 0.5% 能分开。 - v4 回放里六题后题池并不空。 ±10 刷新阶段平均还能出 3.25 道新的带日期题,1900 年后出生的 18 例里只有 1 例为 0。真机上「问完了」的原因用 v4 复现不出来。候选原因包括:采集线已关或被拒、已问年份、封存年份、存在性封锁年份、真人事件多为月精度,都未测。需要真机统计(见
TASK-rectification-telemetry-20260926.md)才能定性。 - 新发现:
_boundary_windows有两处绕过闸门。- 跨年豁免:代码写的是
one.year == two.year and 差 < 阈值才跳过。两条边界只差 7 天、但分别落在 12 月底和 1 月初,也会被放行。例:某例 ±10 初始阶段,放行的 6 个日期全是这种一周内跨年的对(12-27 / 01-03 这类),逐月评估全部失败。 - zip 错位:两个候选的边界列表按位置配对。一旦某条边界跨出
[出生+5, 出生+80]的年份边缘,列表会差一到两个元素(MD 起点与其第一个 AD 起点同一天,所以一次可能差两个)。此后每一对比的都是不对应的边界,间隔很大,于是全部放行。20 分钟虚构对(只算 MD+AD)有 10.7% 错位;刷新阶段(含 PD)线上配对有 35–44% 错位。 - 改成严格闸门后,±10 初始阶段没有带日期题的例子从 4 例变成 8 例,刷新阶段从 3 例变成 4 例,宽窗基本不变。也就是说,窄窗上现有的一部分带日期题是靠这两个漏洞出来的。这些题问的是「边界只差几天的那个月」,按月精度回答是否可靠,本轮没有评估。已记为 BUG-1048(
investigating),不改代码。
- 跨年豁免:代码写的是
改正与文案建议
已加勘误段(不删原文):
docs/tasks/TASK-rectification-precision-adaptive-boundary-research-20260914.md(1.1 天、122 天 / 度、换算表)docs/tasks/TASK-rectification-open-collect-invite-20260914.md(D1b 的依据句)docs/tasks/TASK-rectification-precision-gate-guided-collect-20260916.md(实证第 5 条「20 分钟 ≈ 22 天,题池必然为空」)docs/research/precision_gate_2026_09_14.md(M1b:V1/V2 改为已实测no_benefit)docs/research/rectification_minute_resolution_closure_2026_09_14.md(索引里 V1/V2「未测」的说明)
引导邀请语的依据:建议改写(不改线上文案)。 线上现在的引导题是「YYYY 年 M 月前后,有没有 X?」加年 / 月选择器,日期可选(BUG-740 起)。BUG-689 / D1b 的依据句建议改成:
出生时间每差 1 分钟,Vimshottari 边界整体平移约 1.3–5.9 天(中位约 3.8 天,取决于出生星宿的主星)。记得到月的事件,在候选相隔约 8 分钟以上(慢的星宿约 20 分钟以上)时就能把它们分开;记得到具体哪天(容许几天误差)的事件,能分开相隔 2–5 分钟的候选。但 v4 开放集上把日精度降成月精度,头名与宽度都没有变化(精度闸门研究 M0),所以不能写成「记得到天明显更有用」已经证实,更不能承诺能定到分钟。
对用户文案的建议(仅供产品决定):选择器保持「日可选」,旁边一句「记得哪天就选上,记不清选到月也可以」。只在剩余候选相隔不到约 8 分钟时(交付区间 ≤ 10 分钟的收尾阶段)才值得主动追问具体哪天。不写任何「能定到分钟」的承诺。
附:全部复跑命令
python3 scripts/research/answer_flip_tolerance.py # R1,约 1 分钟
python3 scripts/research/varga_sensitivity_rerun.py # R2,约 16 分钟
python3 scripts/research/dasha_shift_per_minute.py # R3a,1 秒
python3 scripts/research/representative_pairs_probe.py # R3c,约 6 分钟
python3 -m pytest tests/test_offline_research_20260926.py # 纯函数回归