20 名のユーザーの月次経費を一気に集計したい、50 個のエンドポイントの health を並列で見たい、外部 API を 10 回叩いて差分を取りたい。こうした「ツール連打」を従来の tool use で書くと、毎回 Claude を再サンプリングするラウンドトリップが積み上がり、中間データもすべて context window に流れ込みます。
これを根本から畳むのが Programmatic Tool Calling (PTC) です。Claude が Python のスクリプトを書き、コード実行コンテナの中から「Python の関数」としてツールを呼び出します。最終結果だけが Claude の context に戻るため、レイテンシもトークンも 1 桁単位で削れます。本記事では、仕組み・使い所・制約を整理します。
到達点
- PTC が解決する「ラウンドトリップ問題」と「中間データ膨張問題」を理解する
code_execution_20260120とallowed_callersの組み合わせで PTC を有効化する- 単純な tool use と PTC をいつ使い分けるかの判断軸
- 対応モデル(Opus 4.7 / 4.6 / 4.5、Sonnet 4.6 / 4.5)と Claude API での GA 状況
仕組み — Claude がスクリプトを書く側にまわる
通常の tool use:
[user message] → Claude → tool_use → tool 実行 → tool_result
→ Claude → tool_use → tool 実行 → tool_result
→ Claude → tool_use → ...
各往復で Claude を再サンプリングし、tool_result が context に積み上がります。20 ステップなら 20 回のラウンドトリップ。
PTC の場合:
[user message] → Claude → server_tool_use (code_execution: 1 本のスクリプト)
↓
container 内で script 実行
├─ tool_use (caller=code_execution_20260120) ─ tool 実行 ─ result
├─ tool_use (caller=code_execution_20260120) ─ tool 実行 ─ result
├─ ...
└─ Python で集計 / フィルタ / 早期 break
↓
code_execution_tool_result (最終 stdout のみ)
↓
Claude → 最終応答
Claude は最初に “スクリプト 1 本” を書き、container がツールを順次呼び出すだけ。Claude は最終 stdout を 1 度受け取って応答するだけです。
効果
公式ドキュメントの数値を引用すると、20 名の経費チェックの例では:
「20 separate model round-trips」(従来) → 「a single script runs all 20 lookups, filters the results, and returns only the employees who exceeded their limits」(PTC)
加えて、agentic 検索ベンチマーク(BrowseComp や DeepSearchQA)で PTC を加えるだけでエージェント性能が大きく上がると公式が明記しています。
対応モデル / 環境
PTC は code_execution_20260120 に依存し、以下のモデルで利用可能(2026-04 時点):
- Claude Opus 4.7 / 4.6 / 4.5
- Claude Sonnet 4.6 / 4.5
Claude API では GA(beta header 不要)。Bedrock / Vertex AI 経由では beta header が要求されます(Microsoft Foundry も対応)。
有効化 — code_execution ツールと allowed_callers
PTC を成立させる鍵は 2 つだけです。
toolsにcode_execution_20260120を追加する- PTC で呼ばせたいツールに
allowed_callers: ["code_execution_20260120"]を付ける
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-4-7",
max_tokens: 4_096,
messages: [
{
role: "user",
content:
"Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue",
},
],
tools: [
// 1) code execution を有効化
{ type: "code_execution_20260120", name: "code_execution" },
// 2) PTC で呼ばせたいユーザー定義ツール
{
name: "query_database",
description:
"Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
input_schema: {
type: "object",
properties: {
sql: { type: "string", description: "SQL query to execute" },
},
required: ["sql"],
},
// ★ ここが PTC の指定。
// ["direct"] (省略時の既定) — Claude が直接呼ぶ
// ["code_execution_20260120"] — PTC 専用
// ["direct", "code_execution_20260120"] — 両方可
allowed_callers: ["code_execution_20260120"],
},
],
});
allowed_callers の意味は強めの推奨があります — ツール 1 つにつき direct か code_execution のどちらか片方に絞るほうが Claude の判断が明確になる、と公式 Tip にあります。
ハンドラ側の注意 — レスポンスは tool_result のみ
PTC の tool 呼び出しに応答するとき、メッセージは tool_result ブロックだけでなければなりません。text を混ぜると 400 になります。
// ❌ NG — 通常 tool use では許される形だが PTC では invalid
{
role: "user",
content: [
{ type: "tool_result", tool_use_id: "toolu_01", content: "[...rows...]" },
{ type: "text", text: "What should I do next?" }, // ← これがあると 400
],
}
// ✅ OK
{
role: "user",
content: [
{ type: "tool_result", tool_use_id: "toolu_01", content: "[...rows...]" },
],
}
レスポンスを持ち回すときは、server_tool_use(コード本体)と tool_use(caller=code_execution_20260120 つき)の両方を assistant 履歴にそのまま残す必要があります。tool_result の tool_use_id は PTC 側 tool_use の id(toolu_...)を指すのもポイントです。
レスポンス形
通常の tool_use と PTC の tool_use は caller フィールドで見分けられます。
// 通常
{
"type": "tool_use",
"id": "toolu_abc",
"name": "query_database",
"input": { "sql": "..." },
"caller": { "type": "direct" }
}
// PTC
{
"type": "tool_use",
"id": "toolu_xyz",
"name": "query_database",
"input": { "sql": "..." },
"caller": {
"type": "code_execution_20260120",
"tool_id": "srvtoolu_abc" // ← どの code_execution の中から呼ばれたか
}
}
caller.type === "code_execution_20260120" を見ればハンドラ側で PTC 経由か直接呼びかを切り分けられます。
トークン会計が変わる
これが地味に大きな点です。
- PTC 経由の tool_result は input/output token としてカウントされない(公式 Note)
- 課金されるのは 最終
code_execution_tool_resultの stdout と Claude の最終応答 - 中間で 100 件のレコードを取って 5 件にフィルタしても、Claude が見るのはフィルタ後の 5 件だけで、見えないし課金もされない
長い tool ループで context を消費しないということは、プロンプトキャッシュも壊れにくい(本ブログ #8「プロンプトキャッシュ」参照)。
いつ使うか / いつ使わないか
公式が挙げる得意領域:
- 大量データの集計・フィルタ(中間データを Claude に見せる必要がない)
- 3 ステップ以上の依存ある tool 連打
- 条件分岐(中間結果に応じて次の tool を選ぶ)
- 並列操作(50 エンドポイントの health チェック等)
向かない領域:
- 単発の tool 呼び出し
- ユーザーフィードバックを挟みたいツール
- code_execution のオーバーヘッドが効果より大きい超軽量操作
制約・非互換
PTC を有効化したリクエストでは、以下が使えません(明示的に公式が記載):
tools[].strict: true(Structured Outputs の strict tool use と非互換)tool_choiceで特定 PTC ツールを強制することはできないdisable_parallel_tool_use: trueは PTC では無効化される- MCP connector のツールは PTC で呼べない(2026-04 時点、将来対応の可能性あり)
加えて、ZDR (Zero Data Retention) 対象外(コンテナデータが 30 日間保持される)。データ保持要件が厳しい組織では事前に確認が必要です。
落とし穴
allowed_callersを["direct", "code_execution_20260120"]で広く取ると、Claude がどちらを選ぶか不安定になりがち。片方に絞るのが安全- container の idle timeout は 4.5 分。tool 実行が長引くなら、container 寿命を意識してハンドラ側で timeout を下げる
- tool 出力フォーマットを description に詳しく書かないと、Claude のスクリプトが結果を正しく
JSON.parseできないことがある。返却型の構造を description に書く - tool result に text を混ぜて 400 — 上述、PTC 待ちのターンは tool_result のみ
- MCP connector ツールを PTC で呼ぼうとすると仕様外。MCP は通常の direct call として並走させる
まとめ
PTC は、**「tool 連打のラウンドトリップを Claude のサンプリングから切り離す」**ための機能です。allowed_callers を 1 行足すだけで、20 ターンのループが 1 ターンに畳まれ、中間データが context に流れ込まなくなります。長尺の agentic ワークフローや、大量データ集計型のツール群を持っているプロジェクトは試す価値があります。
release notes タグで Anthropic のリリース解説を続けています。次回候補は Memory Tool(/memories 構造のクライアント側ストレージ)または Tool Search(大量ツールから動的に絞り込む)を予定しています。