プロンプト予算の強制
返信経路上の1本のパイプライン:プロンプトを見積もり、予算を超えていたら損失の少ない順に3段階を歩き、それでも収まらなければ拒否する。
経緯についての注記
Section titled “経緯についての注記”このページはかつて「圧縮トライアド」を説明していました:Meta-Token/LTSC(無損失のパターン置換)、LLMLingua-2 ブリッジ(非可逆のトークン剪定)、StreamingLLM(KV-cache 退去)。それぞれが専用の MCP ツールを持っていました。
その圧縮器は v1.33 で duduclaw-inference から削除済みです。手動の MCP ツールからしか到達できず、返信経路のどこからも呼ばれず、下記のパイプラインと機能が重複していました。残っていた2つのダッシュボード設定セクション(inference.toml の [llmlingua] と [streaming_llm]。inference.update はまだ受け付けていました)も 2026-09 に削除しました——誰も読まないキーを書いていただけです。
いま存在するものはより狭く、そして常にホットパス上にあります:crates/duduclaw-gateway/src/prompt_compression.rs。
このパイプライン以前、200K の価格の崖はリクエスト送信後に診断されていました:cost_telemetry::record が cost_pressure イベントを上げ、運用者は警告を受け取るものの、次のリクエストはそのまま送信されていました。予算強制はこのループをリクエスト境界で閉じます。
強制はエージェント単位で、設定しなければ無効です:
[budget]max_input_tokens = 150000 # 0 またはキー無し ⇒ 強制は無効cache_guard_min_eff = 0.5 # 下記「キャッシュガード」参照cache_guard_max_overshoot = 0.15prompt_audit::read_max_input_tokens が最初のキーを読みます。ファイル不在・TOML 破損・キー不在はいずれも 0 になり、パイプラインには一切入りません。これは既定でオンのキャッシュガードとは意図的に逆の慣例です。
トークン見積もり
Section titled “トークン見積もり”estimate_tokens はトークナイザではなく CJK 対応のヒューリスティックです:CJK は1コードポイントあたり 1.306 トークン、それ以外は 3.6 文字あたり 1 トークン。この2つの定数は 2026-08 のローカルコーパス校正で得たもので、従来の一律「1トークン=1.5文字」という推定を置き換えました。旧推定は繁体字中国語のワークロードで実使用量を約 22% 過小評価していました。
estimate_request_tokens は system prompt・履歴・送信待ちのユーザーメッセージを合算します。
各段階は (system, history, user) に対する純粋関数で、より小さい版を返すか None(「これ以上は無理」)を返します。呼び出し側は見積もりが予算に収まるまで順に歩きます。
1. turn_trim — ターンごとの末尾トリム。800 文字を超えるターンは先頭 300 文字と末尾 200 文字を残し、間に [trimmed N chars] マーカーを置きます。文字単位のスライスなので CJK がコードポイントの途中で割れることはありません。コスト圧力下では閾値が 800 から 200 に下がります。短い返信は何も失いません。
2. drop_oldest_tool_echoes — 古いツール内容を剥がし、その履歴経路に永続的な取得ハンドルがない場合はそのバイト列を利用不可としてマークします。ツールのエコーは失っても一番安いものです:結果は通常、その直後のアシスタントターンに反映済みだからです。
3. bisect_and_summarize — 非同期の段階。純粋関数の段階が尽きた後、gateway は保護されていない古いターンだけを要約して予算を再チェックします。分割点は partition_turns_for_summary が決めます。
それでも収まらない場合、パイプラインは予算超過のプロンプトを黙って送ることはしません:BudgetExceeded を返し、budget_exceeded イベントを発します。
never-trim セクション
Section titled “never-trim セクション”split_never_trim_sections / is_never_trim_header / never_trim_tokens は、どの段階も触れてはならない system prompt のセクションを切り出します——アイデンティティ、ワークステートの権威ブロック、安全境界。予算は外してもよいが、これらのセクションを予算のために黙って落とすことは許されません。
キャッシュガード
Section titled “キャッシュガード”プロンプトキャッシュが健全なとき、圧縮はタダではありません。履歴の末尾をわずかに書き換えるだけでもキャッシュのキーとなるバイト列が変わり、キャッシュプレフィックスの全面再構築を強います。arXiv:2607.12161 はこの再構築がその領域のオーバーヘッドの約 87% を占めると測定しています。つまり、節約したトークンより余計に払うことがあるということです。
should_skip_for_cache は maybe_compress_history がパイプラインに入る前に参照する決定論的なゲートです:
| 条件 | 既定値 | 効果 |
|---|---|---|
| 直近のキャッシュ効率 | > 50%(cache_guard_min_eff) |
かつ… |
| 予算超過幅 | < 15%(cache_guard_max_overshoot) |
…圧縮を完全にスキップ |
max_input_tokens と違い、このガードは既定で有効です——ファイル不在やキー不在は論文の閾値にフォールバックし、明示的な cache_guard_min_eff = 0 だけが無効化します。エージェント単位のオプトインを求めずに守るべき安全最適化だからです。
CompressionInfo(compressed / compression_stages)は task-local を通じて maybe_compress_history から数フレーム先の cost_telemetry 記録地点まで運ばれます。
意図的にここに無いもの
Section titled “意図的にここに無いもの”うっかり復活させないよう明記します:
- LLMLingua-2 ブリッジ:Python サブプロセスの起動レイテンシは、リクエストごとの同期圧縮に向きません。戻すなら非同期要約器であって、ホットパスではありません。
- Meta-token / LTSC 置換:エージェント側のデコード時間を消費するため、明示的なオプトインのノブにするしかありません。延期であり、予定ではありません。
- KV-cache 退去(StreamingLLM):DuDuClaw が駆動するのは CLI とベンダー API であり、モデルの KV-cache を所有していません。
トークンが実際に減った場所
Section titled “トークンが実際に減った場所”この領域で最大の削減は、実は圧縮ではありませんでした。[runtime] minimal_context(既定オン)が CLI spawn ごとの --tools リストを絞り、--setting-sources project,local を渡すことで、固定オーバーヘッドは spawn あたり 35,892 から 10,974 トークンへ——約 69% 減です。残る最大の固定コストは DuDuClaw 自身の MCP ツールスキーマです。
他システムとの連携
Section titled “他システムとの連携”- セッション記憶スタック — 圧縮サマリーは system prompt に注入され、会話ターンにはなりません。16-session-memory-stack.md を参照。
- CostTelemetry — リクエストごとに
compressed/compression_stagesを記録し、cache_attribution_snapshot()がどのキャッシュブロックがプレフィックスを壊し続けているかを報告します。 - Direct API — 階層化された
cache_controlブレークポイントがCACHE_SPLIT_MARKERで system prompt を分割します。キャッシュガードの効率値が意味を持つのはそのおかげです。
このページの正直な姿は、1本のパイプライン、3つの段階、2つの設定キー、そして明示的な拒否経路です。置き換えられたトライアドは、立派に聞こえるが返信経路から一度も呼ばれなかった3つの戦略でした。