ツール多用型ワークフローのコストを削減する
ツールを使用するエージェントは、すべてのリクエストで毎回すべてを再送信します。 プログラムによるツール呼び出しとプロンプトキャッシュは、それぞれコストの異なる部分を 削減します。
このビデオのデモは2026年9月に収録されました。Claude Sonnet 5(claude-sonnet-5)上で実行されています。新しいモデルでは結果が異なる場合があります。
ツールを使用するエージェントは、すべてのリクエストで毎回すべてを再送信するため、ツール呼び出しのたびにコストが増加します。 各リクエストには、ツール定義、システムプロンプト、質問、そしてこれまでのすべてのツール結果が含まれます。これらが入力トークンであり、それぞれに課金されます。
プログラムによるツール呼び出しとプロンプトキャッシュは、どちらもこのコストを削減します。
Meridianは、40人のエンジニアを擁する架空のエンジニアリング コンサルティング会社です。そのエージェントは1つの質問を受けました。2026年6月に稼働率が70%未満だったエンジニアは誰か、というものです。エージェントには2つのツールがありました。list_employeesは名簿を返し、get_employee_hoursは1人のエンジニアのタイムシートを返します。
その質問に答えるために、エージェントは42回のリクエストと1,124,799個の入力トークンを要しました。実行全体のトークンコストは2.29ドルでした。
各リクエストの入力トークンを測定する
レスポンスが返ってくるたびにusage.input_tokensを出力してください。その数値を見れば、コストがどこに発生しているかがわかります。
resp = client.messages.create(...)
print(resp.usage.input_tokens, "input tokens")Meridianのエージェントは古典的なループを実行しており、各レスポンスが1回のツール呼び出しを要求します。そのうちの2つの数値を示します。
| リクエスト | 含まれる内容 | 入力トークン |
|---|---|---|
| リクエスト1 | ツール定義、システムプロンプト、質問 | 606 |
| リクエスト37 | 同上に加えて、それ以前の36件のツール結果 | 47,905 |
リクエスト1には、どのリクエストにも含まれる内容しか含まれていません。後続のリクエストには、ツール呼び出しとその結果が加わっていきます。どちらの部分が増加したかを見るために、2つの数値を比較してください。
ツール結果をClaudeが読む内容から除外する
プログラムによるツール呼び出し(新しいタブで開きます)を使うと、Claudeはあなたのツールを呼び出すスクリプトを書き、コード実行ツールがそれをコンテナ内で実行します。スクリプトが出力した内容だけがClaudeに届きます。 Meridianの40件のタイムシートはスクリプト側にとどまり、Claudeはその短い出力結果だけを受け取ります。
まず、ご自身のツールとモデルを確認してください。 2026年10月時点で、プログラムによるツール呼び出しはstrict: trueとマークされたツールをサポートしていません(新しいタブで開きます)。また、Claudeを各レスポンスで1回のツール呼び出しに限定するtool_choice設定であるdisable_parallel_tool_useもサポートしていません。tool_choiceは、Claudeにスクリプトからツールを呼び出すよう強制することはできません。
Claude Haiku 4.5はこの機能をサポートしていません。この機能は、Claude API、Claude Platform on AWS、およびAnthropicでホストされているMicrosoft Foundryデプロイメントで動作します。
プログラムによるツール呼び出しを有効にするには、4つの変更が必要です。
- コード実行ツールを
toolsリストに追加します。 - スクリプトが呼び出せるツールにマークを付けます。 各ツールに
allowed_callersフィールドを設定し、バージョンcode_execution_20260120以降(新しいタブで開きます)のコード実行ツールを指定します。 - コンテナのIDを送り返します。 ツール呼び出しで停止する各レスポンスからそれを読み取り、ツール結果を返すリクエストと一緒に送信します。
- 各ツールの結果を記述します。 スクリプトがそれを解析する必要があるため、記述の中でその形状を説明してください。以下は、
get_employee_hoursに対するMeridianの記述です。
Returns one engineer's hours for the month as JSON: {"name": str, "billable_hours": float, "utilization": float 0-1}
これらの変更を加えた後のMeridianのループを示します。
CODE_EXECUTION = "code_execution_20260120"
TOOLS = [
{"type": CODE_EXECUTION, "name": "code_execution"},
{**LIST_EMPLOYEES, "allowed_callers": [CODE_EXECUTION]},
{**GET_EMPLOYEE_HOURS, "allowed_callers": [CODE_EXECUTION]},
]
messages = [{"role": "user", "content": QUESTION}]
container = None
while True:
resp = client.messages.create(
model="claude-sonnet-5", max_tokens=4096,
tools=TOOLS, messages=messages, container=container,
)
print(resp.usage.input_tokens, "input tokens")
if resp.stop_reason not in ("tool_use", "pause_turn"):
break
container = resp.container.id
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason == "tool_use":
messages.append({"role": "user", "content": run_tools(resp)})
print("".join(b.text for b in resp.content if b.type == "text"))LIST_EMPLOYEESとGET_EMPLOYEE_HOURSは、Meridianのツール定義です。- 独自の
run_toolsが各ツール呼び出しを実行し、結果を返します。 pause_turnになると、ループはレスポンスを送り返し、続行します。
スクリプトがツールを呼び出すと、レスポンスのstop_reasonは"tool_use"になります。あなたのコードがツールを実行し、その結果はClaudeではなくスクリプトに渡されます。スクリプトはツールを並行して実行できるため、1つのレスポンスに複数のツール呼び出しが含まれることがあります。
同じ質問を両方の方法で答えた結果を示します。
| 実行 | リクエスト数 | ツール呼び出し数 | 入力トークン | トークンコスト |
|---|---|---|---|---|
| 古典的ループ | 42 | 41 | 1,124,799 | $2.29 |
| プログラムによるツール呼び出し | 3 | 41 | 28,486 | $0.0667 |
どちらの実行も、同じ質問、同じツール、そしてclaude-sonnet-5を使用しています。コストは2026年9月時点の価格(新しいタブで開きます)を使用しており、コード実行時間はトークンとは別に課金されます。
プログラムによるツール呼び出しは、互いに依存しないルックアップに適しています。Claudeが次のツール呼び出しを行う前に各結果について推論しなければならない場合には、あまり効果がありません。
繰り返されるツール定義をキャッシュする
プロンプトキャッシュを使うと、APIはリクエストのプレフィックス、つまりマークしたキャッシュポイントまでの部分を保存します。そのプレフィックスを再度読み取るコストは、はるかに低くなります。
2つ目のMeridianエージェントには14個のツールがあり、その定義は3,305トークンに及び、それがすべてのリクエストで毎回再送信されます。
最後のツールにcache_controlを付けることでキャッシュポイントをマークすると、リスト全体がキャッシュされます。 これにはたった1行で済みます。
TOOLS[-1]["cache_control"] = {"type": "ephemeral"}Claude Sonnet 5では、プレフィックスを読み取るコストは通常の入力価格の10分の1(新しいタブで開きます)です。書き込みには、通常の入力価格の1.25倍のコストがかかります。
キャッシュされたプレフィックスはデフォルトで5分間持続し、使用するたびに更新されます。間隔がもっと空くリクエストの場合は、cache_controlに"ttl": "1h"を追加すると、1時間保持できます。1時間のキャッシュを書き込むコストは通常の入力価格の2倍(新しいタブで開きます)です。
Meridianは、同じ8つの質問をキャッシュありとキャッシュなしの両方で実行しました。入力コストの合計は、0.0554ドルから0.0154ドルに下がりました。
自動キャッシュ(新しいタブで開きます)を使うと、リクエストのトップレベルにcache_controlフィールドを1つ置くだけで、APIがキャッシュポイントを自動的に配置してくれます。
キャッシュを有効にしたら、次の3点に注意してください。
- ツール定義を少しでも変更すると、キャッシュ全体が失われます。
- Sonnet 5では、キャッシュされるプレフィックスは少なくとも1,024トークンである必要があります。それより短い場合、何もキャッシュされず、エラーもそのことを知らせません。
- キャッシュを有効にすると、
input_tokensにはキャッシュされたプレフィックスが含まれなくなるため、リクエストの実際の合計は、input_tokens、cache_read_input_tokens、cache_creation_input_tokensを合計したものになります。
適した場面で機能を組み合わせる
次の順序で追加してください。
- まずキャッシュから始めます。 たった1行で済みます。
- プログラムによるツール呼び出しをテストします。 通常のトラフィックの一部を
allowed_callersありとなしの両方で実行し、各実行の課金対象入力トークンを比較します。
以下の2つのMeridianリクエストそれぞれについて、入力トークンの大部分を占める部分を見つけ、それを削減する機能を選んでください。
重要なポイント
- すべてのリクエストは毎回すべてを再送信するため、古いツール結果に対して何度も課金されることになります。
- レスポンスが返ってくるたびに
usage.input_tokensを出力し、リクエストのどの部分が最もコストがかかっているかを把握しましょう。 - キャッシュから始めましょう。リストの最後のツールに
cache_controlを追加します。 - ツール呼び出しが互いに依存しない場合は、コード実行ツールと
allowed_callersを使ってプログラムによるツール呼び出しを設定しましょう。以降のすべてのリクエストにコンテナのIDを添えて送信します。