Programmatic Tool Calling(PTC)とは何か
Claude APIを使ったAIエージェント開発において、ツール呼び出しのレイテンシとトークンコストは常に大きな課題でしました。従来のTool Use(関数呼び出し)では、Claudeがツールを1つずつリクエストし、結果を受け取り、次のツールを呼び出す…というラウンドトリップが発生します。5つのツールを連携するワークフローでは、5回のモデル推論パスが必要でしました。
Programmatic Tool Calling(PTC) は、この問題を根本的に解決する新しいアプローチです。Claudeがコード実行環境内でPythonコードを書き、複数のツールをプログラム的に呼び出すことで、ラウンドトリップを最小限に抑えます。
# 従来のTool Use: 5回のラウンドトリップ
# Tool 1 → 結果 → Tool 2 → 結果 → Tool 3 → 結果 → ...
# PTC: Claudeがコードで一括処理
async def research_workflow(query):
# Claudeが書いたコード内でツールを直接呼び出し
search_results = await tool.web_search(query)
relevant_urls = [r["url"] for r in search_results[:5]]
# 並列でページ取得
pages = await asyncio.gather(*[
tool.fetch_page(url) for url in relevant_urls
])
# コード内でフィルタリング — コンテキストに入るのは要約のみ
summaries = [extract_key_points(page) for page in pages]
return summariesこの仕組みにより、マルチツールワークフローのレイテンシが最大10倍改善され、トークン消費も大幅に削減されます。
なぜPTCが必要なのか — 従来のTool Useの限界
コンテキストウィンドウの圧迫問題
従来のTool Useでは、各ツール呼び出しの結果がそのままClaudeのコンテキストウィンドウに入ります。大量のデータを取得するワークフローでは、中間結果がコンテキストを圧迫し、重要な情報が押し出されてしまうことがありましました。
# 従来の問題: 全データがコンテキストに入る
# データベースから1000行取得 → 全行がコンテキストに入る
# → 必要なのは集計結果だけなのにトークンを大量消費
# PTCの解決策: コード内でフィルタリング
results = await tool.query_database("SELECT * FROM orders WHERE date > '2026-01-01'")
# Claudeのコード内で集計 — コンテキストには集計結果のみ
summary = {
"total_orders": len(results),
"total_revenue": sum(r["amount"] for r in results),
"avg_order_value": sum(r["amount"] for r in results) / len(results),
"top_products": Counter(r["product"] for r in results).most_common(5)
}レイテンシの累積
各ツール呼び出しにはモデル推論のオーバーヘッドが加わります。5ステップのワークフローでは、ツール実行時間に加えて5回分の推論時間が累積します。PTCでは、Claudeが最初に一度コードを生成し、そのコード内でツールを連続実行するため、推論パスは1〜2回で済みます。
PTCの実装方法
基本的なAPI呼び出し
PTCを有効にするには、APIリクエストの tools 定義に加えて、betas ヘッダーに対応するフラグを指定します。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
// PTCを有効にしたツール定義
const tools = [
{
name: "web_search",
description: "Search the web for information",
input_schema: {
type: "object",
properties: {
query: { type: "string", description: "Search query" },
max_results: { type: "number", description: "Max results to return" }
},
required: ["query"]
},
// Input Examplesでツールの使い方を示す
input_examples: [
{
query: "Claude API latest features 2026",
max_results: 5
}
]
},
{
name: "fetch_page",
description: "Fetch and extract text content from a URL",
input_schema: {
type: "object",
properties: {
url: { type: "string", description: "URL to fetch" }
},
required: ["url"]
}
}
];
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 8192,
tools: tools,
tool_choice: { type: "auto" },
messages: [
{
role: "user",
content: "最新のClaude APIアップデートについて調査し、主要な変更点を3つにまとめてください"
}
]
});Tool Searchとの組み合わせ
数千のツールを登録している場合、全ツールをコンテキストに含めるのは非効率です。Tool Searchを使えば、Claudeが必要なツールだけを検索して使用できます。
// Tool Search を利用するツール定義
const toolSearchConfig = {
name: "tool_search",
description: "Search available tools by description or capability",
input_schema: {
type: "object",
properties: {
query: { type: "string", description: "What you need the tool to do" },
max_results: { type: "number", default: 5 }
},
required: ["query"]
}
};
// Claudeが動的にツールを発見して使用する
// 1. tool_search("データベースクエリ") → query_database ツールを発見
// 2. tool_search("メール送信") → send_email ツールを発見
// 3. PTCでこれらを組み合わせたワークフローを自動構築本番環境での設計パターン
パターン1: データ分析パイプライン
# PTC内でClaudeが生成するコード例
import json
# 複数データソースから並列取得
sales_data = await tool.query_database(
"SELECT product, SUM(amount) as total FROM sales GROUP BY product ORDER BY total DESC LIMIT 20"
)
market_data = await tool.fetch_api("/api/market-trends?period=30d")
competitor_data = await tool.web_search("competitor pricing analysis Q1 2026")
# コード内でデータを統合・加工
analysis = {
"top_products": sales_data[:5],
"market_trend": market_data["trend_direction"],
"growth_rate": market_data["growth_percentage"],
"competitor_insights": [
item["snippet"] for item in competitor_data[:3]
]
}
# フィルタ済みの結果のみがClaudeのコンテキストに返される
return json.dumps(analysis, ensure_ascii=False, indent=2)パターン2: マルチステップ検証ワークフロー
# コードレビューエージェントの例
import asyncio
# 変更されたファイル一覧を取得
changed_files = await tool.github_get_pr_files(pr_number=1234)
# 各ファイルに対して並列でチェック実行
async def check_file(file_path):
content = await tool.github_get_file_content(file_path)
lint_result = await tool.run_linter(file_path, content)
test_result = await tool.run_tests(file_path)
return {
"file": file_path,
"lint_issues": len(lint_result.get("errors", [])),
"test_passed": test_result["status"] == "pass",
"critical": any(e["severity"] == "critical" for e in lint_result.get("errors", []))
}
results = await asyncio.gather(*[check_file(f) for f in changed_files])
# 問題のあるファイルだけをコンテキストに渡す
issues = [r for r in results if r["lint_issues"] > 0 or not r["test_passed"]]
return json.dumps({"total_files": len(changed_files), "issues": issues})パターン3: エラーハンドリングとリトライ
# 堅牢なエラーハンドリング付きPTCパターン
import asyncio
MAX_RETRIES = 3
async def resilient_tool_call(tool_func, *args, **kwargs):
"""リトライ付きツール呼び出しラッパー"""
for attempt in range(MAX_RETRIES):
try:
result = await tool_func(*args, **kwargs)
return result
except Exception as e:
if attempt == MAX_RETRIES - 1:
return {"error": str(e), "tool": tool_func.__name__}
await asyncio.sleep(2 ** attempt) # 指数バックオフ
# 使用例
search_result = await resilient_tool_call(tool.web_search, "Claude API updates")
if "error" in search_result:
# フォールバック: キャッシュされたデータを使用
search_result = await tool.get_cached_data("claude_api_updates")Input Examplesによるツール呼び出し精度の向上
JSON Schemaだけでは、ツールの「使い方のパターン」を伝えきれません。input_examples を使えば、Claudeに具体的な呼び出しパターンを示せます。
const databaseTool = {
name: "query_database",
description: "Execute SQL query against the application database",
input_schema: {
type: "object",
properties: {
query: { type: "string" },
params: {
type: "array",
items: { type: "string" },
description: "Parameterized query values (prevents SQL injection)"
},
timeout_ms: { type: "number", default: 5000 }
},
required: ["query"]
},
// 具体的な使用例を提供
input_examples: [
{
// 基本的なクエリ
query: "SELECT name, email FROM users WHERE status = $1",
params: ["active"]
},
{
// 集計クエリ
query: "SELECT DATE(created_at) as date, COUNT(*) as count FROM orders WHERE created_at > $1 GROUP BY DATE(created_at) ORDER BY date DESC",
params: ["2026-01-01"],
timeout_ms: 10000
},
{
// JOINを使った複雑なクエリ
query: "SELECT u.name, COUNT(o.id) as order_count, SUM(o.amount) as total FROM users u JOIN orders o ON u.id = o.user_id WHERE o.created_at BETWEEN $1 AND $2 GROUP BY u.id, u.name HAVING COUNT(o.id) > $3",
params: ["2026-01-01", "2026-03-31", "5"],
timeout_ms: 15000
}
]
};これにより、Claudeは以下を学習します。
- パラメータ化クエリの使い方(SQLインジェクション防止)
- 集計クエリでの
timeout_msの適切な設定 - 複雑なJOINクエリの構造パターン
パフォーマンス比較: 従来のTool Use vs PTC
実際のベンチマークデータに基づく比較です。
// ベンチマーク: 10URLのWebリサーチタスク
// 従来のTool Use
// ラウンドトリップ: 10回(検索1回 + ページ取得9回)
// 推論パス: 11回
// 平均レイテンシ: 45秒
// トークン消費: ~120,000トークン
// PTC
// ラウンドトリップ: 2回(コード生成1回 + 結果返却1回)
// 推論パス: 2回
// 平均レイテンシ: 8秒(並列フェッチ)
// トークン消費: ~25,000トークン
// BrowseCompベンチマーク結果:
// 基本Tool Use: スコア 42%
// PTC有効化: スコア 71%(+69%改善)全体を振り返って — PTCで変わるAIエージェント開発
Programmatic Tool Calling(PTC)は、Claude APIを使ったAIエージェント開発のパラダイムを大きく変える機能です。
- レイテンシ削減: マルチツールワークフローの応答時間を最大10倍改善
- トークンコスト削減: 中間結果のフィルタリングにより70〜80%のコスト削減
- 並列実行:
asyncio.gatherパターンで複数ツールを同時実行 - データ加工: コンテキストに入る前にコード内でデータを整形・集約
Tool SearchやInput Examplesと組み合わせることで、数千のツールを効率的に活用し、複雑なビジネスロジックを自動化するプロダクション級AIエージェントを構築できます。
まずは既存のマルチステップワークフローをPTCに移行するところから始めてみてください。レイテンシとコストの改善を実感できるはずです。詳細は[Claude API Tool Useドキュメント]((/articles/api-sdk/tool-use-guide)やストリーミング実装ガイドもあわせてご参照ください。