Claude Media
task_budgetのremainingでcompaction後の予算を引き継ぐ方法

task_budgetのremainingでcompaction後の予算を引き継ぐ方法

自前でメッセージ履歴を圧縮するとtask_budgetのカウントダウンはサーバー側の記憶を失います。remainingフィールドで消費済みトークン数を渡し続ける実装パターンを解説します。

task_budget(ベータ)のカウントダウンはサーバーが管理しますが、自分のコードでメッセージ履歴を要約・圧縮すると、サーバーは圧縮前にどれだけ予算を使ったか覚えていません。remainingフィールドに消費済みトークン数を渡すことで、カウントダウンをtotalにリセットさせず、圧縮前の続きから再開させられます。

task_budgetはベータ機能で、anthropic-beta: task-budgets-2026-03-13ヘッダーを付けたリクエストでのみ有効になります。remainingはこのtask_budgetオブジェクトの一部なので、圧縮を跨いで予算を引き継ぐループでは、ヘッダーをループ内のすべてのリクエストに付け続ける必要があります。対応モデルはclaude-opus-5claude-fable-5-1claude-mythos-5-1など一部に限られ、Claude CodeやCoworkの利用面では使えません。導入前にこの制約を確認しておくと二度手間になりません。

remainingフィールドが必要になる場面

task_budgetはエージェントループ全体で使えるトークン数(thinking・tool call・tool result・出力すべてを含む)をClaudeに伝える機能で、Claudeはリクエストのたびにサーバー側で注入される予算カウントダウンのマーカーを見て、残り予算に応じてペース配分します。このカウントダウンはモデルにしか見えません。レスポンスのusageオブジェクトに残り予算のフィールドは存在せず、SDKにもアクセサはありません。カウントダウンの実体を持っているのはサーバー側だけです。

ここで問題になるのが、自前のコードで会話履歴を書き換える場合です。長時間のエージェントループでは、メッセージ履歴が肥大化する前に古いやり取りを要約して圧縮する実装がよくあります。この圧縮を自分のコードで行うと、サーバーはその会話が「どこまで予算を使った状態か」を知る手がかりを失います。次のリクエストでtask_budget.totalだけを送ると、カウントダウンはtotalから数え直されてしまい、実際にはすでに大きく消費している予算を、まだ丸ごと残っているかのようにClaudeへ提示することになります。

remainingの計算と受け渡し

remainingtask_budgetオブジェクトにtotalと並べて渡すフィールドで、値は「圧縮によって履歴から取り除いたメッセージぶんの使用量」をクライアント側で追跡して算出します。

# 圧縮前に取り除いたメッセージの使用量(クライアント側で追跡)
tokens_spent_so_far=45000
 
curl https://api.anthropic.com/v1/messages \
  -H "anthropic-beta: task-budgets-2026-03-13" \
  -d '{
    "model": "claude-opus-5",
    "output_config": {
      "effort": "high",
      "task_budget": {"type": "tokens", "total": 128000, "remaining": 83000}
    }
  }'

計算式はremaining = total - tokens_spent_so_farです。tokens_spent_so_farは、履歴から取り除いた(=もう送らない)メッセージぶんの使用量の合計で、測り方はtask_budgetを設定せずにusage.output_tokensを積算する通常の消費量測定と同じです。注意点は、いま送るメッセージにまだ含まれている内容(追加した要約文を含む)はこの合計に含めないこと。それらの使用量はサーバー自身が数えるため、二重計上になります。

tokens_spent_so_farはペイロードサイズではない

remainingの計算でつまずきやすいのが、tokens_spent_so_farを「クライアントが送信したリクエストのバイト数」と混同することです。エージェントループでは毎回フルの会話履歴を再送するため、リクエストのペイロードはターンが進むほど大きくなります。しかしtask_budgetが実際にカウントするのはClaudeが見た(生成した・新たに処理した)トークンだけで、すでに見た履歴を再送しても二重に数えられません。

公式ドキュメントの検証例では、ある1ターンの中で3回のリクエストを送っても、送信ペイロードの合計は約20,820トークンだった一方、予算に計上されたのは19,000トークンでした。差分は、2回目・3回目のリクエストで再送された既出の会話履歴ぶんです。remainingのためにtokens_spent_so_farを自分で見積もるときも、圧縮で取り除いたメッセージの送信バイト量ではなく、Claudeが新規に処理・生成したトークン量(thinking・tool call・tool result・出力の合計)を基準にする必要があります。見積もりの取り方自体は、task_budgetを設定しない状態でusage.output_tokensを積算する通常の消費量測定と同じ考え方です。

サーバー側compactionとクライアント側の圧縮は扱いが違う

remainingが必要になるのは、あくまで自分のコードで履歴を要約・書き換える場合です。Anthropicのサーバー側compaction機能がターンの途中で自動的に発火した場合は事情が異なります。サーバー側compactionはターンの予算を消費前後でリセットせず、圧縮が起きる前にそのターンが消費した分もそのまま予算に計上され続けます。ターン開始前の履歴がターン開始時点のcompactionで要約された場合に限り、その要約以前の分は予算にカウントされません。ただしこの除外はサーバー側compactionを跨いで引き継がれる予算に限った挙動で、それより前のターンの履歴が文脈に残っている間は引き続き予算にカウントされます。

つまり、サーバー側compactionを使っているだけならremainingを意識する必要はなく、サーバーが正しく追跡します。remainingが要るのは、アプリ側の判断で履歴を要約・圧縮し、サーバーの知らないところで会話を書き換えているケースに限られます。

いつremainingを渡し、いつ省略するか

判断基準は「毎回のリクエストで会話履歴を丸ごと再送しているか」です。

実装パターンremainingの扱い
毎回フルの会話履歴を再送する(圧縮なし)remainingの扱い省略してサーバーにカウントダウンを任せる
自前の圧縮・要約で履歴を書き換えるremainingの扱い圧縮のたびに更新し、以後毎リクエストで渡す
サーバー側compactionのみ利用(自前の圧縮なし)remainingの扱い省略可(サーバーが正しく追跡する)

remainingを渡すタイミングにも注意点があります。更新するのは実際に履歴を置き換えたときだけで、リクエストごとにデクリメントするような運用はしません。また、一度圧縮を行ってremainingを送り始めたら、その後のリクエストすべてで(圧縮を伴わないリクエストでも)同じ値を渡し続ける必要があります。渡し忘れた1回だけtotalにリセットされた予算がClaudeに見えてしまうためです。

予算総量(total)自体を増やす場合との違い

remainingが扱うのは「圧縮でどれだけ消費済みかをサーバーに伝え直す」問題で、予算の総量であるtotalはそのまま据え置きです。これとは別に、ユーザーの依頼が途中で広がって予算そのものを増やしたいというケースもあります。task_budgetはリクエスト単位の設定なので、予算を増やすには次のリクエストのoutput_configに新しいtask_budgetをそのまま渡し直します。

この2つは独立した操作で、両方が同時に必要になることもあります。たとえば圧縮を経ながら予算そのものも128,000から180,000へ引き上げたい場合は、totalに新しい値(180,000)を、remainingにはその新しいtotalから圧縮前の消費量を引いた値を渡します。remainingだけを更新してtotalを据え置いたままにすると、予算の引き上げ自体は反映されません。

# totalを128000→180000へ引き上げつつ、圧縮前の消費45000ぶんも引き継ぐ
curl https://api.anthropic.com/v1/messages \
  -H "anthropic-beta: task-budgets-2026-03-13" \
  -d '{
    "model": "claude-opus-5",
    "output_config": {
      "effort": "high",
      "task_budget": {"type": "tokens", "total": 180000, "remaining": 135000}
    }
  }'

プロンプトキャッシュへの影響

予算カウントダウンのマーカーはリクエストごとにサーバー側で注入されるため、そもそも複数リクエストにまたがってキャッシュの対象にはなりません。ここで気をつけたいのは、remainingを毎リクエストで値を変えながら渡す実装がプロンプトキャッシュのプレフィックスに与える影響です。task_budgetの値はレンダリングされたプロンプトの一部として扱われるため、remainingを細かく変化させ続けると、その値を含むキャッシュエントリは前回のリクエストと一致しなくなります。キャッシュを保ちたい場合は、remainingを圧縮のたびにまとめて更新するにとどめ、こまめな微調整はしない運用が無難です。

task_budgetが強制上限ではなくadvisory(助言的)な性質であることや、予算が小さすぎたときにClaudeが依頼を断ったりスコープを縮めたりする挙動については、task_budgetは強制上限でなくadvisoryという性質で扱っています。remainingを使う場合でも、この性質そのものは変わりません。

よくあるつまずき

  • 圧縮したのにremainingを送り忘れる: サーバーは圧縮前の消費量を覚えていないため、totalにリセットされた予算がそのままClaudeに見え、想定より長く走り続けます。
  • 今回のリクエストに含まれる内容までtokens_spent_so_farに加算する: 送信メッセージにまだ含まれる要約文などはサーバー自身が数えるため、二重計上で予算切れが早まります。
  • リクエストごとにremainingを細かくデクリメントする: サーバーが認識しているカウントダウンと二重に減算することになり、実際より早く予算が尽きたとClaudeに見せてしまいます。公式ドキュメントも、こまめな追随ではなく大きめの予算を設定してモデルの自己調整に任せる運用を推奨しています。
  • サーバー側compactionと自前圧縮を混同する: サーバー側compactionだけを使っているならremainingは不要です。渡す必要があるのは自分のコードで履歴を書き換える場合だけです。
  • totalとremainingを別々のタイミングで送る: remainingtask_budgetオブジェクトの中でtotalと一緒に渡すフィールドです。予算を引き上げる更新と圧縮の引き継ぎを別リクエストに分けず、必要な値をまとめて同じtask_budgetに渡します。

まとめ

remainingは、自前のコードで会話履歴を圧縮したときに、サーバー側の予算カウントダウンへ「ここまで使った」という情報を橋渡しするフィールドです。計算式はtotalから圧縮で取り除いた分の使用量を引くだけですが、更新は圧縮のタイミングに限り、以後は毎リクエストで渡し続ける必要があります。サーバー側compaction機能だけを使う場合は不要で、remainingの出番はあくまでアプリ側で履歴を書き換える実装に限られます。

自前の圧縮を導入する予定がまだ無いなら、remainingは最初から使わずフルの会話履歴を毎回再送する構成のままにしておくのが最も単純です。長時間のエージェントループでメッセージ数が増え、履歴の圧縮が避けられなくなった段階で、初めてremainingの受け渡しを実装に組み込む、という順序で導入すると変更範囲を小さく保てます。

この記事を共有:XはてブLinkedIn