yoi/tickets/compact-improvements.md
2026-04-13 02:08:25 +09:00

4.8 KiB
Raw Blame History

Compact の改善

背景

Pod::compact() とその周辺機構CompactInterceptor, CompactState, Controller 統合)は 実装済み。挙動の詳細に未決定事項が残っており、要約品質にも改善余地がある。


1. 要約入力の改善

現状の問題

build_summary_prompt() が全 Item をフラットにテキスト化して LLM に投げている。

  1. データが多すぎる: ToolResult の contentファイル内容、grep 結果等)を含めている
  2. 単一関心事の前提: "Original Task" が1つだけ。タスク切り替わりに対応できない

Phase 1: 入力データの削減

build_summary_prompt で渡すデータを絞る:

fn build_summary_prompt(items: &[Item]) -> String {
    for item in items {
        match item {
            Item::ToolResult { summary, .. } => {
                // content を含めない。summary だけ
                lines.push(format!("[ToolResult] {summary}"));
            }
            Item::ToolCall { name, .. } => {
                // arguments を含めない。ツール名だけ
                lines.push(format!("[ToolCall] {name}"));
            }
            Item::Reasoning { .. } => {
                // skip内部思考は要約に不要
            }
            // User/Assistant のテキストはそのまま
        }
    }
}

Phase 2: 要約フォーマットの改善

タスク切り替わりを反映する:

## Tasks
### Task 1: (最初のユーザー指示)
- 完了した作業
- 判明した事実

### Task 2: (次のユーザー指示)
- 完了した作業
- 判明した事実

## Current State
- (変更されたファイル、残タスク)

Phase 3: マルチターン要約 Worker

1リクエストで全部読ませるのではなく、要約 Worker にツールを持たせて自律的に要約させる。

要約 Worker:
  system: 「セッションログを読んで構造化要約を生成せよ」
  ツール: read_session_segment(offset, limit)

利点:

  • 巨大セッションでもコンテキストに収まる
  • Worker が自分で「重要/不要」を判断できる
  • タスク切り替わりを検出し、関心事ごとに要約できる

builtin-tools チケットとの依存あり。


2. 挙動の未決定事項

現在の挙動

トリガー2段階:

  1. ターン間 (CompactInterceptor): input_tokens > turn_threshold → Yield → compact + resume
  2. run 後 (Controller): input_tokens > post_run_threshold (×9/8) → best-effort compact

安全機構:

  • サーキットブレーカー: 3回連続失敗で無効化
  • Thrash 検出: compact 直後に再び閾値超過 → CompactThrash エラー
  • Yield 前の永続化: persist_turn を compact の前に実行

2-1. Yield のタイミング精度

現状: pre_llm_request でチェック = ターンの切れ目でしか発火しない。 1ターン内でツール呼び出しが多く、途中でコンテキストが膨らむケースは次のターンまで待つ。

検討:

  • ツール実行後にもチェックする?(post_tool_call で Yield 相当の処理)
  • 現状の「ターン切れ目のみ」で十分か?

2-2. 閾値の導出

  • turn_threshold = マニフェストの compact_threshold そのまま
  • post_run_threshold = turn_threshold * 9 / 8(≈ 112.5%

9/8 の根拠はない(安全マージン)。マニフェストで個別指定可能にする?

2-3. Prune と Compact の相互作用

pre_llm_request:
  1. PruneHookcontent を除去)
  2. CompactInterceptorトークン数チェック

Prune はリクエストコンテキストのみ操作し、last_input_tokens は前回の LLM レスポンスの値。 Prune の効果は last_input_tokens に反映されず、Compact の判断には影響しない。 → Prune で十分に縮んでも Compact が走る可能性がある。保守的で実害は小さい。

2-4. compact 中のクライアント通知

compact は LLM 呼び出しを伴う。この間 Controller は Pod を占有。 AlreadyRunning エラーで弾かれる。Protocol チケットの CompactStart/CompactDone で対応。

2-5. 復元時の挙動

Outcome::Yielded で記録されたセッションを restore した場合:

  • last_run_interrupted = true で復元
  • Pod は resume 可能(通常の interrupted セッションと同じ)
  • compact 後の新セッションが存在する場合、どちらを restore するかは呼び出し側の責任
  • compacted_from で辿れる

実装順序

  1. Phase 1content/arguments/reasoning 除去)→ build_summary_prompt の変更のみ
  2. 挙動の未決定事項 → 実運用でのフィードバックを元に判断
  3. Phase 2フォーマット改善→ チューニング
  4. Phase 3マルチターン要約→ builtin-tools 後