El function calling se ve idéntico entre proveedores —hasta que no lo es. OpenAI envía tool_calls como un delta que acumulas a través de los chunks de streaming. Anthropic devuelve tool_use como un bloque de contenido al mismo nivel que los bloques text. Google envuelve todo en candidates con objetos functionCall. DeepSeek sigue a OpenAI de cerca —hasta que no lo hace en llamadas paralelas.
Tu código de agente se rompe cada vez que cambias de modelo. Esta guía lo arregla. Código funcional para los cuatro proveedores. Una tabla de diferencias que te dice qué se rompe dónde. Y un patrón de wrapper unificado que te permite escribir definiciones de herramientas una vez y usarlas en todas partes. El function calling es la base de cada creación de agentes de IA —domina el bucle de herramientas y la arquitectura de agentes se vuelve sencilla.
Cómo funciona realmente el function calling
El patrón es el mismo en todos los proveedores. Entenderlo una vez es más importante que memorizar la sintaxis de cada proveedor.
El bucle de herramientas:
- Defines herramientas —nombre, descripción, JSON Schema para parámetros
- Envías un mensaje de usuario + definiciones de herramientas al modelo
- El modelo decide si responder con texto o solicitar una llamada de herramienta
- Si es llamada de herramienta: tu código analiza el nombre de la función y los argumentos —ejecuta la función —envía el resultado de vuelta
- El modelo procesa el resultado —decide: responder con texto, o llamar otra herramienta
- Repite hasta que el modelo responda con texto o llegues a un límite máximo de iteraciones
Function calling vs. salidas estructuradas. Function calling: el modelo decide cuándo usar una herramienta. Salidas estructuradas: el modelo siempre devuelve tu schema. Usa function calling cuando el modelo necesite autonomía —“averigua qué información necesitas y consíguela”. Usa salidas estructuradas cuando necesites formato garantizado —“siempre devuelve un objeto JSON con estos campos”.
Proveedor 1: OpenAI Function Calling
La implementación de function calling de OpenAI es la más madura y el estándar de referencia que otros siguen.
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.tokspan.com/v1",
api_key="ts-your-key-here"
)
tools = [{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol. Returns price in USD.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}
}]
response = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "What's Apple's stock price?"}],
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
if msg.tool_calls:
for tool_call in msg.tool_calls:
args = json.loads(tool_call.function.arguments)
result = execute_stock_lookup(args["symbol"])
# Send result back
messages = [
{"role": "user", "content": "What's Apple's stock price?"},
msg,
{"role": "tool", "tool_call_id": tool_call.id, "content": str(result)}
]
final = client.chat.completions.create(model="gpt-5.5", messages=messages)
print(final.choices[0].message.content)
Especificidades de OpenAI. Llamadas de herramientas paralelas: GPT-5.5 puede solicitar múltiples herramientas en una respuesta —verifica múltiples elementos en msg.tool_calls. Streaming: tool_calls llegan como deltas; acumula index —function.name —function.arguments a través de los chunks. Salidas estructuradas + function calling: define parámetros de herramienta con strict: true para cumplimiento de schema garantizado.
Proveedor 2: Uso de herramientas de Anthropic
El uso de herramientas de Claude es estructuralmente diferente —las herramientas aparecen como bloques de contenido dentro de los mensajes, no como un campo separado.
import anthropic
client = anthropic.Anthropic(
base_url="https://api.tokspan.com/anthropic",
api_key="ts-your-key-here"
)
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
tools=[{
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol.",
"input_schema": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}],
messages=[{"role": "user", "content": "What's Apple's stock price?"}]
)
for block in response.content:
if block.type == "tool_use":
# Execute the tool Claude requested
result = execute_stock_lookup(block.input["symbol"])
# Build the conversation continuation —the full cycle:
# 1. The assistant message contains ALL content blocks from Claude's response
# 2. The user message contains tool_result blocks matching each tool_use
assistant_msg = {"role": "assistant", "content": response.content}
tool_result_msg = {
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
}]
}
# Send the result back and get Claude's final response
follow_up = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[
{"role": "user", "content": "What's Apple's stock price?"},
assistant_msg,
tool_result_msg
]
)
# Claude will return a text block with the final answer
for follow_block in follow_up.content:
if follow_block.type == "text":
print(follow_block.text)
Diferencias clave de OpenAI. Las definiciones de herramientas usan input_schema en lugar de parameters. Las llamadas de herramientas son bloques de contenido tool_use dentro de response.content —son pares de los bloques text, no un campo separado. Los resultados de herramientas se envían como bloques de contenido tool_result en un mensaje de usuario. El streaming incluye bloques tool_use parciales —obtienes el nombre y los argumentos de la herramienta incrementalmente.
Proveedor 3: Google Gemini Function Calling
# Gemini uses a different structure —function declarations with OpenAPI-like schema
tools = [{
"function_declarations": [{
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}]
}]
# Response structure:
# response.candidates[0].content.parts[0].function_call.name
# response.candidates[0].content.parts[0].function_call.args
Especificidades de Gemini. Function calling automático: Gemini puede llamar y ejecutar funciones en un solo request de API —configura automatic_function_calling en la configuración de herramientas. Grounding de búsqueda: una “herramienta” integrada que basa respuestas en resultados de Google Search sin que implementes una API de búsqueda.
Proveedor 4: DeepSeek Function Calling
DeepSeek sigue el formato de OpenAI. Misma definición de herramientas, misma estructura de respuesta. La diferencia práctica: la llamada paralela de herramientas es menos confiable que GPT-5.5 —las herramientas que deberían llamarse en paralelo a veces se llaman secuencialmente. Prueba tus escenarios de múltiples herramientas específicamente si estás cambiando de GPT-5.5 a DeepSeek.
# Identical to OpenAI code —just change base_url and model
client = OpenAI(
base_url="https://api.tokspan.com/v1",
api_key="ts-your-key-here"
)
# Same tool definitions, same response handling as OpenAI example above
Tabla de diferencias entre proveedores
| Característica | OpenAI | Anthropic | DeepSeek | |
|---|---|---|---|---|
| Formato de definición de herramientas | function.parameters (JSON Schema) | input_schema (JSON Schema) | function_declarations.parameters | Igual que OpenAI |
| Ubicación de la respuesta | message.tool_calls[] | bloques content[] | candidates[].content.parts[] | Igual que OpenAI |
| Llamadas de herramientas paralelas | Sí, confiable | Sí, confiable | Sí | Parcial, menos confiable |
| Herramientas de streaming | Deltas, se acumulan | Bloques parciales | Candidates parciales | Igual que OpenAI |
| Control de elección de herramienta | tool_choice: "auto"/"required"/"none" | tool_choice con opciones similares | function_calling_config | Igual que OpenAI |
| Máximo de herramientas por request | 128 | No documentado (grande) | No documentado | Sigue a OpenAI |
| Cambios de código para cambiar | — | 100% (SDK diferente) | ~80% | 0% (desde OpenAI) |
Errores comunes
Estos son los bugs que llegan a producción. Cada uno tiene un arreglo que puedes implementar en una tarde —pero solo si sabes buscarlo antes que tus usuarios.
1. Bugs de acumulación de tool_calls en streaming. En modo streaming, tool_calls llegan a través de múltiples chunks —cada uno lleva un index, un function.name parcial y una cadena function.arguments parcial. El error: llamar a json.loads() en la cadena de argumentos antes de que llegue el chunk delta final con finish_reason: "tool_calls". Obtienes un JSONDecodeError cada vez, y la lógica de reintentos lo empeora porque el estado parcial se corrompe. Acumula argumentos por index a través de los chunks. Analiza solo cuando el stream señale completitud. Este es uno de los modos de falla más comunes de la llamada de herramientas en producción.
2. Diferencias de JSON Schema entre proveedores. OpenAI soporta $ref, anyOf y oneOf anidados en los schemas de parámetros de herramientas. Gemini ignora silenciosamente las definiciones $ref —tu herramienta funciona, pero el modelo nunca ve el schema referenciado. Anthropic ejecuta validación del lado del servidor más estricta que OpenAI; un schema que pasa en GPT-5.5 devuelve un 400 en Claude con un error de validación opaco. Prueba tus schemas contra cada proveedor en CI, no manualmente el día antes del lanzamiento. Un paso de validación de schemas en CI con la API de cada proveedor lo atrapa en minutos.
3. Desajuste de ID en llamadas paralelas de herramientas. El modelo devuelve get_price("AAPL") y get_price("GOOGL") en una respuesta. Ejecutas ambos concurrentemente. Los resultados llegan fuera de orden. Los mapeas de vuelta al tool_call_id equivocado porque asumiste que la posición coincide con el orden de ejecución. El modelo recibe el precio de GOOGL bajo el ID de AAPL y genera una respuesta confiada, plausible y completamente incorrecta. Siempre indexa resultados por tool_call_id antes de construir los mensajes de resultados. Nunca dependas de la posición del arreglo.
4. Errores de herramientas que pasan como datos legítimos. La llamada HTTP de tu función get_stock_price agota el tiempo. Capturas la excepción y devuelves la cadena "Error: connection timeout". El modelo lee esa cadena como datos y responde: “El precio actual es Error: connection timeout.” Formatea los errores de herramientas con un prefijo reconocible como TOOL_ERROR: <type> —<message>. Describe el manejo de errores en el campo description de la herramienta para que el modelo sepa reintentar o decirte que la herramienta falló. Un modelo no puede distinguir entre un bug y datos inusuales a menos que le des una señal.
Un wrapper unificado de function calling
El patrón del wrapper: define herramientas una vez en un formato agnóstico de proveedor. Traduce al formato nativo de cada proveedor en el momento de la llamada. Normaliza las respuestas de vuelta a un formato unificado.
class UnifiedToolClient:
"""One tool definition. Any provider. Automatic translation."""
def __init__(self, base_url: str, api_key: str):
self.openai_client = OpenAI(base_url=base_url, api_key=api_key)
def call_with_tools(self, model: str, messages: list, tools: list):
"""Provider-agnostic tool calling. Handles translation internally."""
# Tools defined in OpenAI format —works for OpenAI, DeepSeek, and
# platforms that translate to Anthropic/Google natively
response = self.openai_client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto"
)
return self._normalize_response(response)
def _normalize_response(self, response):
"""Return a unified format regardless of which provider served the request."""
msg = response.choices[0].message
return {
"text": msg.content,
"tool_calls": [
{"name": tc.function.name, "arguments": json.loads(tc.function.arguments)}
for tc in (msg.tool_calls or [])
] if msg.tool_calls else []
}
El atajo de la plataforma de agregación. Este wrapper son 30 líneas de código. Pero solo maneja la traducción para proveedores que hablan formato compatible con OpenAI. Para funciones nativas de Anthropic (pensamiento + uso de herramientas juntos, resultados parciales de herramientas en streaming) y funciones nativas de Google (function calling automático), necesitas una plataforma con soporte de protocolo nativo para cada proveedor —de lo contrario estás manteniendo tres rutas de código separadas. Las plataformas con soporte multi-protocolo manejan esto a nivel de infraestructura. Tu código se mantiene agnóstico de proveedor mientras las características únicas de cada proveedor siguen disponibles. Si apenas estás empezando con la llamada de herramientas unificado, la guía de inicio rápido de TokSpan te guía para configurar tu primer request de herramientas multi-proveedor en menos de cinco minutos.
FAQ
¿Qué proveedor tiene el mejor function calling?
GPT-5.5: el más confiable, mejor llamada paralela, ecosistema más fuerte. Claude Opus: mejor para cadenas de herramientas complejas de varios pasos donde la profundidad de razonamiento importa. Gemini: el function calling automático es una victoria de conveniencia para herramientas simples. DeepSeek: suficientemente bueno para herramientas simples, ocasionalmente poco confiable para llamadas paralelas. Usa GPT-5.5 cuando la confiabilidad de herramientas sea crítica. Usa Claude cuando la profundidad de razonamiento de herramientas importe más que la confiabilidad cruda.
¿Puedo usar las mismas definiciones de herramientas en todos los proveedores?
No nativamente. El JSON Schema es compartido, pero el formato del wrapper difiere. Usa una capa de traducción (30 líneas de Python) o una plataforma de agregación que traduzca automáticamente. Tus definiciones de herramientas —nombres, descripciones, schemas de parámetros— son portables incluso cuando el formato del wrapper no lo es.
¿Cuántas herramientas puedo definir por request?
OpenAI: 128. Anthropic: no documentado pero grande. Google: sin límite duro. En la práctica, más de 10 herramientas degrada la precisión de selección —el modelo empieza a confundir herramientas con nombres similares. Mantén tu conjunto de herramientas activas enfocado.
¿Debo construir mi propio wrapper o usar una plataforma?
Construye si usas 1–2 proveedores y necesitas control específico sobre el bucle de llamada de herramientas. Usa una plataforma si quieres cambiar libremente de proveedor y evitar mantener cuatro rutas de código. El patrón de wrapper de esta guía toma 30 minutos de implementar y mantener. Una plataforma lo hace cero —consulta los docs de TokSpan para la API a nivel de plataforma que maneja traducción y normalización entre los cuatro proveedores.
El function calling es la base de cada agente de IA. Pero aquí está la pregunta incómoda que la industria no ha respondido: ¿por qué, en 2026, cada proveedor de LLM todavía tiene un formato ligeramente diferente para las definiciones de herramientas? El JSON Schema es compartido. El concepto de “tool_call” es compartido. Sin embargo, el formato del wrapper —input_schema vs. parameters, bloques tool_use vs. arreglo tool_calls— sigue obstinadamente específico del proveedor. Un organismo de estándares podría arreglarlo en un grupo de trabajo de seis meses. Hasta ahora, nadie lo ha convocado. La pregunta: ¿forzará el mercado la estandarización a través del default compatible con OpenAI, o las características nativas de uso de herramientas se volverán tan diferenciadas que la compatibilidad entre proveedores sea abandonada permanentemente?
Prueba el function calling unificado —una definición de herramienta. Cuatro proveedores. Cero código de wrapper —mientras la industria averigua si la estandarización realmente viene.