La llamada programática a herramientas permite que un modelo escriba y ejecute JavaScript para coordinar sus herramientas. Un programa puede llamar a herramientas en paralelo, usar bucles y condiciones, y conservar resultados intermedios en el entorno de ejecución alojado. Esto resulta útil cuando una tarea necesita una secuencia de llamadas a herramientas relacionadas o debe procesar salidas de herramientas de gran tamaño antes de devolver un resultado.
En la API Responses, tu aplicación decide si la llamada programática a herramientas está disponible y qué herramientas compatibles puede llamar el modelo directamente, desde un programa o de ambas formas. Tu aplicación sigue ejecutando todas las llamadas a herramientas a cargo del cliente. La API de agentes habilita la llamada programática a herramientas de forma predeterminada y administra el bucle del agente por ti.
Consulta la página de modelos antes de habilitar la llamada programática a herramientas.
Comprender el entorno de ejecución
OpenAI ejecuta cada programa generado en un entorno de ejecución V8 nuevo y aislado. El entorno admite JavaScript con await en el nivel superior, pero no proporciona Node.js, instalación de paquetes, acceso directo a la red, un sistema de archivos de uso general, ejecución de subprocesos, una consola ni un estado de JavaScript persistente entre ejecuciones del programa. Los programas solo pueden interactuar con sistemas externos a través de las herramientas habilitadas en la solicitud y pueden emitir salidas con text(...) o image(...).
Para las solicitudes a la API Responses, la llamada programática a herramientas admite flujos de trabajo con retención cero de datos (ZDR) sin requerir un contenedor persistente para ejecutar código. La ZDR debe estar habilitada para la organización o el proyecto; establecer store: false permite la continuación sin estado, pero no habilita la ZDR por sí solo. La elegibilidad y la retención dependen de la solicitud completa, incluidos el modelo, las herramientas y los servicios de terceros que utiliza; consulta los controles de datos.
Elegir cuándo usar la llamada programática a herramientas
Usa la llamada programática a herramientas cuando una etapa tenga un flujo de control predecible y el código pueda devolver un resultado estructurado más pequeño. Usa llamadas directas a herramientas cuando una sola llamada sea suficiente, cada resultado requiera una nueva evaluación del modelo o el trabajo requiera aprobación o la conservación de citas o artefactos nativos.
| Tipo de tarea | Modo recomendado |
|---|---|
| Una sola consulta o acción | Usa llamadas directas a herramientas. |
| Varios resultados que el código puede filtrar, combinar, ordenar, deduplicar, agregar o validar | Usa la llamada programática a herramientas cuando el programa pueda devolver un resultado estructurado más pequeño. |
| Llamadas dependientes con un flujo de datos predecible | Usa la llamada programática a herramientas cuando el código pueda derivar los argumentos de llamadas posteriores y los límites y el comportamiento ante fallas estén definidos explícitamente. |
| Búsqueda adaptativa o evaluación semántica | Usa llamadas directas a herramientas cuando cada resultado deba influir en la siguiente decisión del modelo. |
| Operaciones de escritura o acciones que requieren atención a las aprobaciones | Usa llamadas directas a herramientas de forma predeterminada para mantener un límite de autorización claro. |
| Validación final de citas o artefactos nativos | Usa llamadas directas a herramientas, a menos que el programa conserve la salida nativa y valide cada elemento requerido. |
Configurar la llamada programática a herramientas
Para la API Responses, agrega la herramienta alojada programmatic_tool_calling a la solicitud. Luego, configura allowed_callers en cada herramienta compatible que el programa pueda invocar.
[
{
"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 controla cómo puede invocar el modelo una herramienta:
| Valor | Comportamiento |
|---|---|
Omitido o ["direct"] | El modelo puede llamar a la herramienta directamente. |
["programmatic"] | Solo el código de un elemento program puede llamar a la herramienta. |
["direct", "programmatic"] | El modelo puede llamar a la herramienta directamente o desde un programa. |
parameters describe los argumentos de la función. Cuando una función devuelve datos estructurados predecibles, output_schema describe el objeto JSON codificado en su cadena function_call_output.output. Define ambos para que el código JavaScript generado pueda usar los campos devueltos de forma confiable.
Herramientas compatibles
Los siguientes tipos de herramientas admiten allowed_callers: ["programmatic"]:
functionycustommcpapply_patchshelllocal y alojadocode_interpreter
En el caso de las herramientas MCP, la política require_approval de la herramienta puede pausar el programa hasta que apruebes la llamada.
En el caso de las herramientas alojadas por OpenAI, revisa las indicaciones sobre retención de datos y seguridad de la herramienta antes de habilitarla en un programa.
Combinar con la búsqueda de herramientas
La búsqueda de herramientas se ejecuta como una herramienta de nivel superior de la API Responses, no desde el código JavaScript generado. Las herramientas de función, personalizadas y MCP con defer_loading: true no están disponibles inicialmente para un programa. Después de que el modelo carga una herramienta que coincide con la búsqueda, un programa posterior puede invocarla mediante tools.* si su allowed_callers incluye "programmatic". Un programa que ya está en ejecución no puede invocar la búsqueda de herramientas, por lo que el modelo debe cargar las herramientas de carga diferida antes de iniciar un programa que las necesite.
Orientar la elección de la vía de llamada cuando ambos modos estén disponibles
Cuando tu aplicación permita que el modelo llame a una función directamente o desde un programa, asigna cada vía a una etapa específica del flujo de trabajo. Las instrucciones genéricas, como “usa la llamada programática a herramientas de manera eficiente”, no especifican el límite previsto. Por ejemplo:
<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>
Este es un ejemplo de cómo usar esta plantilla:
<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>
Para los flujos de trabajo que necesiten ambos modos, define un único punto de transición y evita alternar entre vías o repetir el trabajo. Si existe una alternativa segura, defínela una sola vez y limita sus reintentos.
Comprender los elementos de respuesta de los programas
Cada llamada a la API sigue devolviendo el objeto estándar de la API Responses. La llamada programática a herramientas no introduce una estructura contenedora de respuesta independiente. Cuando el modelo usa la llamada programática a herramientas, el arreglo output de la respuesta puede contener:
- Un elemento
programque contiene el código JavaScript generado, uncall_idy unfingerprintopaco que se usa para reanudar o volver a ejecutar el programa. - Un elemento
function_callgenerado por el programa. Tiene su propiocall_id, que tu aplicación usa para devolver el resultado de la función. Sucaller.caller_idcoincide con elcall_iddel programa. - Un elemento
program_outputque contiene el resultado final y el estado del programa. Sucall_idcoincide con elcall_iddel programa, y sustatusescompletedoincomplete.
Estos son elementos independientes de nivel superior en response.output; el campo caller registra la relación de ejecución entre ellos.
Por ejemplo, un programa puede pausarse mientras tu aplicación ejecuta get_inventory y 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"
}
}
]Estos ejemplos muestran solo los elementos relevantes de response.output; omiten el objeto estándar de Responses que los contiene. Después de que tu aplicación devuelve los resultados de las funciones anidadas, una respuesta posterior puede contener el elemento program_output completo:
{
"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"
}La cadena JSON de program_output.result sigue la estructura del resultado del programa definida en tus instrucciones. El elemento program_output que la contiene sigue el contrato de la API mostrado arriba. Son contratos distintos. Un message final puede llegar junto con la salida del programa o en una respuesta posterior, así que continúa hasta recibir ese mensaje.
OpenAI ejecuta el código JavaScript generado por el modelo en el entorno de ejecución alojado. Tu aplicación ejecuta las llamadas a funciones a cargo del cliente que recibe; no ejecuta el código JavaScript generado.
Devuelve el resultado de la función como un function_call_output. Copia caller de la llamada a la función sin modificarlo. El servicio usa ese valor para reanudar el programa correcto.
Continuar después de las llamadas a funciones a cargo del cliente
Un programa puede pausarse más de una vez al llegar a herramientas a cargo del cliente. Continúa hasta que la respuesta contenga un mensaje final del asistente:
- Envía la solicitud con la herramienta alojada y las funciones que permitan llamadas programáticas.
- Ejecuta todas las llamadas a funciones a cargo del cliente que recibas.
- Devuelve el resultado de cada función con los valores originales de
call_idycaller. - Maneja las respuestas incompletas antes de continuar.
- Si la respuesta no contiene elementos
function_callpendientes ni un elementomessagefinal, continúa a partir de esa respuesta. Constore: false, vuelve a enviar sus elementos de salida; para una respuesta almacenada, usaprevious_response_id. - Detente cuando la respuesta contenga un elemento
messagefinal. Leeresponse.output_texto el contenido de rechazo del mensaje.
El siguiente ejemplo usa store: false, conserva todos los elementos de respuesta y devuelve el resultado de cada función al programa:
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,
}
)Cuando almacenas las respuestas, puedes continuar a partir de previous_response_id en lugar de volver a enviar todos los elementos de las respuestas anteriores. Envía los nuevos elementos function_call_output como la siguiente entrada. Con store: false, vuelve a enviar la secuencia completa en orden, incluidos todos los elementos de program, razonamiento, llamadas a funciones, salidas de llamadas a funciones y program_output.
Para las solicitudes sin estado a modelos de razonamiento, vuelve a enviar todos los elementos de razonamiento recibidos. Cada elemento incluye encrypted_content de forma predeterminada. Consulta estado de la conversación para conocer el patrón general sin estado.
Diseñar herramientas para programas
- Devuelve datos estructurados y compactos que JavaScript pueda inspeccionar sin analizar texto en prosa.
- Usa
output_schemapara definir los campos y tipos que se espera que devuelva cada herramienta, y documenta su comportamiento ante errores. Si no se conoce de antemano la estructura del resultado, mantén la llamada directa a la herramienta para que el modelo pueda inspeccionarlo. - Define la estructura exacta del resultado del programa y la evidencia requerida. Devuelve un fallo claro y estructurado cuando el programa no pueda producir un resultado válido.
- Haz que las llamadas a funciones sean idempotentes cuando sea posible. Un reintento o un reenvío no debería repetir un efecto secundario inseguro.
- Verifica los argumentos y permisos de cada llamada en tu aplicación, incluso cuando provenga de un programa alojado.
- Asigna nombres y descripciones específicos a las herramientas para que el modelo pueda combinarlas correctamente.
- Exige aprobación a nivel de la aplicación antes de realizar acciones de alto impacto, independientemente de quién haga la llamada.
Evaluar la llamada programática a herramientas
La llamada programática a herramientas puede reducir la cantidad de resultados intermedios de herramientas que se agregan al contexto del modelo, pero el efecto depende de la tarea y de las respuestas de las herramientas. Empieza con llamadas directas a herramientas como referencia y luego compara ambos enfoques en tareas representativas.
Define el nivel de calidad exigido para la respuesta final y la evidencia requerida antes de medir la eficiencia. Evalúa el uso de tokens y las llamadas a herramientas junto con la exactitud, la exhaustividad y la cobertura de la evidencia, y deja explícita cualquier concesión aceptada en cuanto a la calidad.
Mide:
- La exactitud, la exhaustividad y la cobertura de la evidencia de la respuesta final.
- Los tokens de entrada y totales, la latencia de extremo a extremo y el costo.
- Los turnos del modelo, las llamadas a herramientas, los reintentos y el comportamiento de recuperación.
- Los resultados de seguridad, especialmente en cuanto a efectos secundarios y requisitos de aprobación.
- Si la vía que se ejecutó correspondía a la etapa prevista del flujo de trabajo.
API de agentes
En la API de agentes, la llamada programática a herramientas se ejecuta en el arnés de ejecución del agente administrado por OpenAI y está habilitada de forma predeterminada. El arnés de ejecución proporciona al agente una herramienta exec y permite usar sus herramientas existentes dentro del JavaScript generado. No necesitas encapsular esas herramientas como programas de línea de comandos ni instalarlas en el sandbox.
Para deshabilitar la llamada programática a herramientas, incluye esta entrada en agent.tools:
{
"type": "programmatic_tool_calling",
"enabled": false
}
Si omites la entrada o su campo enabled, la llamada programática a herramientas permanece habilitada. Una entrada que solo especifica el tipo, { "type": "programmatic_tool_calling" }, también la mantiene habilitada. La configuración de allowed_callers y el bucle de continuación de Responses descritos anteriormente corresponden a la integración con la API Responses.
La llamada programática a herramientas también funciona en sesiones de solo conversación con environment.type establecido en none. Bash, los MCP ejecutores y otras herramientas que se ejecutan en un sandbox siguen requiriendo un entorno de ejecución.
Orquestar una herramienta en JavaScript no cambia dónde se ejecuta. Una llamada al shell ejecuta comandos en el sandbox; el entorno de ejecución de JavaScript no inicia procesos del sistema por sí mismo. Los MCP ejecutores siguen usando el sandbox y las herramientas de funciones siguen llamando al servidor de tu aplicación. El agente procesa sus resultados antes de decidir qué se incorpora al contexto del modelo.
Usa las recomendaciones anteriores sobre enrutamiento para definir qué etapas del flujo de trabajo deben usar código. Sigue las guías de Funciones y Conexiones MCP para configurar la API de agentes y gestionar las llamadas.
Guías relacionadas
- Usa la llamada a funciones para definir funciones a cargo del cliente.
- Usa la búsqueda de herramientas para aplazar la inclusión de definiciones extensas de herramientas hasta que un modelo las necesite.
- Usa el estado de la conversación para continuar solicitudes almacenadas o sin estado de la API Responses.
- Revisa los controles de datos antes de elegir un modo de almacenamiento.