プログラムによるツール呼び出しでは、モデルがツールの連携を制御する JavaScript を作成して実行できます。プログラムはツールの並列呼び出し、ループや条件分岐の使用、ホスト型ランタイムでの中間結果の保持が可能です。関連するツールを順に呼び出す必要があるタスクや、結果を返す前にツールの大量の出力を処理する必要があるタスクに役立ちます。
Responses API では、プログラムによるツール呼び出しを利用可能にするかどうかをアプリケーション側で決定します。また、対象ツールごとに、モデルが直接呼び出せるか、プログラムから呼び出せるか、その両方を許可するかを設定します。クライアント側のツール呼び出しは、引き続きアプリケーションが実行します。Agents API では、プログラムによるツール呼び出しがデフォルトで有効になっており、エージェントループも API 側で管理されます。
プログラムによるツール呼び出しを有効にする前に、モデルのページを確認してください。
ランタイム環境の概要
OpenAI は、生成されたプログラムごとに新しい隔離された V8 ランタイムを用意して実行します。このランタイムはトップレベルの await を含む JavaScript をサポートしますが、Node.js、パッケージのインストール、直接のネットワークアクセス、汎用ファイルシステム、サブプロセスの実行、コンソール、プログラムの実行間での JavaScript の状態保持は提供しません。プログラムが外部システムとやり取りできるのは、リクエストで有効にしたツールを通じてのみです。出力には text(...) または image(...) を使用できます。
Responses API のリクエストでは、プログラムによるツール呼び出しはゼロデータ保持(ZDR)のワークフローに対応しており、永続的なコード実行コンテナは必要ありません。ZDR は組織またはプロジェクトで有効にする必要があります。store: false を設定するとステートレスで処理を継続できますが、この設定だけでは ZDR は有効になりません。適用可否とデータ保持の扱いは、モデル、ツール、サードパーティサービスを含むリクエスト全体によって決まります。詳しくはデータ制御を参照してください。
プログラムによるツール呼び出しの使いどころ
処理段階の制御フローが予測可能で、コードによって結果を小さな構造化データにまとめて返せる場合は、プログラムによるツール呼び出しを使用します。1 回の呼び出しで十分な場合、結果ごとにモデルの新たな判断が必要な場合、または作業に承認や引用・ネイティブ形式の成果物の保持が必要な場合は、ツールを直接呼び出します。
| タスクの特性 | 推奨モード |
|---|---|
| 単一の情報取得またはアクション | ツールを直接呼び出します。 |
| コードでフィルタリング、結合、順位付け、重複排除、集計、検証ができる複数の結果 | プログラムが結果を小さな構造化データにまとめて返せる場合は、プログラムによるツール呼び出しを使用します。 |
| データフローが予測可能な、依存関係のある呼び出し | コードで後続の呼び出しの引数を導き出せ、制限と失敗時の動作が明確な場合は、プログラムによるツール呼び出しを使用します。 |
| 結果に応じた検索や意味に基づく評価 | 各結果をモデルの次の判断に反映する必要がある場合は、ツールを直接呼び出します。 |
| 書き込みや、承認の扱いに注意が必要なアクション | 認可の境界を明確に保つため、原則としてツールを直接呼び出します。 |
| 引用やネイティブ形式の成果物の最終検証 | プログラムがネイティブ形式の出力を保持し、必要な項目をすべて検証する場合を除き、ツールを直接呼び出します。 |
プログラムによるツール呼び出しの設定
Responses API では、ホスト型ツールの programmatic_tool_calling をリクエストに追加します。次に、プログラムから呼び出せる対象ツールごとに allowed_callers を設定します。
[
{
"type": "function",
"name": "get_inventory",
"description": "Return an object with sku (string) and available_units (number).",
"parameters": {
"type": "object",
"properties": {
"sku": { "type": "string" }
},
"required": ["sku"],
"additionalProperties": false
},
"output_schema": {
"type": "object",
"properties": {
"sku": { "type": "string" },
"available_units": { "type": "number" }
},
"required": ["sku", "available_units"],
"additionalProperties": false
},
"allowed_callers": ["programmatic"]
},
{
"type": "programmatic_tool_calling"
}
]allowed_callers は、モデルがツールを呼び出せる方法を制御します。
| 値 | 動作 |
|---|---|
省略または ["direct"] | モデルはツールを直接呼び出せます。 |
["programmatic"] | program アイテム内のコードからのみツールを呼び出せます。 |
["direct", "programmatic"] | モデルはツールを直接、またはプログラムから呼び出せます。 |
parameters は関数の引数を記述します。関数が予測可能な構造化データを返す場合、output_schema は、その function_call_output.output 文字列にエンコードされる JSON オブジェクトを記述します。生成された JavaScript が返されたフィールドを確実に利用できるよう、両方を定義してください。
対応ツール
次のツールタイプが allowed_callers: ["programmatic"] に対応しています。
functionとcustommcpapply_patch- ローカルおよびホスト型の
shell code_interpreter
MCP ツールでは、ツールの require_approval ポリシーによって、呼び出しを承認するまでプログラムが一時停止する場合があります。
OpenAI がホストするツールをプログラムで有効にする前に、そのツールのデータ保持とセキュリティに関するガイダンスを確認してください。
ツール検索との組み合わせ
ツール検索は、生成された JavaScript 内ではなく、Responses API のトップレベルのツールとして実行されます。defer_loading: true が設定された関数ツール、カスタムツール、MCP ツールは、最初はプログラムから利用できません。モデルが条件に合うツールを読み込むと、そのツールの allowed_callers に "programmatic" が含まれていれば、後続のプログラムから tools.* を通じて呼び出せます。実行中のプログラムはツール検索を呼び出せないため、モデルは遅延読み込み対象のツールを必要とするプログラムを開始する前に、それらのツールを読み込む必要があります。
両方のモードが利用可能な場合の呼び出し経路の指定
アプリケーションがモデルに関数の直接呼び出しとプログラムからの呼び出しの両方を許可する場合は、それぞれの経路をワークフローの具体的な段階に割り当てます。「プログラムによるツール呼び出しを効率的に使う」といった一般的な指示では、意図した使い分けの境界が明確になりません。たとえば、次のように指定します。
<tool_orchestration>
Use Programmatic Tool Calling for [bounded stage] using only [eligible tools].
Run independent calls concurrently when safe. Use only documented tool input
and output fields.
Process and reduce the intermediate results, then emit exactly [program result shape],
including the evidence needed for the final answer.
Stop when [condition] is met. Retry transient failures at most [R] times.
Do not repeat completed calls or perform side-effecting actions. If a required
result is still missing, return a clear structured failure.
Use direct tool calls for [semantic judgment, approval, or final validation].
</tool_orchestration>
このテンプレートの使用例を以下に示します。
<tool_orchestration>
Use Programmatic Tool Calling to compare inventory with demand for sku_123
using only get_inventory and get_demand. Run both calls concurrently. Use
only documented tool input and output fields.
Process and reduce the intermediate results, then emit exactly one JSON object
with sku, available_units, requested_units, and shortage_units, where
shortage_units is max(requested_units - available_units, 0). Include
available_units and requested_units as evidence for the calculation.
Stop when both tool results contain the required fields. Retry transient
failures at most 1 time. Do not repeat completed calls or perform
side-effecting actions. If a required result is still missing, return a clear
structured failure.
Use direct tool calls only for approval before any inventory-changing action.
</tool_orchestration>
両方のモードを必要とするワークフローでは、モード間の引き継ぎを 1 回と定め、経路の切り替えや同じ作業の繰り返しを避けます。安全なフォールバックがある場合は、それを 1 つ定義し、再試行の回数を制限します。
プログラムのレスポンスアイテムの概要
各 API 呼び出しは、引き続き標準の Responses API オブジェクトを返します。プログラムによるツール呼び出し専用のレスポンスエンベロープはありません。モデルがプログラムによるツール呼び出しを使用すると、レスポンスの output 配列に次のアイテムが含まれる場合があります。
- 生成された JavaScript、
call_id、およびプログラムの再開や再実行に使用する不透明な値fingerprintを含むprogramアイテム - プログラムが作成した
function_callアイテムです。固有のcall_idを持ち、アプリケーションはそれを使って関数の結果を返します。このアイテムのcaller.caller_idは、プログラムのcall_idと一致します。 - プログラムの最終結果とステータスを含む
program_outputアイテムです。このアイテムのcall_idはプログラムのcall_idと一致し、statusはcompletedまたはincompleteです。
これらは response.output 内の個別のトップレベルアイテムです。実行時の関係は caller フィールドに記録されます。
たとえば、アプリケーションが get_inventory と get_demand を実行している間、プログラムは一時停止できます。
[
{
"type": "program",
"id": "prog_123",
"call_id": "call_prog_123",
"code": "const [stock, demand] = await Promise.all([tools.get_inventory({ sku: 'sku_123' }), tools.get_demand({ sku: 'sku_123' })]); text(JSON.stringify({ sku: stock.sku, available_units: stock.available_units, requested_units: demand.requested_units, shortage_units: Math.max(demand.requested_units - stock.available_units, 0) }));",
"fingerprint": "opaque_replay_state"
},
{
"type": "function_call",
"id": "fc_123",
"call_id": "call_inventory_123",
"name": "get_inventory",
"arguments": "{\"sku\":\"sku_123\"}",
"caller": {
"type": "program",
"caller_id": "call_prog_123"
}
},
{
"type": "function_call",
"id": "fc_456",
"call_id": "call_demand_123",
"name": "get_demand",
"arguments": "{\"sku\":\"sku_123\"}",
"caller": {
"type": "program",
"caller_id": "call_prog_123"
}
}
]これらの例では、response.output 内の関連するアイテムのみを示し、それを囲む標準の Responses オブジェクトは省略しています。アプリケーションがネストされた関数の結果を返した後、後続のレスポンスに完了した program_output アイテムが含まれる場合があります。
{
"type": "program_output",
"id": "prog_out_123",
"call_id": "call_prog_123",
"result": "{\"sku\":\"sku_123\",\"available_units\":42,\"requested_units\":31,\"shortage_units\":0}",
"status": "completed"
}program_output.result 内の JSON 文字列は、指示で指定したプログラムの結果形式に従います。それを包む program_output 項目は、上記の API 仕様に従います。この 2 つは別々の仕様です。最終的な message はプログラムの出力と同時に届く場合も、後続のレスポンスで届く場合もあるため、そのメッセージを受信するまで処理を続けてください。
OpenAI は、モデルが生成した JavaScript をホスト型ランタイムで実行します。アプリケーションは、返されたクライアント側で管理する関数の呼び出しを実行します。生成された JavaScript を実行するわけではありません。
関数の結果を function_call_output として返してください。関数呼び出しの caller を変更せずにコピーします。サービスはその値を使って、対応するプログラムを再開します。
クライアント側で管理する関数の呼び出し後の処理継続
プログラムは、クライアント側で管理するツールの呼び出しに到達するたびに、複数回一時停止することがあります。レスポンスにアシスタントの最終メッセージが含まれるまで、次の処理を続けてください。
- ホスト型ツールと、プログラムからの呼び出しを許可した関数を含めてリクエストを送信します。
- 返されたクライアント側で管理する関数の呼び出しをすべて実行します。
- 元の
call_idとcallerを付けて、各関数の結果を返します。 - レスポンスが未完了の場合は、必要な処理を行ってから続行します。
- レスポンスに未処理の
function_call項目も最終的なmessage項目も含まれていない場合は、そのレスポンスから処理を続けます。store: falseの場合は出力項目を再送し、保存済みのレスポンスの場合はprevious_response_idを使用します。 - レスポンスに最終的な
message項目が含まれていたら、処理を終了します。response.output_textまたはメッセージ内の拒否内容を読み取ります。
次の例では、store: false を使用し、レスポンスの全項目を保持して、各関数の結果をプログラムに返します。
import json
from openai import OpenAI
client = OpenAI()
model = "gpt-6-astra"
def get_inventory(sku):
return {"sku": sku, "available_units": 42}
def get_demand(sku):
return {"sku": sku, "requested_units": 31}
implementations = {
"get_inventory": get_inventory,
"get_demand": get_demand,
}
tools = [
{
"type": "function",
"name": "get_inventory",
"description": "Return an object with sku (string) and available_units (number).",
"parameters": {
"type": "object",
"properties": {"sku": {"type": "string"}},
"required": ["sku"],
"additionalProperties": False,
},
"output_schema": {
"type": "object",
"properties": {
"sku": {"type": "string"},
"available_units": {"type": "number"},
},
"required": ["sku", "available_units"],
"additionalProperties": False,
},
"allowed_callers": ["programmatic"],
},
{
"type": "function",
"name": "get_demand",
"description": "Return an object with sku (string) and requested_units (number).",
"parameters": {
"type": "object",
"properties": {"sku": {"type": "string"}},
"required": ["sku"],
"additionalProperties": False,
},
"output_schema": {
"type": "object",
"properties": {
"sku": {"type": "string"},
"requested_units": {"type": "number"},
},
"required": ["sku", "requested_units"],
"additionalProperties": False,
},
"allowed_callers": ["programmatic"],
},
{"type": "programmatic_tool_calling"},
]
input_items = [
{
"role": "user",
"content": "Compare inventory with demand for sku_123.",
}
]
while True:
response = client.responses.create(
model=model,
store=False,
input=input_items,
tools=tools,
)
if response.status != "completed":
raise RuntimeError(f"Response ended with status {response.status}")
# Preserve every output item, including program and reasoning items.
input_items.extend(item.model_dump(exclude_none=True) for item in response.output)
calls = [item for item in response.output if item.type == "function_call"]
if not calls:
message = next(
(item for item in response.output if item.type == "message"), None
)
if message:
refusal = next(
(part.refusal for part in message.content if part.type == "refusal"),
"",
)
print(response.output_text or refusal)
break
continue
for call in calls:
run = implementations.get(call.name)
if run is None:
raise ValueError(f"Unknown tool: {call.name}")
result = run(**json.loads(call.arguments))
input_items.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result),
# Preserve caller so the runtime can resume the correct program.
"caller": call.caller.model_dump() if call.caller else None,
}
)レスポンスを保存する場合は、過去のレスポンス項目をすべて再送する代わりに、previous_response_id から処理を続けられます。新しい function_call_output 項目を次の入力として送信してください。store: false の場合は、すべての program 項目、推論項目、関数呼び出し項目、関数呼び出しの出力項目、program_output 項目を含む一連の項目を、元の順序で再送してください。
リーズニングモデルへのステートレスなリクエストでは、返された推論項目をすべて再送してください。各項目には、デフォルトで encrypted_content が含まれます。一般的なステートレス処理のパターンについては、会話の状態を参照してください。
プログラム向けのツール設計
- JavaScript が文章を解析せずに内容を確認できるよう、構造化されたコンパクトなデータを返してください。
output_schemaを使用して、各ツールが返す想定のフィールドと型を定義し、エラー時の動作を文書化してください。戻り値の形式が事前にわからない場合は、モデルが結果を確認できるよう、ツールは直接呼び出す形にしておきます。- プログラムの結果の厳密な形式と、必要な根拠を定義してください。プログラムが有効な結果を生成できない場合は、失敗を明確に示す構造化データを返してください。
- 可能な場合は、関数呼び出しを冪等にしてください。再試行や再送によって、安全でない副作用が繰り返し発生しないようにします。
- ホスト型プログラムからの呼び出しであっても、アプリケーション側で呼び出しごとに引数と権限を確認してください。
- モデルがツールを正しく組み合わせられるよう、具体的な名前と説明を付けてください。
- 影響の大きいアクションを実行する前には、呼び出し元にかかわらず、アプリケーション側での承認を必須にしてください。
プログラムによるツール呼び出しの評価
プログラムによるツール呼び出しは、モデルのコンテキストに追加されるツールの中間出力を削減できますが、その効果はタスクとツールのレスポンスによって異なります。まず直接のツール呼び出しを基準とし、代表的なタスクで両方の方法を比較してください。
効率を測定する前に、最終回答の品質基準と必要な根拠を定義してください。トークン使用量とツール呼び出しを、正確性、完全性、根拠の網羅性と併せて評価し、品質に関して許容するトレードオフがあれば明示してください。
次の項目を測定してください。
- 最終回答の正確性、完全性、根拠の網羅性
- 入力トークン数と総トークン数、エンドツーエンドのレイテンシ、コスト
- モデルのターン数、ツール呼び出し、再試行、復旧時の動作
- 安全性の評価結果、特に副作用と承認要件に関する結果
- 実際の呼び出し経路が、意図したワークフローの段階に合っていたか
Agents API
Agents API では、プログラムによるツール呼び出しは OpenAI が管理するエージェントハーネス内で実行され、デフォルトで有効になっています。ハーネスはエージェントに exec ツールを提供し、生成された JavaScript 内から既存のツールを利用できるようにします。これらのツールをコマンドラインプログラムとしてラップしたり、サンドボックスにインストールしたりする必要はありません。
プログラムによるツール呼び出しを無効にするには、agent.tools に次のエントリを追加します。
{
"type": "programmatic_tool_calling",
"enabled": false
}
このエントリまたはその enabled フィールドを省略すると、プログラムによるツール呼び出しは有効のままになります。型のみを指定したエントリ { "type": "programmatic_tool_calling" } でも、有効のままです。前述の allowed_callers の構成と Responses の継続処理ループは、Responses API との連携について説明したものです。
プログラムによるツール呼び出しは、environment.type を none に設定した会話専用セッションでも動作します。Bash、エグゼキューター MCP、その他のサンドボックス内で動作するツールには、引き続き実行環境が必要です。
JavaScript でツールの連携を制御しても、ツールの実行場所は変わりません。シェルの呼び出しでは、コマンドはサンドボックス内で実行されます。JavaScript ランタイム自体がシステムプロセスを起動することはありません。エグゼキューター MCP は引き続きサンドボックスを使用し、関数ツールは引き続きアプリケーションサーバーを呼び出します。エージェントはそれらの結果を処理してから、モデルのコンテキストに含める内容を決定します。
前述の呼び出し経路に関するガイダンスに従い、ワークフローのどの段階でコードを使用するかを定義してください。Agents API の構成と呼び出しの処理については、関数と MCP 接続を参照してください。
関連ガイド
- Function Calling を使用して、クライアント側で管理する関数を定義します。
- ツール検索を使用して、サイズの大きいツール定義の読み込みを、モデルが必要とする時点まで遅らせます。
- 会話の状態を使用して、保存済みまたはステートレスな Responses API リクエストの処理を続けます。
- 保存モードを選択する前に、データ管理を確認してください。