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
toolsytool_calls. Anthropic usa bloquestool_useytool_result. Gemini usafunctionDeclarationsy 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_choiceen 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:
- Lista de herramientas. Nombre, descripción y esquema de entrada de cada herramienta.
- Elección de herramienta. Forzar una herramienta específica, prohibir herramientas o dejar que el modelo decida.
- Emisión de la llamada. Salida estructurada que especifica el nombre de la herramienta y los argumentos.
- ID de la llamada. Correlacionar la respuesta con la llamada correcta (importante para llamadas en paralelo).
- 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 deoneOf/anyOf/allOf, y que cada propiedad esté listada enrequired. - 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_usesepara 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.argumentsllegan incrementalmente. Los acumulas hasta que ocurrafinish_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_deltatransportan argumentos parciales. - Gemini.
streamFunctionCallArguments(nuevo en Gemini 3) emite fragmentos con unfunctionCallIdpara 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.
inputpuede contener campos inesperados; el esquema es de carácter consultivo. Valida del lado del servidor. - Gemini. Peculiaridad de OpenAPI 3.0:
enumen 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, bloquecontent[], entradaparts[]). - 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
Ejecuta
code/main.pyy verifica que los tres JSON de declaración de proveedores serialicen el mismo objetoToolbajo 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.Agrega un analizador
ListToolsResponsepara cada proveedor que extraiga la lista de herramientas que devuelve un modelo después de una llamadalist_toolso de descubrimiento. OpenAI no tiene uno de forma nativa; ten en cuenta esta asimetría.Implementa la conversión de
tool_choice: mapea unToolChoice(mode="force", tool_name="x")canónico a las tres formas de proveedor. Luego mapeamode="any"ymode="none". Consulta la tabla comparativa de la lección.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:
strictde OpenAI,disable_parallel_tool_usede Anthropic,function_calling_config.allowed_function_namesde Gemini.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
- OpenAI — Function calling guide — referencia canónica que incluye el modo estricto y llamadas paralelas
- Anthropic — Tool use overview — semántica de los bloques
tool_useytool_result - Google — Gemini function calling — llamadas paralelas, IDs únicos y subconjunto de OpenAPI
- Vertex AI — Function calling reference — interfaz empresarial de Gemini
- OpenAI — Structured outputs — detalles sobre el cumplimiento del esquema en modo estricto