# ツール実行結果のサイズ上限 ## 背景 Pod のセッションで、Glob が `pattern:"*"` でプロジェクト全体を走査し、 約 125KB / 推定 70k トークン超の tool_result を返した結果、次ターンのリクエストが 組織レートリミット(30k input tokens/分)を単発で超過して永久に 429 で詰む事故が発生した。 一度肥大化した履歴は prune/compact が走る前に再送され続け、待っても抜けられない。 根本原因はツールが「呼ばれた通りの結果を素直に全部返す」こと。ツール自身の件数上限 (例: `Glob` の 1000 件)はバイト/トークン単位の上限ではないため機能しない。 ツール実行結果のサイズを LLM に投げる前に強制的にキャップし、LLM に 「検索範囲を絞れ」と促す必要がある。 ## 要件 - **単一チョークポイントで全ツールに効く**: 個別ツールの実装を信用しない。 Tool 実行境界(llm-worker の Tool runner)で `ToolOutput.content` をトークン計測し、 上限を超えていたら切り詰めてから履歴に積む。 - **マニフェストで設定可能**: デフォルトは 5000 トークン(30k/分レートリミットに 対して余裕を持った値)。プロジェクトごと・ツールごとに上書き可能。 - **切り詰め後は LLM が検知できる**: `summary` 又は `content` 末尾に `truncated: N tokens dropped, refine your query` 形式の追記を入れ、 LLM が自発的に絞り込みを試みるヒントにする。`ToolError` にはしない (エラーにすると LLM がリトライループに入りやすい)。 - **計測は既存の `token-counter` クレートを使う**。文字数やバイト数で近似しない。 ## マニフェスト ```toml [worker.tool_output] # 全ツール共通の既定上限。省略時 5000。 default_max_tokens = 5000 # ツールごとの上書き。ツール名は登録名("Glob", "Read", ...)。 [worker.tool_output.per_tool] Read = 8000 # Read は大きいファイルを意図的に返すので少し緩める Grep = 3000 ``` - `[worker.tool_output]` セクション自体は省略可能。省略時はデフォルト 5000 が全ツールに適用。 - `per_tool` も省略可能。 - 未知のツール名がマップに含まれていても manifest エラーにはしない(ログ警告のみ)。 ## 実装方針(実装順序) 1. `token-counter` を llm-worker の依存に追加(まだなら)し、Tool 実行境界の `ToolOutput` を計測する薄いラッパーを導入 2. 超過時の切り詰めロジック(content を二分探索などでトークン数ぎりぎりまで詰め、 末尾に注記を追記) 3. `manifest::WorkerManifest` に `tool_output: Option` を追加、 llm-worker の `Worker` 生成時に渡す 4. 各ツール単体には本チケットでは手を入れない。上限を踏んだツールに対して 後続の改善(Glob が `git_ignore` を尊重する等)は別チケットで扱う ## 非ゴール - **ツール固有の賢い縮退**(Glob が件数で、Read が行範囲で、など)は扱わない。 まず一律上限で事故を止め、各ツールの自主制限は必要に応じて別チケットで追加する。 - **prompt caching の導入**や compaction 側の改善は扱わない。 本チケットは「1 回のツール結果が履歴に載る前にキャップする」ことだけに集中する。 - **入力側(ツール引数)のサイズ制限**は扱わない。