Phase 13 - Lesson 02

Deep Dive en Function Calling — OpenAI, Anthropic, Gemini

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Los tres proveedores de frontera convergieron en el mismo ciclo de llamada de herramienta en 2024 y luego divergieron en todo lo demás. OpenAI usa tools y tool_calls. Anthropic usa bloques tool_use y tool_result. Gemini usa functionDeclarations y correlación de ID único. Esta lección compara los tres lado a lado para que el código que se ejecuta en un proveedor no se rompa al portarlo.

Tipo: Build Idiomas: Python (stdlib, schema translators) Prerrequisitos: Phase 13 · 01 (the tool interface) Tiempo: ~75 minutos

Objetivos de Aprendizaje

  • Identificar las tres diferencias de forma entre los payloads de llamada de función de OpenAI, Anthropic y Gemini (declaración, llamada, resultado).
  • Traducir una declaración de herramienta a los formatos de los tres proveedores y predecir dónde diferirán las restricciones del modo estrito (strict-mode).
  • Usar tool_choice en cada proveedor para forzar, prohibir o seleccionar automáticamente llamadas a herramientas.
  • Conocer los límites estrictos de cada proveedor (cantidad de herramientas, profundidad del esquema, longitud de los argumentos) y las firmas de error que emite cada uno cuando se violan los límites.

El Problema

La forma de una solicitud de llamada de función difiere según el proveedor. Tres ejemplos concretos de stacks de producción de 2026:

OpenAI Chat Completions / Responses API. Se pasa tools: [{type: "function", function: {name, description, parameters, strict}}]. La respuesta del modelo contiene choices[0].message.tool_calls: [{id, type: "function", function: {name, arguments}}] donde arguments es una cadena JSON que debes analizar (parsear). El modo estricto (strict: true) impone el cumplimiento del esquema mediante decodificación restringida.

Anthropic Messages API. Se pasa tools: [{name, description, input_schema}]. La respuesta regresa como content: [{type: "text"}, {type: "tool_use", id, name, input}]. input ya está analizado (un objeto, no una cadena). Respondes con un nuevo mensaje de user que contiene un bloque {type: "tool_result", tool_use_id, content}.

Google Gemini API. Se pasa tools: [{functionDeclarations: [{name, description, parameters}]}] (anidado bajo functionDeclarations). La respuesta llega como candidates[0].content.parts: [{functionCall: {name, args, id}}] donde id es único en Gemini 3 y superior para la correlación de llamadas paralelas. Respondes con {functionResponse: {name, id, response}}.

El mismo ciclo. Diferentes nombres de campos, diferente anidamiento, diferentes convenciones de cadena vs. objeto, diferentes mecanismos de correlación. Un equipo que escribe un agente del clima en OpenAI paga una migración de dos días a Anthropic y otro día a Gemini solo por la plomería de la integración.

Esta lección construye un traductor que unifica los tres formatos en una declaración de herramienta canónica y enruta en el borde (edge). La Phase 13 · 17 generaliza este mismo patrón en un gateway de LLM.

El Concepto

La estructura común

Cada proveedor necesita de cinco cosas:

  1. Lista de herramientas. Nombre, descripción y esquema de entrada de cada herramienta.
  2. Elección de herramienta. Forzar una herramienta específica, prohibir herramientas o dejar que el modelo decida.
  3. Emisión de la llamada. Salida estructurada que especifica el nombre de la herramienta y los argumentos.
  4. ID de la llamada. Correlacionar la respuesta con la llamada correcta (importante para llamadas en paralelo).
  5. Inyección de resultado. Un mensaje o bloque que vincula el resultado de regreso a la llamada.

Diferencias de forma, campo por campo

Aspecto OpenAI Anthropic Gemini
Contenedor de declaración {type: "function", function: {...}} {name, description, input_schema} {functionDeclarations: [{...}]}
Campo del esquema parameters input_schema parameters
Contenedor de respuesta tool_calls[] en el mensaje del asistente content[] de tipo tool_use parts[] de tipo functionCall
Tipo de argumentos JSON como cadena objeto analizado (parsed) objeto analizado (parsed)
Formato de ID call_... (generado por OpenAI) toolu_... (Anthropic) UUID (Gemini 3+)
Bloque de resultado rol (role) tool, tool_call_id user con tool_result, tool_use_id functionResponse con id correspondiente
Forzar una herramienta tool_choice: {type: "function", function: {name}} tool_choice: {type: "tool", name} tool_config: {function_calling_config: {mode: "ANY"}}
Prohibir herramientas tool_choice: "none" tool_choice: {type: "none"} mode: "NONE"
Esquema estricto (strict) strict: true esquema-es-esquema (siempre se aplica) responseSchema a nivel de solicitud

Límites reales que alcanzarás

  • OpenAI. 128 herramientas por solicitud. Profundidad de esquema de 5. Cadena de argumentos <= 8192 bytes. El modo estricto requiere que no haya $ref, ni superposiciones de oneOf/anyOf/allOf, y que cada propiedad esté listada en required.
  • Anthropic. 64 herramientas por solicitud. Profundidad del esquema efectivamente ilimitada, pero con un límite práctico de 10. Sin flag de modo estricto; el esquema es un contrato y el modelo tiende a cumplirlo.
  • Gemini. 64 funciones por solicitud. Los tipos de esquema son un subconjunto de OpenAPI 3.0 (ligera divergencia de JSON Schema 2020-12). Las llamadas paralelas usan ID únicos desde Gemini 3.

Comportamiento de tool_choice

Tres modos que todos soportan, nombrados de manera diferente.

  • Auto. El modelo elige la herramienta o texto. Predeterminado.
  • Required / Any. El modelo debe llamar al menos a una herramienta.
  • None. El modelo no debe llamar a herramientas.

Además de un modo único para cada proveedor:

  • OpenAI. Fuerza una herramienta específica por nombre.
  • Anthropic. Fuerza una herramienta específica por nombre; la flag disable_parallel_tool_use separa el uso único del múltiple.
  • Gemini. mode: "VALIDATED" enruta cada respuesta a través de un validador de esquema, independientemente de la intención del modelo.

Llamadas paralelas

El parallel_tool_calls: true de OpenAI (predeterminado) emite múltiples llamadas en un solo mensaje del asistente. Las ejecutas todas y respondes con un mensaje por lotes con rol (role) tool que contiene una entrada por cada tool_call_id. Anthropic históricamente realizaba llamadas únicas; disable_parallel_tool_use: false (predeterminado a partir de Claude 3.5) habilita las llamadas múltiples. Gemini 2 permitía llamadas en paralelo pero no entregaba IDs estables; Gemini 3 añade UUIDs para que las respuestas fuera de orden se correlacionen de forma limpia.

Streaming

Los tres soportan llamadas a herramientas en streaming. El formato de transmisión difere:

  • OpenAI. Fragmentos delta de tool_calls[i].function.arguments llegan incrementalmente. Los acumulas hasta que ocurra finish_reason: "tool_calls".
  • Anthropic. Eventos de inicio de bloque (block-start), delta de bloque (block-delta) y fin de bloque (block-stop). Fragmentos de input_json_delta transportan argumentos parciales.
  • Gemini. streamFunctionCallArguments (nuevo en Gemini 3) emite fragmentos con un functionCallId para que múltiples llamadas paralelas puedan entrelazarse.

La Phase 13 · 03 profundiza en el reensamblaje de llamadas paralelas + streaming. Esta lección se enfoca en las formas de declaración y de llamada única.

Errores y reparación

Los errores de argumentos inválidos también se ven diferentes.

  • OpenAI (no estricto). El modelo devuelve arguments: "{bad json}", tu análisis (parse) de JSON falla, inyectas un mensaje de erro y vuelves a realizar la llamada.
  • OpenAI (estricto). La validación ocurre durante la decodificación; un JSON inválido es imposible pero puede aparecer un refusal (rechazo).
  • Anthropic. input puede contener campos inesperados; el esquema es de carácter consultivo. Valida del lado del servidor.
  • Gemini. Peculiaridad de OpenAPI 3.0: enum en campos de objeto se ignora silenciosamente; valídalo tú mismo.

El patrón de traductor

Una declaración de herramienta canónica en tu código se ve así (tú eliges la forma):

Tool(
    name="get_weather",
    description="Use when ...",
    input_schema={"type": "object", "properties": {...}, "required": [...]},
    strict=True,
)

Tres funciones pequeñas traducen esto a las formas de los tres proveedores. El arnés en code/main.py hace exactamente esto, luego realiza un ciclo completo (round-trip) de una llamada de herramienta ficticia a través de la forma de respuesta de cada proveedor. No se requiere conexión de red; esta lección enseña las formas, no el protocolo HTTP.

Los equipos de producción envuelven este traductor en AbstractToolset (Pydantic AI), UniversalToolNode (LangGraph) o BaseTool (LlamaIndex). La Phase 13 · 17 entrega un gateway que expone una API con la forma de OpenAI frente a cualquiera de los tres.

Uso

El archivo code/main.py define una dataclass canónica Tool y tres tradutores que emiten el JSON de declaración para OpenAI, Anthropic y Gemini. Luego, analiza (parsea) una respuesta de proveedor construida a mano de cada forma en el mismo objeto de llamada canónico, demostrando que la semántica es idéntica internamente. Ejecútalo y compara las tres declaraciones lado a lado.

Qué observar:

  • Los tres bloques de declaración difieren solo en el sobre y los nombres de campo.
  • Los tres bloques de respuesta difieren en dónde vive la llamada (nivel superior de tool_calls, bloque content[], entrada parts[]).
  • Una única función canonical_call() extrae {id, name, args} de las tres formas de respuesta.

Entrega

Esta lección produce el archivo outputs/skill-provider-portability-audit.md. Dada una integración de llamada de función contra un proveedor, la habilidad produce una auditoría de portabilidad: en qué límites del proveedor se apoya, qué campos requieren renombrarse y qué se rompe al portarse a cada uno de los otros proveedores.

Ejercicios

  1. Ejecuta code/main.py y verifica que los tres JSON de declaración de proveedores serialicen el mismo objeto Tool bajo y subyacente. Modifica la herramienta canónica para agregar un parámetro enum y confirma que solo el traductor de Gemini necesita manejar la peculiaridad de OpenAPI.

  2. Agrega un analizador ListToolsResponse para cada proveedor que extraiga la lista de herramientas que devuelve un modelo después de una llamada list_tools o de descubrimiento. OpenAI no tiene uno de forma nativa; ten en cuenta esta asimetría.

  3. Implementa la conversión de tool_choice: mapea un ToolChoice(mode="force", tool_name="x") canónico a las tres formas de proveedor. Luego mapea mode="any" y mode="none". Consulta la tabla comparativa de la lección.

  4. Elige uno de los tres proveedores y lee su guía de llamada de función de principio a fin. Encuentra un campo en su especificación de esquema que los otros dos no soporten. Candidatos: strict de OpenAI, disable_parallel_tool_use de Anthropic, function_calling_config.allowed_function_names de Gemini.

  5. Escribe un vector de prueba: una llamada a herramienta cuyos argumentos violen el esquema declarado. Ejecútalo a través del validador de cada proveedor (el de la biblioteca estándar en la Lección 01 servirá como proxy) y registra qué errores se disparan. Documenta qué proveedor usarías en producción para mayor rigurosidad (strictness).

Términos Clave

Término Lo que la gente dice Lo que realmente significa
Function calling "Uso de herramientas" (Tool use) API a nivel de proveedor para la emisión estructurada de llamadas a herramientas
Declaración de herramienta "Especificación de herramienta" Nombre + descripción + payload de entrada JSON Schema
tool_choice "Forzar / prohibir" Modos auto / required / none / nombre-específico
Modo estricto (Strict mode) "Cumplimiento del esquema" Flag de OpenAI que restringe la decodificación para que coincida con el esquema
Bloque tool_use "Forma de llamada de Anthropic" Bloque de contenido en línea con id, name, input
Parte functionCall "Forma de llamada de Gemini" Una entrada parts[] que contiene name, args e id
Argumentos como cadena "JSON serializado como cadena" OpenAI devuelve los argumentos como una cadena JSON, no como un objeto
Llamadas a herramientas paralelas "Fan-out en un solo turno" Múltiples llamadas a herramientas en un solo mensaje del asistente
Rechazo (Refusal) "El modelo se rehúsa" Bloque de rechazo exclusivo de modo estricto en lugar de una llamada
Subconjunto de OpenAPI 3.0 "Peculiaridad de esquema de Gemini" Gemini usa un dialecto similar a JSON Schema con diferencias menores

Lecturas Recomendadas

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).