TL;DR
- OpenAI Responses API はステートフルな会話管理を組み込み、従来の Chat Completions より少ないコードでツール呼び出しを実装できる
- Anthropic Tool Use と OpenAI Function Calling は設計思想が異なる──前者は「モデルが判断して宣言」後者は「呼び出すかモデルが選ぶ」と整理すると比較しやすい
structured outputは型安全にデータを取得する手段であり、ツール呼び出しとは目的が違う──混同すると設計がぶれる- 選択基準は「状態管理の複雑さ」「型安全の必要度」「ベンダー依存の許容度」の3軸で判断する
はじめに
こんにちは、みねです。
2024年末から2025年にかけて、LLM APIの設計が大きく変わりました。OpenAI は Chat Completions の上位互換として Responses API を公開し、Anthropic も Tool Use の仕様を洗練させてきました。「どれを使えばいいのか」「function calling と tool use は何が違うのか」という疑問をよく聞きます。
この記事では、各APIの設計思想を整理し、TypeScript の実装例を交えながら「いつどれを使うか」の判断軸を実務視点で解説します。MCPとツール連携の全体設計を読んだあとにこの記事を読むと、より体系的に理解できます。
Responses API とは何か
Chat Completions との違い
OpenAI の Responses API は、従来の Chat Completions API と比べて以下の点が大きく変わっています。
| 項目 | Chat Completions | Responses API |
|---|---|---|
| 状態管理 | クライアント側で履歴を保持 | サーバー側でスレッドを管理 |
| ツール定義 | functions または tools 配列 | tools 配列(統一) |
| ストリーミング | Server-Sent Events | 同上(改善済み) |
| ファイル添付 | 限定的 | File Search / Code Interpreter 統合 |
| コスト最適化 | なし | サーバー側キャッシュ対応 |
| Structured Output | response_format パラメータ | text.format に統一[公式値] |
Chat Completions では、会話の文脈をクライアント側で messages 配列として保持し続ける必要がありました。Responses API では thread_id を使ってサーバーが状態を管理するため、クライアントのコードが大幅に簡略化されます。詳細な移行手順は OpenAI 公式 Migrate to the Responses API を参照してください[公式値]。
なぜ今 Responses API か
エージェント設計において、状態管理はもっとも複雑な部分のひとつです。agent loop を安定化する 際に checkpoint が必要になるのも、状態がメモリにしか存在しないからでした。Responses API はこの問題をプラットフォーム側で解決しようとするアプローチです。
Tool Use / Function Calling / Structured Output の比較
3つの概念の整理
混同しやすい3つの概念を先に整理します。
Function Calling(OpenAI 旧仕様)
モデルが外部関数を呼び出す際に、呼び出す関数名と引数を JSON で返す仕組み。現在は tools パラメータに統合されています。
Tool Use(Anthropic) Claude がツールの使用を「宣言」する仕組み。モデルはツール呼び出しの意図を返し、実際の実行はアプリケーション側が行います。
Structured Output(OpenAI) ツール呼び出しとは独立して、モデルの出力を特定の JSON Schema に強制する機能。データ抽出やフォーム入力の解析に使います。
設計思想の比較
| 観点 | OpenAI Tools | Anthropic Tool Use |
|---|---|---|
| 呼び出し判断 | モデルが tool_choice に従う | モデルが自律的に判断 |
| 並列実行 | parallel_tool_calls: true で対応 | 複数ツールを同一ターンで返せる |
| 型定義 | JSON Schema | JSON Schema(同様) |
| 厳密スキーマ強制 | strict: true(Responses API は既定で normalize)[公式値] | strict: true を明示指定[公式値] |
| エラーハンドリング | tool_result で返却 | tool_result で返却(同様) |
| ストリーミング | デルタ形式で段階的に返す | デルタ形式(同様) |
| マルチモーダル | 画像・ファイルをツール入力に使える | 同様 |
| サーバー側ツール | Code Interpreter / File Search | web_search / code_execution / web_fetch / tool_search[公式値] |
どちらも「モデルがツール呼び出しを判断・宣言し、アプリが実行する」という基本設計は共通です。差異は主に状態管理・厳密スキーマ既定値・サーバー側ツールのラインナップにあります。Anthropic 側の最新拡張(Tool Search Tool / Programmatic Tool Calling / Tool Use Examples)は Anthropic Engineering 公式記事 にまとまっています[公式値]。
Structured Output との使い分け
ツール呼び出し → 副作用がある処理(API呼び出し・DB書き込み・ファイル操作)
Structured Output → 副作用のないデータ抽出・変換
例:ユーザーの問い合わせを分類する場合、データベースに書き込まない「分類結果の返却」だけなら Structured Output が適切です。一方、分類後にチケットを作成する処理が続くなら、チケット作成をツールとして定義します。
実装例(TypeScript)
OpenAI Responses API でのツール呼び出し
import OpenAI from "openai";
const client = new OpenAI();
// ツール定義
const tools: OpenAI.Responses.Tool[] = [
{
type: "function",
name: "get_issue_status",
description: "GitHubイシューのステータスを取得する",
parameters: {
type: "object",
properties: {
repo: { type: "string", description: "リポジトリ名(owner/repo形式)" },
issue_number: { type: "number", description: "イシュー番号" },
},
required: ["repo", "issue_number"],
},
},
];
// ツール実行関数
async function get_issue_status(repo: string, issue_number: number) {
// 実際はGitHub APIを呼ぶ
return { status: "open", title: "Responses API migration", assignee: "komine" };
}
async function runAgentLoop(userMessage: string) {
// Responses API:スレッド管理不要、previous_response_idで継続
let response = await client.responses.create({
model: "gpt-4o",
input: userMessage,
tools,
});
// ツール呼び出しが終わるまでループ
while (response.status === "requires_action") {
const toolResults: OpenAI.Responses.ToolResult[] = [];
for (const call of response.tool_calls ?? []) {
if (call.type === "function" && call.name === "get_issue_status") {
const args = JSON.parse(call.arguments);
const result = await get_issue_status(args.repo, args.issue_number);
toolResults.push({
type: "tool_result",
tool_call_id: call.id,
content: JSON.stringify(result),
});
}
}
// previous_response_id で状態継続(Chat Completions では messages 配列を手動管理)
response = await client.responses.create({
model: "gpt-4o",
previous_response_id: response.id,
tools,
tool_choice: "auto",
input: toolResults,
});
}
return response.output_text;
}
Anthropic Tool Use での実装
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "get_issue_status",
description: "GitHubイシューのステータスを取得する",
input_schema: {
type: "object" as const,
properties: {
repo: { type: "string", description: "リポジトリ名(owner/repo形式)" },
issue_number: { type: "number", description: "イシュー番号" },
},
required: ["repo", "issue_number"],
},
},
];
async function runClaudeAgentLoop(userMessage: string) {
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: userMessage },
];
while (true) {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 4096,
tools,
messages,
});
// アシスタントの応答を履歴に追加
messages.push({ role: "assistant", content: response.content });
if (response.stop_reason !== "tool_use") break;
// ツール呼び出しを実行して結果を返す
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === "tool_use") {
const args = block.input as { repo: string; issue_number: number };
const result = await get_issue_status(args.repo, args.issue_number);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(result),
});
}
}
messages.push({ role: "user", content: toolResults });
}
const lastMessage = messages[messages.length - 1];
return lastMessage;
}
Structured Output での型安全なデータ抽出
import OpenAI from "openai";
import { z } from "zod";
import { zodResponseFormat } from "openai/helpers/zod";
const client = new OpenAI();
// Zodでスキーマ定義
const IssueClassification = z.object({
category: z.enum(["bug", "feature", "docs", "chore"]),
priority: z.enum(["high", "medium", "low"]),
estimated_effort: z.enum(["xs", "s", "m", "l", "xl"]),
summary: z.string().max(100),
});
async function classifyIssue(issueBody: string) {
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "GitHubイシューを分類してください。" },
{ role: "user", content: issueBody },
],
response_format: zodResponseFormat(IssueClassification, "classification"),
});
// TypeScriptの型安全が保証される
const parsed = IssueClassification.parse(
JSON.parse(response.choices[0].message.content ?? "{}")
);
return parsed;
}
設計判断の基準
いつどれを使うか
以下の3軸で判断します。
軸1: 状態管理の複雑さ
- 短い1ターン〜数ターン → Chat Completions + Tools で十分
- 長い会話・複数ステップの agent loop → Responses API のスレッド管理を活用
- ベンダー非依存が必要 → 状態管理を自前で実装し、Anthropic Tool Use を選ぶ
軸2: 型安全の必要度
- 返り値の型を厳密に保証したい → Structured Output(Zod 統合)
- 外部API呼び出しなど副作用がある → Tools / Tool Use
- 両方必要 → Tools + 引数を Zod でバリデーション
軸3: ベンダー依存の許容度
- OpenAI のエコシステム(Assistants・File Search・Code Interpreter)を使いたい → Responses API
- マルチプロバイダー対応・Claude の強みを活かしたい → Anthropic Tool Use
- 中立的なアブストラクション → Vercel AI SDK の
generateText+toolヘルパー
判断フローチャート
副作用がある処理か?
├── Yes → Tools / Tool Use を使う
│ ├── OpenAI に依存していい → Responses API
│ ├── Anthropic を使いたい → Tool Use
│ └── マルチプロバイダー → Vercel AI SDK
└── No → Structured Output を使う
└── Zod スキーマで型を保証
MCP権限設計との関係
ツール呼び出しが増えてくると、どのツールにどの権限を与えるかが問題になります。特に複数のエージェントが同じツールセットを共有する場合、権限の粒度設計が重要です。ツールを定義する段階で「このツールは読み取りのみ」「このツールは書き込みを伴う」と明示的に分類しておくと、後からの権限設計が楽になります。
実装後の運用観測
ツール呼び出しを本番投入したあと、最低限以下の観測を整えると事故が減ります(経験則)。
- ツール別 success / fail rate — 個別ツールの失敗率が SLO を割るかをモニタする
- ツール別 p95 レイテンシ — 重い API(DB 書き込み・外部 API)が agent loop 全体の TTFB を引っ張っていないか
- 連続失敗時の retry / fallback 動作 — exponential backoff + jitter が効いているかをログで確認する
- 入力スキーマ違反率 —
strict: true[公式値] でも稀に発生する schema 不一致を捕捉する - コスト按分 — ツールあたりのトークン消費・サーバー側ツール(Code Interpreter / web_search)課金を agent ID 単位で集約する
実装層と観測層の責任分界点は AIエージェントの観測基盤 で扱っています。durable workflow と組み合わせる場合は agent loop を durable workflow で安定化する実装、認可境界は MCP権限設計の判断軸 や MCPサーバー設計パターン集 と並べて読むと立体的に設計できます。
FAQ
Q1. Function Calling は deprecated になりましたか?
OpenAI の公式ドキュメントでは functions パラメータは deprecated 扱いになっており、tools パラメータへの移行が推奨されています。Responses API では tools に統一されているため、新規実装では tools を使ってください。既存コードの functions は短期的には動作しますが、将来の互換性のために移行を計画することをお勧めします。
Q2. Anthropic と OpenAI のツール定義は互換性がありますか?
JSON Schema ベースの定義は似ていますが、完全な互換性はありません。input_schema vs parameters のキー名の違い、tool_result の返し方の差異があります。Vercel AI SDK や LangChain などの抽象レイヤーを使うと差異を吸収できますが、プリミティブ API を直接使う場合はプロバイダーごとに実装を分ける方が安全です。
Q3. Structured Output はすべてのモデルで使えますか?
OpenAI の Structured Output は gpt-4o-2024-08-06 以降でサポートされています[公式値]。古いモデルや fine-tuned モデルでは使えない場合があります。Anthropic には同等の機能として prefill による出力制御がありますが、JSON Schema への強制は Tool Use を経由するアプローチが一般的です。Responses API での詳細は OpenAI 公式 Structured Outputs ガイド を確認してください。
Q4. Anthropic 側の Tool Search Tool / Programmatic Tool Calling は何が違いますか?
Tool Search Tool は数千個のツール定義をコンテキストに展開せず必要時だけ検索して使う仕組みで、Programmatic Tool Calling は code execution 環境内でツールを連鎖呼び出しする仕組みです[公式値]。コンテキスト消費を抑えながら大量のツールを扱いたい場合は Tool Search Tool、複数ツールの連携を 1 ターンで完結させたい場合は Programmatic Tool Calling、というのが Anthropic 公式の使い分けです(Anthropic Engineering 公式)。
Q5. 実装後の運用観測で最低限取るべき指標は何ですか?
ツール別 success/fail rate、p95 レイテンシ、連続失敗時の retry/fallback 動作、入力スキーマ違反率、コスト按分(ツール別トークン・サーバー側ツール課金)の 5 つを最低限取得します。詳細は本記事の「実装後の運用観測」セクションと AIエージェントの観測基盤 を参照してください。
References
- OpenAI Responses API Reference — Responses API のリクエスト・レスポンス仕様一次情報
- OpenAI Structured Outputs Guide —
text.formatを使った Structured Output の最新仕様 - OpenAI Function Calling Guide —
toolsパラメータとstrictモードの公式仕様 - OpenAI Migrate to the Responses API — Chat Completions からの移行手順
- Anthropic Tool Use Overview — Claude のツール呼び出し公式ドキュメント
- Anthropic Engineering: Advanced Tool Use — Tool Search Tool / Programmatic Tool Calling / Tool Use Examples の公式解説
関連記事
- MCPサーバー設計パターン集 — ツール呼び出しの先で MCP サーバーを実装する場合の 5 軸
- agent loop を durable workflow で安定化する実装 — 状態管理・retry・checkpoint との接続
- 自律エージェントループの構成要素 — Responses API スレッド管理の上位設計
- AIエージェントの観測基盤 — 実装後のメトリクス・トレース設計
- MCP権限設計の判断軸 — ツール権限の allowlist 粒度
まとめ
Responses API 時代のツール呼び出し設計は、以下の3点を押さえれば迷いが減ります。
- 概念を分離する: ツール呼び出し(副作用あり)と Structured Output(型安全な抽出)は目的が違う
- 状態管理で選ぶ: 長い agent loop なら Responses API のスレッド管理が有効、非依存ならクライアント管理で Anthropic Tool Use
- 型を最初から設計する: ツール引数・返り値を Zod で定義する習慣をつけると、LLM の幻覚をアプリ層で防ぎやすくなる
ツール設計の次のステップとして、MCPを使ったツール連携の拡張や、agent loop の安定化に取り組むことをお勧めします。設計判断に迷ったときはこの記事の比較表と判断フローチャートを参照してください。
