Function calling en OpenAI: estructurando salidas del modelo
Actualizado: 2026-05-03
OpenAI introdujo function calling en GPT-3.5-turbo y GPT-4 en junio de 2023. Es una de las features que más cambia cómo se integra un LLM en una aplicación: en lugar de parsear texto libre y rezar, el modelo devuelve directamente JSON estructurado que puedes ejecutar. Cubrimos cómo funciona, qué patrones se han consolidado, y los errores típicos que aparecen en producción.
Puntos clave
- Function calling formaliza la comunicación entre el modelo y el código externo mediante JSON Schema declarativo.
- El modelo decide cuándo invocar una función — o responder directamente. Maneja siempre ambos casos.
- Los schemas demasiado permisivos y las descripciones pobres son las causas número uno de salidas incorrectas.
- El patrón ReAct — decide, actúa, observa, itera — es la base de todos los frameworks de agentes.
- Cada declaración de función consume input tokens; con muchas funciones o contextos largos, el coste se acumula.
El problema que resuelve
Antes de function calling, integrar GPT con un sistema externo seguía un patrón frágil:
- Pides al modelo que responda en JSON con un schema concreto.
- Aplicas regex o un parser robusto a la salida.
- Si el modelo se desvía mínimamente (un comentario fuera del JSON, una coma extra), todo falla.
- Añades reintentos con prompts cada vez más estrictos.
Function calling formaliza este patrón. Declaras explícitamente las “funciones” disponibles y sus parámetros usando JSON Schema. El modelo decide cuándo invocar una y devuelve un objeto que respeta el schema. El parsing es trivial.
Cómo se declara
Una función se describe con tres campos:
{
"name": "get_weather",
"description": "Obtiene el tiempo actual en una ciudad",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Ciudad, p.ej. 'Madrid'"},
"units": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}Pasas la lista de funciones disponibles junto con el mensaje del usuario:
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": "¿Qué tiempo hace en Madrid?"}],
functions=[get_weather_function],
function_call="auto",
)La respuesta puede ser un mensaje normal, o un objeto con function_call que contiene name y arguments (un string JSON con los parámetros). Tu código recibe el JSON, ejecuta la función real, y opcionalmente devuelve el resultado al modelo para que componga la respuesta final.
Casos de uso que han madurado
En los meses desde el lanzamiento han emergido patrones claros:
- Agentes simples. Un modelo que puede invocar herramientas (búsqueda web, calculadora, base de datos) cuando lo necesita.
- Extracción estructurada. Convertir texto libre (correo, llamada transcrita, formulario sin estructura) en datos tipados — un caso donde antes se usaba regex frágil o NER tradicional.
- APIs de conversación tipo “asistente del producto”. El usuario pide en lenguaje natural; el modelo decide qué endpoint backend invocar.
- Routing de consultas. Decidir entre varios pipelines (FAQ, ticket de soporte, escalado humano) basándose en la naturaleza de la pregunta.
- Generación de código que llama APIs internas. El modelo “compone” llamadas a funciones del sistema dado un objetivo de alto nivel.
El patrón ReAct con function calling
El loop que repiten todos los frameworks de agentes — LangChain, LlamaIndex, AutoGen — en su forma esencial:
1. Usuario hace pregunta
2. Modelo recibe la pregunta + lista de funciones
3. Modelo decide: responde directamente o invoca función
4. Si invoca: ejecutar función real
5. Pasar resultado al modelo
6. Modelo decide otra vez: nueva función o respuesta final
7. Repetir hasta que el modelo dé respuesta finalFunction calling hace este loop robusto. Sin él, era un castillo de naipes basado en parseo de texto. Con él, la arquitectura es determinista y testeable.
Errores comunes en producción
Después de meses de uso, los problemas que aparecen una y otra vez:
- Schemas demasiado permisivos. Si declaras un parámetro como
stringsinenumni descripción precisa, el modelo improvisa. Sé estricto con tipos y enumeraciones. - Descripciones pobres. La
descriptionde la función y de cada parámetro es lo que el modelo lee para decidir cuándo invocarla. Es prompt, no documentación. Inviértele tiempo. - Demasiadas funciones a la vez. Más de 10-15 funciones simultáneas degrada la decisión del modelo. Considera categorización o un primer paso de routing.
- Confiar en que la función se invoca siempre. El modelo puede decidir responder directamente. Maneja ambos casos explícitamente.
- No validar los argumentos. El modelo se ajusta al schema en >95% de los casos pero no siempre. Valida antes de ejecutar — sobre todo si la función tiene side effects (borrar datos, enviar email).
- Bucles infinitos en loops de agente. Sin límite de iteraciones, un agente confundido puede llamarse a sí mismo eternamente. Pon
max_iterations.
Coste y latencia
Function calling tiene costes extra que conviene conocer:
- Tokens consumidos: la declaración de funciones se incluye en cada llamada — ocupa input tokens. Un schema grande puede sumar 500-1000 tokens por petición.
- Latencia de la cadena: cada loop “modelo → función → modelo” añade roundtrips. Un agente que requiere cuatro pasos tarda 4× más que una respuesta directa.
- Cacheo: las funciones declaradas no cambian entre llamadas; con prompt caching el coste se amortiza por debajo de lo que parece. Esto conecta con los principios del prompt engineering maduro.
Conclusión
Function calling es una de las mejores adiciones recientes a la API de OpenAI: resuelve un problema real (parsear salida de LLM) con la abstracción correcta. Para cualquier proyecto que integre GPT con sistemas que esperan datos estructurados, es la opción por defecto. Los patrones de agentes que se construyen sobre esto van a definir cómo se construye software con IA en los próximos años.