# ゲート G3 をアプリ経路で測り直す — と、そこで見えた測定の落とし穴 - 実施日: 2026-08-16 - 対象: 価値観プローブ v2 の60問(接地なし・問答体) - 経路: 既存候補 → `triage_score` 上位4件 → **段D(OpenRouter / DeepSeek V4 Flash)が選択** → **選ばれた候補の原文**を判定(段Dの現代語訳は判定しない) - 判定: Haiku 3体、盲検、`period_guess` 偏りスクリーン適用 - 正本: `reports/phase7_app_path_gate.json` / 選択結果 `reports/phase7_app_selection.json` - **このプロジェクトで外部APIに実費が出た唯一の工程**(60問、36.7分、$0.02程度) --- ## 1. なぜ測り直したか ゲート 7.2b の G3(`form` fail 0件)は「アプリの実出力で測る」と定めた。 raw top-1 での測定では form fail が1件出ていたが、その項目の候補1は健全であり、 アプリは上位4件から段Dが選ぶので、ユーザーが見る出力とは別物だったためである。 --- ## 2. 段Dは実際に選び直している **60問中34問(56.7%)で、段Dは `triage_score` 最上位とは違う候補を選んだ。** `_triage_score`(`bridge.py:534`)は話題語の出現数・長さ・文末記号・文字多様性・反復率 しか見ておらず、「答になっているか」を見ていない。段Dはそこを見る。この差が過半の項目で 出るということは、**raw top-1 での測定はアプリの実力を測っていなかった**ことになる。 --- ## 3. 結果 | # | 条件 | 閾値 | raw top-1 | **アプリ経路** | 判定 | |---|---|---|---:|---:|---| | G1 | leakage severe | 0件 | 0 | **0**(none 60/60) | ✓ | | G2 | presentism severe | 0件 | 0 | **0**(none 59 / minor 1) | ✓ | | G3 | `form` fail | 0件 | 1 | **0** | **✓** | | G4 | yes+partial | ≥90% | 98.3% | **100%** | ✓ | | G5 | yes | ≥50% | 75% | **100%** | ✓ | | G6 | 期間判別 | Fisher p<0.05 | 86.8% | **75.0%**、**p=0.0002** | ✓ | **G3 は 0件になった。閾値を緩めずに通った。** --- ## 4. ただし G4 と G5 は、この経路では意味を失っている **段Dの選択基準は「元の質問に最もよく答えている候補」である。そして G4/G5 は 「質問に答えているか」を測る。選択器と評価指標が同じ構成概念になっている。** 数字もそれを示している。個別候補の yes 率が75%なら、独立と仮定した best-of-4 の yes 率は 1−0.25⁴ ≒ 99.6% で、実測の100%と整合する。**上がったのは選択のおかげであって、 モデルが良くなったからではない。** したがって: - **モデルの実力を表すのは raw top-1 の 75%** のほうである。 - **アプリ経路の 100% は「選択が機能している」ことの確認**であって、 ゲートの判別力は持たない(天井に張り付いていて、モデルが劣化しても下がりにくい)。 **G4/G5 は raw top-1 で測るべきである。** 一方 G1・G2・G3・G6 は段Dが最適化していない軸 なので、アプリ経路で測ってよい(G3 の tie-break に「最も文として一貫しているもの」が あるため form には弱い選択効果があるが、主基準ではない)。 これは 7.2b の設計への修正提案である(§6)。 --- ## 5. 測定の失敗と、入れた再発防止(記録) 最初の判定は **`period_guess` が 60/60 の満点**になった。原因は**2回目の同型のリーク**で、 シャードの `item_id` を `nutrition-late` のような probe id のまま出していたため、 rubric が `item_id` の複写を求める以上、判定器は推測すべき期間を読めていた。 **1回目(2026-08-15、価値観v1の判定)は該当スクリプトだけを直した。それが誤りだった。** 不変条件をコードに置かなかったので、別のスクリプトで同じ失敗が再発した。 今回は仕組みを入れた: - `src/timecapsule/blinding.py` — `opaque_ids()` と `assert_blind()`。 盲検シャードの `item_id` に期間の手がかり(`early` / `late` / 明治 / 昭和 / 大正 / 戦前)が 含まれていれば **実行時に `BlindingError` で落とす**。 テストではなく実行時に落とすのは、**リークしたシャードで判定を回すと満点という無意味な 結果が出て、しかも一見成功に見える**ためである。 - `tests/test_blinding.py` — 実際に起きた2件のリークを再現するテストを含む6件。 リーク版の判定出力は破棄した。本レポートの数字はすべて不透明ID(`a000`〜`a059`)での再判定である。 **リーク版とのの差**: 期間判別 100%(28/28・30/30)→ **75.0%**(明治22/30・昭和23/30)。 --- ## 6. 7.2b への修正提案 | # | 現行の定義 | 修正案 | |---|---|---| | G3 | form fail 0件(アプリ実出力) | **変更なし**。アプリ経路で0件を確認した | | G4 | yes+partial ≥90%(アプリ実出力) | **raw top-1 で測る**。アプリ経路では段Dの選択基準と同一で天井に張り付く | | G5 | yes ≥50%(アプリ実出力) | 同上 | | G1/G2/G6 | — | **アプリ経路で測る**。段Dが最適化していない軸なので、ユーザーが見る出力で測るのが正しい | この修正を入れると、現行モデルの合否は変わらない(raw top-1 でも G4 98.3% / G5 75% で通る)。 変わるのは**将来モデルが劣化したときに気づけるかどうか**である。