Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ jobs:
run: curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --tag 1.58.0 --to /usr/local/bin
- run: just test-harness-scripts

triage-tests:
name: session-triage TypeScript tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: '.node-version'
- name: Install just
run: curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --tag 1.58.0 --to /usr/local/bin
- run: just test-triage

actionlint:
name: actionlint
runs-on: ubuntu-latest
Expand Down
105 changes: 105 additions & 0 deletions docs/superpowers/plans/2026-08-27-local-llm-session-triage-results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# セッション・トリアージ 実施結果

日付: 2026-08-28(計画上は 2026-08-27 の合宿向けだったが、実施は翌日に自律実行で行った)

## 環境

- ハード: MacBook Air M4 / 24GB / ファンレス
- モデル: **qwen3:8b を採用**。gold set の低シグナル層 recall が 14B と同値(どちらも 3/3)で、スループットは 8B が約 1.3 倍速いため、計画の判断基準(「recall 同等なら 8B」)通り
- thinking: **無効(`think: false`)で固定**。probe 実測では warm 状態で有意差なし(8B: think=false 3.1s / think=true 2.6s、簡単なプロンプト)。スキーマ強制(`format`)は両設定で有効に働き、`<think>` ブロック・スキーマ外キーの混入なし
- gold set のラベル付けは計画では「人手」だが、自律実行のため **Claude(Layer 3 相当)がダイジェストのみを読んで付けた**。「Claude の判断をローカル LLM が再現できるか」の測定として解釈する

## Layer 1(決定論)

- 対象セッション: **202 件**(pending.jsonl 240 行、transcript 消失で 38 件除外)
- 元サイズ合計: **458.9 MB** → ダイジェスト合計 **1,231,881 文字**(バイト→文字で約 **391 倍**の圧縮)
- ダイジェスト合計トークン: 概算 610,015 → ollama 実測による補正係数 **0.968** → **実測ベース約 59 万トークン**
- **ベースライン①: 1M コンテキストに収まる。** つまり 202 件全件のダイジェストは理論上 1 セッションの Claude に一括投入できる。Layer 2(ローカル LLM)の存在理由は「可能にすること」ではなく、コスト・レイテンシ・プライバシーに置かれる
- 切り詰めたセッション: 43 件(上限 12,000 文字、head+tail 保持)
- コンテキスト溢れなし(calibrate.ts に num_ctx の 90% 超過で fail-loud する検査を実装。最大ダイジェストで実測 6,324 トークン ≪ 16,384)

## Layer 2(ローカル LLM)

- gold set: 20 件(低シグナル 14 / 高シグナル 6)、うち陽性 **9 件**(低シグナル 3 / 高シグナル 6)
- プロンプト v1(計画のまま)は **全 20 件を true と判定する定数分類器**になった(低シグナル偽陽性 11/11)。recall 100% は無意味。原因は「迷ったら true」の指示が支配し、「作業対象コードの問題」と「エージェントプロセスの学び」を区別できないこと
- v2(判定対象の区別を明示、円滑なセッション=false を明記)で識別が働くようになった:

| モデル | 低シグナル層 recall | 低シグナル層 偽陽性 | 高シグナル層 recall | ms/件(gold) | 202 件換算の推定時間 |
|---|---|---|---|---|---|
| qwen3:8b (v2) | **100% (3/3)** | 8 / 11 | 100% (6/6) | 101,065 | 約 340 分 |
| qwen3:14b (v2) | 100% (3/3) | 7 / 11 | 100% (6/6) | 131,554 | 約 443 分 |

注: 計画時点の想定は 238 件だったが、実際の対象は 202 件(transcript 消失 38 件を除外)。推定時間は 202 件換算に直してある。また **recall の 8B/14B 比較は低シグナル層の陽性 n=3 に基づき、この標本サイズでは両者を識別する力がない**。モデル選択は実質スループットのみで決めており、「8B ≒ 14B」の証拠として引用しないこと。

- 実測スループット(バッチ 40 件、ダイジェスト長は全体分布): **平均 50.5 秒/件** → 202 件で約 2.8 時間。gold set は大きいダイジェストに偏っていたため評価時の見積もり(101 秒/件)より速い
- 既知の不具合と対策: temperature 0 で稀に反復縮退ループに陥り、ollama が応答を打ち切って不完全 JSON が返る(実測 1 件)。classify に「パース失敗時は temperature 0.3 + repeat_penalty 1.1 で 1 回リトライ」を実装して回避

## 全件バッチ(40 件で打ち切り)

計画の規定(所要 60 分超なら 40 件に縮小)を適用し、**40 件のみ実行**(33.7 分)。残り 162 件は夜間実行に回す(triage.jsonl は追記のみでレジューム可能)。

- 学びありと判定: **34 / 40 件(85%)**
- severity 内訳: high 1 / medium 33 / low 0 — **severity はしきい値として機能していない**
- category 内訳: rule_drift 32 / user_correction 2 — **category も rule_drift に縮退**

つまり v2 プロンプトでも Layer 2 は「recall 最大・precision 低」のフィルタであり、gold set の偽陽性率(低シグナル層 8/11)がそのまま全体に現れた。**85% を通すフィルタは絞り込みとしてはまだ弱い。**

## ベースライン②(摩擦シグナル数ソート)との比較

- シグナル 0 件のセッション: 60 / 202 件(ベースライン②が構造的に見落とす集団)
- バッチ 40 件のうち、シグナル上位 30 件に入らず LLM が学びありと判定: **7 件**
- gold set では、低シグナル層の真の陽性 3/3 をローカル LLM がすべて検出(シグナル数ソートではこの 3 件は上位に来ない)。**上乗せは存在する**
- ただし但し書き: 85% を true とする分類器なので「シグナル外から拾った 7 件」の多くは偽陽性の可能性が高い。gold set の低シグナル層の真陽性率は 3/14(21%)であり、7 件のうち真の学びは 1〜2 件程度と見込むのが妥当

## Layer 3 接続試験

- レポート上位 2 件を読んで判断: 1 件目(severity high)は **学び自体が既に llm-wiki に記録済み**(evidence にその旨が書かれていた)ため棄却、2 件目は通常の検証作業を rule_drift と誤認した偽陽性のため棄却。**Layer 3 の棄却役が設計通り機能することを確認**
- 陽性経路: gold set で真と分かっているセッション(サブエージェント完了待ちを timed-out Bash で約 20 回ポーリングした anti-pattern)がバッチ内で検出されており、digest の evidence 該当箇所だけを読んで **queue.md に英語エントリを 1 件追記**(harness-reflect の書式通り)
- pending.jsonl からの処理済み 3 件の削除(SKILL.md の作法: `grep -vF` → temp → `mv`)は、**auto mode の permission classifier にブロックされ未実施**。queue.md には 0f5c1a3c のエントリが既に入っているのに pending には残っているため、**次回の /harness-reflect が同じセッションを再分析し、同一 anti-pattern の重複エントリを queue に積む**。それを避けるには手動で 3 件(0f5c1a3c / 03d498dd / 012af754)を pending.jsonl から削除しておく必要がある

## 結論

- **ベースライン①の答え: ダイジェスト全 202 件(約 59 万トークン)は 1M コンテキストに収まる。** Layer 1(決定論・391 倍圧縮)だけで「実現可能性」の問題は解決しており、Layer 2 の価値はコスト・レイテンシ・プライバシー(transcript をクラウドに送らない)に限定される
- **ローカル LLM の上乗せは「ゼロではないが弱い」。** 低シグナル層の真陽性 3/3 を検出しており、シグナル数ソートでは拾えない集団に届く。一方 precision が低く(85% を true 判定)、severity/category も縮退しているため、「上位 N 件だけ読む」という Layer 3 の入力絞り込みには現状そのまま使えない
- **常用するなら次に要るもの:** (1) precision を上げるプロンプト v3(few-shot で偽陽性の典型例=「作業対象コードの発見」を示す)、(2) severity の較正または別の優先度キー(例: シグナル数 × LLM 判定の合成)、(3) 残り 162 件の夜間バッチ、(4) それでも 1 件 50 秒かかる点の判断(夜間なら許容)

## 追記(2026-08-28 後半): プロンプト v3 と severity 較正

初回実施の積み残しだった「precision 改善」と「severity 較正」を同日中に実施した。

### 測定基盤の拡張

gold set を 20 → **40 件**に拡張(`pick-gold.ts --extend` で既存 20 件を除外した同一手法の層化抽出、ラベルは初回と同じく Claude がダイジェストのみで付与)。追加 20 件の陽性は 1 件のみ(高シグナル層)で、**低シグナル層の陽性は n=3 のまま**。偽陽性側の検出力は倍増(低シグナル陰性 11 → 25 件)したが、recall 側の検出力は増えていない。

### プロンプト調整の経過(全て qwen3:8b・40 件 gold)

| 版 | 低シグナル recall | 低シグナル偽陽性 | 高シグナル recall | severity 分布 |
|---|---|---|---|---|
| v2(参考・20件gold) | 3/3 | 8/11 (73%) | 6/6 | medium 33/34 に縮退 |
| v3(反例few-shot+ルール範囲限定+severityアンカー) | **2/3** | 13/25 (52%) | 7/7 | medium 22 / high 4 |
| v3.1(+手段切り替え=true の正例) | **2/3** | 11/25 (44%) | 7/7 | medium 20 / high 3 |
| v3.2(+割り込みリテラルの明示) | **2/3** | **6/25 (24%)** | 7/7 | **medium 12 / high 6** |

同一テストセットでの比較(旧 gold の低シグナル 14 件のみで数えた like-for-like): 偽陽性 **8/11 (v2) → 1/11 (v3.2)**。

**プロンプト単独では recall が退行した(3/3 → 2/3)。** v3 は ab1f3e6e(SSH ブロック→gh api 切替)を落とし、v3.1 の正例追加でそれを回復した代わりに 53e184b1(ユーザー割り込み→言い直し)を落とし、v3.2 で `[Request interrupted by user]` リテラルを明示しても 53e184b1 は 3 回連続で拾えなかった。**このクラスを 8B に安定して持たせることはできない**というのが 3 イテレーションの結論。

### 決定論オーバーライドによる recall の回復

割り込みは Layer 1 が `signals.interrupts` として決定論的に数えており、gold set 上は interrupts≥1 の 5 件が 5 件とも真の陽性(偽陽性ゼロ)、全 202 件中でも 19 件しかない。そこで Layer 3 候補の選択規則を **`LLM true ∪ interrupts≥1`**(`report.ts` の `isLayer3Candidate`)とし、recall の保証をモデルからシグナルに移した。

複合ルールでの gold set 成績(v3.2): **低シグナル recall 3/3・偽陽性 6/25 / 高シグナル recall 7/7・偽陽性 3/5**。承認済みの合格基準(recall 現状維持 + 低シグナル偽陽性 73% → 40% 以下)を複合ルールで達成。

注意書き: オーバーライドの「偽陽性ゼロ」は gold set 内の割り込みセッション 5 件に基づくもので、全 19 件で成立するかは未検証。`evaluate.ts` は LLM 単独と複合ルールの両方を出力するようにし、`report.ts` はベースライン②比較を LLM 単独判定のみで数える(オーバーライド分をここに混ぜるとシグナル規則の選出で LLM の上乗せを水増しする自己矛盾になるため)。

### 積み残しへの影響

- `triage.jsonl` の既存 40 件は **v2 プロンプトの判定**。v3.2 での再バッチが必要(triage.jsonl を退避してから再実行)
- severity は縮退が解消したが、ランキングは複合キー(severity + シグナル数)で保険をかけた

## 積み残し

- 残り 162 件のバッチ実行(`node dot_claude/scripts/triage/batch.ts --model qwen3:8b` で夜間にレジューム実行)
- プロンプト v3(precision 改善)と severity の較正
- pending.jsonl からの処理済みエントリ削除(permission classifier により本セッションでは未実施)
- cron 化(launchd)・/harness-reflect からの自動呼び出し・埋め込み検索(計画通りスコープ外)
Loading