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

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

自前でメッセージ履歴を圧縮すると、task_budgetのカウントダウンはtotalからやり直しになります。remainingに予算の残りを渡し続ける計算と更新のタイミングを、数値例つきで示します。

task_budget(ベータ)のカウントダウンはサーバーが数えますが、自分のコードで履歴を要約・圧縮した場合、サーバーは圧縮前の消費量を知りません。そのままtotalだけを送ると、使い込んだ予算が丸ごと残っているように見えてしまいます。

この食い違いを防ぐのがremainingです。渡すのは消費済みの量ではなく、totalから消費済みを引いた予算の残りで、圧縮前の続きからカウントダウンを再開させます。

使える環境と前提

task_budgetはanthropic-beta: task-budgets-2026-03-13ヘッダーを付けたリクエストでだけ有効です。remainingはtask_budgetオブジェクトの一部なので、引き継ぎを行うループでは全リクエストにヘッダーが必要です。

対応モデルは、Claude Fable 5.1・Mythos 5.1・Opus 5.5・Opus 5・Fable 5・Mythos 5・Sonnet 5.5・Opus 4.8・Opus 4.7です。

Sonnet 5・Opus 4.6・Sonnet 4.6・Haiku 4.5は対象外です。Claude CodeとCoworkでも使えません。

totalの下限は20,000トークンで、それより小さい値は400エラーになります。

圧縮すると予算がどう見えるか

Claudeが見ているのは、サーバーが各リクエストに差し込む残りトークンのカウントダウンです。レスポンスのusageに残り予算のフィールドはなく、SDKにも取得手段がありません。モデルにだけ見える数字です。

長いエージェントループでは、古いやり取りを要約して履歴を縮める実装がよくあります。縮めた履歴を送ると、サーバーには「すでにどれだけ使ったか」を示す材料が残りません。total: 128000だけを送れば、45,000トークン使った後でも、カウントダウンは128,000から始まります。

数字

圧縮1回目の数字(例)

  • total

    128,000

    ループ全体の予算

  • 外したぶんの使用量

    45,000

    クライアント側で記録

  • remaining

    83,000

    次のリクエストに渡す値

remaining = total − 履歴から外したメッセージの使用量

remainingの計算と受け渡し

履歴から外したメッセージの使用量を積み上げ、totalから引いた値をremainingにします。使用量の測り方は、task_budgetなしで試走するときと同じです。ループ内の全リクエストのusage.output_tokensを合計し、そこへ途中で足したツール結果のトークン数を加えます。

# 圧縮前に取り除いたメッセージの使用量(クライアント側で記録)
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}
    }
  }'

数字の管理はクライアント側の台帳に任せると、取り違えにくくなります。次のPythonは、更新する場所をcompact()の1か所に絞った例です。

class BudgetLedger:
    def __init__(self, total: int):
        self.total = total
        self.removed_usage = 0  # 履歴から外したメッセージの使用量(累計)
 
    def compact(self, usage_of_removed_messages: int):
        # 履歴を差し替えたときだけ呼ぶ。リクエストごとには呼ばない
        self.removed_usage += usage_of_removed_messages
 
    def task_budget(self) -> dict:
        # 圧縮の有無にかかわらず、毎リクエストの output_config に入れる
        return {
            "type": "tokens",
            "total": self.total,
            "remaining": self.total - self.removed_usage,
        }

呼び出し側は、ベータヘッダーと台帳のtask_budget()を毎回渡すだけです。公式のSDK例と同じclient.beta.messages.streamの形で書くと次のようになります。

ledger = BudgetLedger(total=128000)
 
with client.beta.messages.stream(
    model="claude-opus-5",
    max_tokens=128000,
    output_config={"effort": "high", "task_budget": ledger.task_budget()},
    messages=messages,
    betas=["task-budgets-2026-03-13"],
) as stream:
    response = stream.get_final_message()

値の求め方で間違えやすい2点

1つ目は、送信量を使用量と取り違えること。エージェントループは毎回フルの履歴を再送するので、ペイロードはターンごとに膨らみます。予算が数えるのは、Claudeが新たに生成した・処理したトークンだけで、再送した既出の履歴は二重に数えません。

公式の例では、total: 100000のループが1ターンで3回のリクエストを送り、送信ペイロードは合計で約20,820トークンに達しました。予算に計上されたのは19,000トークンです。差は、2回目と3回目に再送された既出の履歴にあたります。

2つ目は、いま送るメッセージに残っているものを足さないこと。追加した要約文を含め、サーバーが自分で数えます。足すと二重計上になり、カウントダウンが速く減ります。

圧縮を重ねるときの更新と渡し方

更新の頻度は少なく、渡す頻度は多くします。手順は次のとおりです。

手順

圧縮のたびに行うこと

  1. 1

    外すメッセージの使用量を加算する

    履歴から外すメッセージの使用量を、累計に足します。

  2. 2

    履歴を差し替える

    要約を入れた新しい履歴を組み立てます。累計に足すのは外したメッセージだけで、要約文は足しません。

  3. 3

    以後の全リクエストに同じremainingを付ける

    圧縮を伴わないリクエストにも、同じ値を付け続けます。

2回目の圧縮では、累計を使います。1回目で外したぶんが45,000、2回目で外したぶんが30,000なら、remainingは128,000 − 75,000で53,000です。

あゆみ

圧縮2回分の remaining の推移(total 128,000 の例)

  1. 圧縮前remaining を省略

    履歴を丸ごと再送しているあいだは、サーバーが数えます。remainingは付けません。

  2. 1回目の圧縮83,000 を渡し始める

    45,000トークンぶんを外し、以後すべてのリクエストに"remaining": 83000を付けます。

  3. 圧縮の間値は動かさない

    リクエストが続いても83,000のままです。毎回減らす運用にはしません。

  4. 2回目の圧縮53,000 に更新

    さらに30,000トークンぶんを外した時点で、累計75,000から計算し直します。

totalと使用量を同じ物差しで決める

remainingの引き算が意味を持つのは、totalと外したぶんの使用量が同じ基準で測られているときです。totalを決める前に、task_budgetなしで代表的なタスクを何件か走らせ、1タスクあたりの合計トークンを記録します。ループ内の全リクエストのusage.output_tokensに、途中で足したツール結果のトークンを加えた値です。

その分布のp99あたりを出発点にして、上下に試していきます。同じ記録の取り方が、圧縮で外したメッセージの使用量を数える方法にもなります。メッセージごとに生成量とツール結果の量を控えておくと、後から台帳に足せます。小さすぎるtotalは、依頼の拒否や範囲の縮小、途中での打ち切りを招きます。20,000トークンで数時間かかるコーディングタスクを任せる、といった設定です。

max_tokensは別物です。リクエスト単位の厳密な上限で、task_budgetは複数リクエストにまたがる助言的な目安なので、どちらかを小さくする必要はありません。xhighやmaxのeffortでは、max_tokensを64k以上にします。

思考トークンも予算に数えられます。アダプティブシンキングは、予算が減るほど思考量を絞ります。remainingを実際より小さく渡すと、カウントダウンが速く減り、Claudeが早めに切り上げます。

どこまでが1回の予算か

予算の単位は、1回のユーザー指示に対するエージェントループ(1ターン)です。ツール結果を含まないユーザーメッセージが来ると、新しいターンとして新しい予算が始まります。予算を使い切ってClaudeがターンを終えたあと、「続けて」と送り直す場合が典型です。ただし現状は、以前のターンの履歴が文脈に残っているかぎり、そのぶんも新しいターンのカウントダウンに数えられます。

tool_resultブロックを含むメッセージは、同じターンの続きです。テキストが一緒に付いていても、扱いは変わりません。新しいターンの予算と引き継ぎ中の予算を取り違えないよう、台帳はターン単位で持つと整理しやすくなります。

サーバー側compactionなら渡さなくてよい

remainingを渡すのは、アプリが自前で履歴を圧縮するときです。サーバー側compactionはターンの途中で予算をリセットしないので、渡さなくて済みます。引き継ぎの操作は足しません。

くらべる

2種類の圧縮と remaining

remaining が必要

自前の圧縮

アプリが要約を作って履歴を置き換えます。サーバーには消費量の記録がないので、remainingで渡します。

remaining は不要

サーバー側compaction

ターンの途中で起きても、圧縮の前にそのターンが使ったぶんは予算に数えられたままです。ターン開始前の履歴は、開始時点の圧縮で要約されていれば数えません。

後者の除外は、サーバー側compactionを跨ぐ予算に限られます。以前のターンの履歴が文脈に残っているあいだは、予算に数えられ続けます。

予算そのものを増やしたいとき

ユーザーの依頼が途中で広がり、totalを引き上げたい場合は、次のリクエストのoutput_configに新しいtask_budgetを渡し直します。圧縮の引き継ぎとは別の操作で、両方が重なることもあります。

たとえばtotalを128,000から180,000へ上げ、圧縮前の消費45,000も引き継ぐなら、remainingは180,000 − 45,000で135,000です。引き上げるときは、totalを含む新しいtask_budgetをまるごと渡し直します。

# 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}
    }
  }'

プロンプトキャッシュとの関係

予算のカウントダウンはリクエストごとにサーバーが差し込むので、リクエスト間で一致しません。一方、task_budgetの値自体はレンダリングされたプロンプトの一部です。プロンプトキャッシュのプレフィックスにこの値が含まれると、値が変わった時点で一致しなくなります。

remainingを毎リクエスト減らすと、そのたびにキャッシュが外れます。圧縮の瞬間は、メッセージ側も差し替わるタイミングです。値の更新はその瞬間だけにして、以後は固定します。キャッシュを最優先するなら、圧縮を入れず最初のリクエストで予算を一度だけ設定し、サーバー側のカウントダウンに任せる構成が最も外れにくくなります。totalを途中で変えた場合も、古い値で作られたキャッシュには一致しません。

task_budgetは強制上限ではなく、advisory(助言的)な目安です。予算が小さすぎるときに依頼を断ったり範囲を縮めたりする挙動も含め、task_budgetは強制上限でなくadvisoryという性質で扱っています。remainingを使っても、この性質は変わりません。

よくあるつまずき

  • 圧縮したのにremainingを送らない: カウントダウンがtotalからやり直しになり、実際より多く残っているとClaudeに見えます。
  • 圧縮していないループにもremainingを付ける: 履歴を丸ごと再送するだけなら省略して、サーバーに任せます。
  • フル履歴を再送しながらremainingを減らし続ける: モデルには予算が過少に見え、予算に余裕があっても早めに切り上げます。大きめのtotalを置き、モデルの自己調整に任せる運用が公式の勧める形です。
  • 圧縮を伴わないリクエストでremainingを外す: 一度付け始めたら、以後の全リクエストに付けます。外した回だけ、totalのカウントダウンに戻ります。
  • totalとremainingを別のリクエストに分ける: 2つは同じtask_budgetの中で一緒に渡します。

まとめ

自前の圧縮を入れたら、remainingに「totalから外したぶんを引いた残り」を入れ、圧縮のたびに更新し、以後は毎リクエスト同じ値で送ります。サーバー側compactionだけなら、省略して構いません。

圧縮を入れる予定がなければ、フル履歴を再送してremainingを使わない構成が最も単純です。

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