Function Calling: Responses API frente a Chat Completions

Comparación de definiciones, Call IDs, resultados, argumentos en streaming, autorización, idempotencia y compatibilidad de rutas.

Responses y Chat Completions pueden pedir a una aplicación que ejecute una función, pero representan el tool loop de forma distinta. Chat Completions define funciones bajo tools[].function, devuelve message.tool_calls y recibe resultados como mensajes role: "tool". Responses usa definiciones planas, output items function_call y resultados function_call_output asociados mediante call_id.

El modelo no ejecuta la función. El código de la aplicación debe validar argumentos, autorizar la operación, ejecutarla, devolver el resultado y evitar efectos secundarios duplicados cuando una solicitud se repite.

El tool loop tiene cuatro pasos

1. Declarar una función permitida y su Schema de argumentos
2. Recibir una o más llamadas propuestas por el modelo
3. Validar, autorizar y ejecutar cada llamada en la aplicación
4. Devolver cada resultado con su correlation ID

Solo después del cuarto paso el modelo puede producir una respuesta basada en el resultado. Si solicita otra herramienta, el ciclo continúa con un nuevo Call ID. La guía de Function Calling de OpenAI lo define como intercambio de varios pasos; tratar el primer Tool Call como respuesta final es un error común.

Comparar los wire contracts

Aspecto Responses API Chat Completions
Definición Elemento plano en tools[] Campos dentro de tools[].function
Llamada propuesta Output item function_call tool_calls[] del mensaje assistant
Correlación call_id id, devuelto como tool_call_id
Nombre function_call.name tool_calls[].function.name
Argumentos String en function_call.arguments String en tool_calls[].function.arguments
Resultado Input item function_call_output Mensaje role: "tool"
Streaming Eventos tipados de argumentos Fragmentos delta.tool_calls[]
Texto final Output items / helper choices[0].message.content

No correlaciones llamadas por posición en el array. Las llamadas paralelas y los chunks pueden completarse en otro orden. El ID explícito es la clave estable.

Definir una función con Schema estricto

Esta función de solo lectura consulta un envío:

{
  "type": "object",
  "properties": {
    "order_id": { "type": "string", "pattern": "^ORDER-[0-9]{4}$" }
  },
  "required": ["order_id"],
  "additionalProperties": false
}

strict: true pide al modelo compatible que siga el Schema, pero la aplicación debe parsear y validar. El soporte de pattern y otros keywords varía, por lo que también se aplica el gate de compatibilidad de Structured Outputs.

Implementar el loop con Responses API

const tools = [{
  type: "function",
  name: "get_delivery_status",
  description: "Return the current delivery status for one order.",
  parameters: {
    type: "object",
    properties: { order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" } },
    required: ["order_id"],
    additionalProperties: false,
  },
  strict: true,
}];

const first = await client.responses.create({
  model, input: "Where is ORDER-1001?", tools, parallel_tool_calls: false,
});
const calls = first.output.filter(item => item.type === "function_call");
const outputs = calls.map(call => ({
  type: "function_call_output",
  call_id: call.call_id,
  output: JSON.stringify(executeTool(call.name, call.arguments)),
}));
const final = await client.responses.create({
  model, input: [...first.output, ...outputs], tools, parallel_tool_calls: false,
});

El call.call_id del output debe coincidir con el call_id del resultado. En un intercambio stateless se devuelven los output items anteriores junto a los resultados. No asumas que cada ruta OpenAI-compatible almacena el estado necesario para una continuación stateful.

Implementar el loop con Chat Completions

const tools = [{
  type: "function",
  function: {
    name: "get_delivery_status",
    description: "Return the current delivery status for one order.",
    parameters: {
      type: "object",
      properties: { order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" } },
      required: ["order_id"],
      additionalProperties: false,
    },
    strict: true,
  },
}];

const first = await client.chat.completions.create({ model, messages, tools });
const assistant = first.choices[0].message;
messages.push(assistant);
for (const call of assistant.tool_calls ?? []) {
  messages.push({
    role: "tool",
    tool_call_id: call.id,
    content: JSON.stringify(executeTool(call.function.name, call.function.arguments)),
  });
}

El mensaje assistant que contiene las llamadas debe añadirse antes de los resultados. Omitirlo o usar un tool_call_id sin pareja crea un transcript inválido.

Tratar argumentos en streaming como fragmentos

Un chunk {"order no es JSON inválido, sino incompleto. Mantén un buffer por call identity, añade cada delta, espera la señal Arguments Done o Completed, parsea una sola vez y ejecuta después de validar. Responses usa eventos tipados; Chat Completions usa choices[].delta.tool_calls[], donde el índice identifica la entrada en curso. Nunca ejecutes al recibir el primer fragmento. Consulta la guía de streaming de AI API.

Hacer la ejecución segura e idempotente

  • aceptar solo nombres registrados;
  • limitar el tamaño y validar el Schema completo;
  • autorizar al usuario o workload para el recurso;
  • separar herramientas de lectura y efectos secundarios;
  • eliminar secretos del resultado enviado al modelo;
  • usar deadline y retries downstream limitados;
  • guardar una referencia segura de la operación.

Para efectos secundarios, deriva una idempotency key de la solicitud estable y del Call ID. Persiste el resultado antes de devolverlo. Si una red repite la llamada, devuelve el resultado guardado en vez de emitir otro reembolso, mensaje, deployment o cambio de base de datos.

El nombre y los argumentos generados por el modelo son input no confiable. Un Schema válido no concede permisos ni sustituye una transacción.

Decidir cómo tratar múltiples llamadas

Con parallel_tool_calls: false el state machine es más fácil de inspeccionar. Si se habilita paralelismo:

  • correlaciona por Call ID, nunca por orden de finalización;
  • limita llamadas por respuesta y concurrency de aplicación;
  • define el efecto de una herramienta fallida sobre las demás;
  • devuelve un resultado por cada llamada aceptada;
  • serializa efectos secundarios cuyo orden importe.

El paralelismo reduce latencia para lecturas independientes, pero aumenta complejidad de autorización, orden, retry y fallo parcial.

Comprender el límite de compatibilidad de Modelflare

Modelflare conserva definiciones strict y mapea formas compatibles entre Responses y Chat Completions en rutas OpenAI/Codex elegibles. El mapping es deliberadamente menor que toda la superficie de Tools de Responses.

Las funciones definidas por la aplicación pueden expresarse en ambos formatos. Hosted Tools ejecutadas por el proveedor —búsqueda, ejecución de código u otros tipos— no equivalen a Function Calling de la aplicación y no se deben convertir por suposición. Otras familias OpenAI-compatible funcionan como Raw Chat Completions Pass-through hasta verificarse; el upstream decide soporte para tools, strict, llamadas paralelas, argumentos en streaming y tool_choice.

Prueba Evidencia necesaria
Llamada de lectura Nombre, argumentos, Call ID, resultado y texto final
Argumentos inválidos Fallo explícito sin ejecución
Función desconocida Rechazo por allowlist
Sin herramienta Texto normal sin llamada inventada
Streaming Argumentos reconstruidos e ID correcto
Request repetido Un efecto o un resultado almacenado
Varias llamadas Correlación independiente del orden
Route fallback Mismo modelo, protocolo, Schema y contrato

Usa Reliable AI API Routing para cambios explícitos de ruta y seguridad de AI API Keys para aislar permisos.

Elegir formato según el contrato de la aplicación

Usa Responses para output items tipados, su modelo de eventos, continuación de estado u otras funciones verificadas. Usa Chat Completions cuando la aplicación mantiene un transcript estable y el contrato de Tools del proveedor está comprobado. Consulta Responses API vs Chat Completions.

El estándar es el mismo: allowlist explícita, Schema strict, validación de aplicación, autorización antes de ejecutar, correlación por Call ID, idempotencia de efectos y resultado completo para el modelo. Esos controles, no el nombre del endpoint, hacen fiable el workflow.