Claude API の Programmatic Tool Calling — tool 連打のレイテンシとトークン消費を 1/10 まで削減する

Programmatic Tool Calling (PTC) は、Claude が Python スクリプトを書いて自分のツールを container 内から呼び出す仕組みです。tool 1 回ごとに Claude を再サンプリングする従来のラウンドトリップが消え、中間結果が context に入らないため、長いツール連打のレイテンシとトークンを 1 桁単位で削れます。仕組みと使い所、allowed_callers の指定、制約までをまとめます。

20 名のユーザーの月次経費を一気に集計したい、50 個のエンドポイントの health を並列で見たい、外部 API を 10 回叩いて差分を取りたい。こうした「ツール連打」を従来の tool use で書くと、毎回 Claude を再サンプリングするラウンドトリップが積み上がり、中間データもすべて context window に流れ込みます

これを根本から畳むのが Programmatic Tool Calling (PTC) です。Claude が Python のスクリプトを書き、コード実行コンテナの中から「Python の関数」としてツールを呼び出します。最終結果だけが Claude の context に戻るため、レイテンシもトークンも 1 桁単位で削れます。本記事では、仕組み・使い所・制約を整理します。

到達点

  • PTC が解決する「ラウンドトリップ問題」と「中間データ膨張問題」を理解する
  • code_execution_20260120allowed_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 つだけです。

  1. toolscode_execution_20260120 を追加する
  2. 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_resulttool_use_idPTC 側 tool_use の id(toolu_...)を指すのもポイントです。

レスポンス形

通常の tool_use と PTC の tool_usecaller フィールドで見分けられます

// 通常
{
  "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(大量ツールから動的に絞り込む)を予定しています。

参考

記事で登場したコードをひとつの最小リポジトリとしてまとめました。ファイルツリーから切り替えて、全体の構造をそのまま確認できます。

$cat./package.jsonjson
{
  "name": "claude-ptc-starter",
  "private": true,
  "type": "module",
  "scripts": {
    "basic": "bun src/basic.ts",
    "loop": "bun src/full-loop.ts",
    "compare": "bun src/standard-vs-ptc.ts",
    "caller": "bun src/inspect-caller.ts"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.90.0"
  },
  "devDependencies": {
    "@types/bun": "^1.3.0",
    "typescript": "^6.0.3"
  }
}
slugclaude-programmatic-tool-callingfiles8click a file in the tree to switch