From eec33aba988499b1afbbd37d71e96217a00af22c Mon Sep 17 00:00:00 2001 From: Hare Date: Mon, 11 May 2026 01:14:59 +0900 Subject: [PATCH] docs(tickets): add memory-extract-occupancy-cap ticket --- TODO.md | 3 ++ tickets/memory-extract-occupancy-cap.md | 55 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 tickets/memory-extract-occupancy-cap.md diff --git a/TODO.md b/TODO.md index e7e5ee52..5ec5dc54 100644 --- a/TODO.md +++ b/TODO.md @@ -24,9 +24,12 @@ - セッションコンテキスト長 / ウィンドウ占有率の常時表示 → [tickets/tui-context-usage-indicator.md](tickets/tui-context-usage-indicator.md) - Manifest: Tool Output / File Upload 上限の分離とデフォルト緩和 → [tickets/manifest-output-upload-limits.md](tickets/manifest-output-upload-limits.md) - Prune: 保護境界を turn 数から末尾 token budget に置き換え → [tickets/prune-token-budget.md](tickets/prune-token-budget.md) +- Compact worker サーキットブレーカーを占有量ベースに統一 → [tickets/compact-worker-occupancy-cap.md](tickets/compact-worker-occupancy-cap.md) - メモリ機構 - 使用頻度メトリクス + Knowledge 化候補レポート → [tickets/memory-usage-metrics.md](tickets/memory-usage-metrics.md) + - Phase 1/2 呼称を extract/consolidation に統一 → [tickets/memory-phase-naming.md](tickets/memory-phase-naming.md) - extract / consolidation 監査ログ → [tickets/memory-audit-log.md](tickets/memory-audit-log.md) + - extract worker サーキットブレーカーを占有量ベースに統一 → [tickets/memory-extract-occupancy-cap.md](tickets/memory-extract-occupancy-cap.md) - セッション内 Task ツールの注意機構(無アクティビティで `` ナッジ) → [tickets/session-todo-reminder.md](tickets/session-todo-reminder.md) - ワークスペースのメモリーをLintするヘッドレスCLI - system-reminder 注入機構の汎用化(2件目の利用者が出た時に検討。タグ形式 `...` の規約は session-todo-reminder で先行確立。注入された Item は worker.history に append する方針) diff --git a/tickets/memory-extract-occupancy-cap.md b/tickets/memory-extract-occupancy-cap.md new file mode 100644 index 00000000..1ac77068 --- /dev/null +++ b/tickets/memory-extract-occupancy-cap.md @@ -0,0 +1,55 @@ +# Memory: extract worker のサーキットブレーカーを占有量ベースに統一 + +## 背景 + +`compact-worker-occupancy-cap` で compact worker のサーキットブレーカーを `UsageTracker` + `llm_worker::token_counter::total_tokens` ベースに切り替えたが、同じ設計上のバグを抱えた `MemoryExtractWorkerInterceptor` が `crates/pod/src/pod.rs:2180-2199` に残っている。実装コメントには `Mirror of compact::worker::CompactWorkerInterceptor;` と書かれており、compact 側が直った今、コメントも事実と乖離している。 + +具体的には: + +- `MemoryExtractWorkerInterceptor` は `input_so_far: AtomicU64` に `UsageEvent.input_tokens` を `fetch_add` し、`pre_llm_request` で累積値が `max_input_tokens` を超えたら `Cancel` する (`pod.rs:1930-1942` で配線、`pod.rs:2186-2198` で判定)。 +- `UsageEvent.input_tokens` は cache hit を含む prompt prefix 全体の占有量 (`crates/llm-worker/src/llm_client/event.rs:76-94`)。同じ prefix を毎ターンフルカウントするため、cache が 99% ヒットしても累積値は context size × ターン数で増える。 +- 結果は compact と同じ:cap が「現在占有量」ではなく「累積(≒ context × turns)」を測っており、設計と整合しない。 + +extract worker は compact ほどツールループが深くない(`extract.write_extracted_tool` のみ、ファイル探索なし)ため誤発火頻度は compact より低いものの、構造的な誤りは同じであり、放置すると今後ツールが増えた瞬間に再発する。 + +メイン Pod のしきい値群と compact worker (修正済み) はどちらも `Pod::total_tokens()` (`pod.rs:146-149`) 経由で「現在の占有量」を見ており、extract worker だけ別ロジックという歪みも残っている。 + +## 方針 + +`MemoryExtractWorkerInterceptor` を、compact 側と同じ `UsageTracker` + `llm_worker::token_counter::total_tokens` 機構に乗せ替える。累積メトリックは廃止する。compact と対称に `extract_worker_max_turns` を新設し、`Worker::set_max_turns` 経由で extract worker に伝える。 + +## 要件 + +- `MemoryExtractWorkerInterceptor` の `pre_llm_request` を `llm_worker::token_counter::total_tokens(context, &records).tokens > max_input_tokens` ベースに置き換える。`input_so_far: AtomicU64` の累積パスは廃止。 +- Extract worker にも `UsageTracker` を持たせ、`pre_llm_request` で `note_request(context.len())`、`on_usage` で `record_usage(event)` する。compact worker (`pod.rs:1535-1547` 周辺) と同じ配線パターン。 +- `extract_worker_max_input_tokens` の意味を「extract worker 側の現在占有量しきい値」に変更し、ドキュメント (`crates/manifest/src/lib.rs:111-115`, `crates/manifest/src/defaults.rs:54-56`, 関連 `docs/`) を更新する。デフォルト値 30000 は新セマンティクスで再評価可能だが、変更必須ではない。 +- `MemoryConfig` に `extract_worker_max_turns: Option` を追加し、cascade (`crates/manifest/src/config.rs`) に通したうえで extract worker 構築箇所 (`pod.rs:1924-1942`) で `extract_worker.set_max_turns` に渡す。デフォルトは要検討(仮: `Some(8)` — extract は本来 1-2 ターンで終わる軽量 worker のため compact より小さく)。 +- compact 側で消した「Mirror of `CompactWorkerInterceptor`」というコメントを削除し、独立した interceptor として doc を整える(実体はほぼ同じだが、ロジック共有のための共通化はやらない: subsystem ごとにメッセージとしきい値が異なる前提を維持)。 +- 後方互換 shim は入れない。`extract_worker_max_input_tokens` はフィールド名を維持しつつセマンティクスだけ差し替える。 + +## 完了条件 + +- 通常の extract 実行で、cache hit 込みの prefix がフルカウントされなくなり、現実的な extract 入力 (compaction 区間の skeleton) で cap に当たらない。 +- extract worker のループが暴走するケースで、`extract_worker_max_turns` により打ち切られる。 +- `Pod::total_tokens()` / `CompactWorkerInterceptor` / `MemoryExtractWorkerInterceptor` の 3 者が同じ算出経路 (`llm_worker::token_counter::total_tokens`) を使っている。 +- `MemoryExtractWorkerInterceptor` のユニットテストで「累積では落ちる量でも occupancy では通る」「占有量が cap を超えたら cancel」の 2 パターンが covered されている。 + +## 範囲外 + +- `extract_threshold`(extract 発火条件)のセマンティクス変更。これは「ポインタ以降の cumulative input tokens」で別概念。 +- Consolidation 側の同等変更(consolidation worker は別の lock + iteration 機構で動いており、interceptor 設計が異なる。必要なら別チケット)。 +- `compact_threshold` / `compact_request_threshold` 自体のセマンティクス変更。 +- 共通 `OccupancyInterceptor` を新設して compact / extract で共有する抽象化。サブシステム個別のメッセージ・cap・将来の挙動分岐を残せるよう、現状の重複構造を維持。 + +## 影響範囲 + +- `crates/pod/src/pod.rs`: + - `MemoryExtractWorkerInterceptor` 定義 (`pod.rs:2180-2199`) を occupancy ベースに書き換え、`input_so_far` フィールドを `usage_tracker: Arc` に置換。 + - extract worker 構築箇所 (`pod.rs:1924-1942`) で `UsageTracker` 配線、`set_max_turns` 呼び出し追加。 +- `crates/manifest/src/lib.rs`: + - `MemoryConfig::extract_worker_max_input_tokens` の doc を新セマンティクスに更新。 + - `extract_worker_max_turns: Option` フィールド追加 + デフォルト値関数。 +- `crates/manifest/src/config.rs`: `MemoryConfigPartial` への追加と cascade 解決。 +- `crates/manifest/src/defaults.rs`: `MEMORY_EXTRACT_WORKER_MAX_INPUT_TOKENS` の doc 更新、`MEMORY_EXTRACT_WORKER_MAX_TURNS` 定数の追加。 +- `docs/manifest.toml` 等の memory 設定例: 新フィールドと意味の更新。 +- `crates/pod/src/pod.rs` 周辺もしくは新規モジュールでのユニットテスト(compact 側で追加した 2 パターンを extract 用に)。