Ir al contenido principal

Primeros pasos con Structured Outputs de OpenAI

Aprende a empezar con Structured Outputs de OpenAI, entiende su nueva sintaxis y descubre sus principales aplicaciones.
Actualizado 17 sept 2026  · 9 min leer

Explorar con IA

ChatGPTClaudePerplexity

En agosto de 2024, OpenAI anunció una nueva y potente función en su API: Structured Outputs. Como su nombre indica, con esta función puedes garantizar que los LLM generen respuestas únicamente en el formato que especifiques. Esta capacidad facilita enormemente crear aplicaciones que requieren un formato de datos preciso. 

En este tutorial, aprenderás a empezar con Structured Outputs de OpenAI, entender su nueva sintaxis y explorar sus aplicaciones clave.

Desarrollar aplicaciones de IA

Aprende a crear aplicaciones de IA utilizando la API OpenAI.
Empieza a Hacer Upskilling Gratis

Importancia de Structured Outputs en aplicaciones de IA

Las respuestas deterministas, o dicho de otra forma, respuestas con un formato consistente, son cruciales para muchas tareas como entrada de datos, recuperación de información, preguntas y respuestas, flujos de trabajo en varios pasos, etc. Seguramente hayas visto cómo los LLM pueden generar salidas en formatos muy distintos, incluso con el mismo prompt.

Por ejemplo, piensa en esta sencilla función classify_sentiment impulsada por GPT-4o:

# List of hotel reviews
reviews = [
   "The room was clean and the staff was friendly.",
   "The location was terrible and the service was slow.",
   "The food was amazing but the room was too small.",
]
# Classify sentiment for each review and print the results
for review in reviews:
   sentiment = classify_sentiment(review)
   print(f"Review: {review}\nSentiment: {sentiment}\n")

Salida:

Review: The room was clean and the staff was friendly.
Sentiment: Positive
Review: The location was terrible and the service was slow.
Sentiment: Negative
Review: The food was amazing but the room was too small.
Sentiment: The sentiment of the review is neutral.

Aunque las dos primeras respuestas tienen el mismo formato de una palabra, la última es una frase completa. Si alguna otra aplicación dependiera de la salida del código anterior, fallaría porque espera una respuesta de una sola palabra.

Podemos arreglarlo con algo de prompt engineering, pero es un proceso iterativo que lleva tiempo. Incluso con un prompt perfecto, no podemos asegurar al 100% que las respuestas respeten el formato en futuras peticiones. A menos que usemos Structured Outputs:

def classify_sentiment_with_structured_outputs(review):
   """Sentiment classifier with Structured Outputs"""
   ...
# Classify sentiment for each review with Structured Outputs
for review in reviews:
   sentiment = classify_sentiment_with_structured_outputs(review)
   print(f"Review: {review}\nSentiment: {sentiment}\n")

Salida:

Review: The room was clean and the staff was friendly.
Sentiment: {"sentiment":"positive"}
Review: The location was terrible and the service was slow.
Sentiment: {"sentiment":"negative"}
Review: The food was amazing but the room was too small.
Sentiment: {"sentiment":"neutral"}

Con la nueva función, classify_sentiment_with_structured_outputs, todas las respuestas siguen el mismo formato.

Esta capacidad de forzar a los modelos de lenguaje a un formato rígido es muy relevante y te ahorrará incontables horas de prompt engineering o depender de otras herramientas open source.

Primeros pasos con Structured Outputs de OpenAI

En esta sección, desglosaremos Structured Outputs con el ejemplo del analizador de sentimiento.

Configura tu entorno

Requisitos previos

Antes de empezar, asegúrate de tener lo siguiente:

  • Python 3.7 o superior instalado en tu equipo.
  • Una clave de la API de OpenAI. Puedes obtenerla registrándote en la web de OpenAI.

Configurar la API de OpenAI

1. Instala el paquete de OpenAI para Python: Abre tu terminal y ejecuta el siguiente comando para instalar o actualizar el paquete de OpenAI a la última versión:

$ pip install -U openai

2. Configura tu clave de API: Puedes establecer tu clave como variable de entorno o directamente en tu código. Para definirla como variable de entorno, ejecuta:

$ export OPENAI_API_KEY='your-api-key'

3. Verifica la instalación: Crea un script sencillo en Python para comprobar la instalación:

from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
   model="gpt-4o-mini",
   messages=[
       {"role": "system", "content": "You are a helpful assistant."},
       {"role": "user", "content": "Say hello!"}
   ],
   max_tokens=5
)
>>> print(response.choices[0].message.content.strip())
Hello! How can I

Ejecuta el script para confirmar que todo está correctamente configurado. Deberías ver la respuesta del modelo impresa en la terminal.

Además del paquete de OpenAI, necesitarás la librería Pydantic para definir y validar esquemas JSON para Structured Outputs. Instálala con pip:

$ pip install pydantic

Con estos pasos, tu entorno queda listo para usar la función Structured Outputs de OpenAI.

Definir un esquema de salida con Pydantic

Para usar Structured Outputs, debes definir la estructura de salida esperada con modelos de Pydantic. Pydantic es una librería de validación de datos y gestión de configuraciones para Python que te permite definir modelos de datos con anotaciones de tipo. Estos modelos se pueden usar para imponer la estructura de las salidas generadas por los modelos de OpenAI.

Aquí tienes un modelo Pydantic de ejemplo para especificar el formato de nuestro clasificador de sentimiento de reseñas:

from pydantic import BaseModel
from typing import Literal
class SentimentResponse(BaseModel):
   sentiment: Literal["positive", "negative", "neutral"]

En este ejemplo:

  • SentimentResponse es un modelo de Pydantic que define la estructura esperada de la salida.
  • El modelo tiene un único campo sentiment, que solo puede tomar uno de estos tres valores literales: "positive", "negative" o "neutral".

Cuando pasemos este modelo como parte de nuestras peticiones a la API de OpenAI, las salidas serán únicamente una de las palabras indicadas.

Veamos cómo.

Usar el helper parse

Para aplicar nuestro esquema de Pydantic en las peticiones a OpenAI, basta con pasarlo al parámetro response_format de la API de chat completions. A grandes rasgos, sería así:

response = client.beta.chat.completions.parse(
   model=MODEL,
   messages=[...],
   response_format=SentimentResponse
)

Fíjate en que, en lugar de usar client.chat.completions.create, utilizamos el método client.beta.chat.completions.parse. .parse() es un método nuevo en la API de Chat Completions pensado específicamente para Structured Outputs.

Ahora, juntemos todo reescribiendo el clasificador de sentimiento de reseñas con Structured Outputs. Primero, hacemos los imports necesarios, definimos el modelo de Pydantic, el prompt del sistema y una plantilla de prompt:

from openai import OpenAI
from pydantic import BaseModel
from typing import Literal
class SentimentResponse(BaseModel):
   sentiment: Literal["positive", "negative", "neutral"]
client = OpenAI()
MODEL = "gpt-4o-mini"
SYSTEM_PROMPT = "You are a sentiment classifier assistant."
PROMPT_TEMPLATE = """
   Classify the sentiment of the following hotel review as positive, negative, or neutral:\n\n{review}
"""

Después, escribimos una nueva función que usa el helper .parse():

# Function to classify sentiment using OpenAI's chat completions API with structured outputs
def classify_sentiment_with_structured_outputs(review):
   response = client.beta.chat.completions.parse(
       model=MODEL,
       messages=[
           {"role": "system", "content": SYSTEM_PROMPT},
           {"role": "user", "content": PROMPT_TEMPLATE.format(review=review)},
       ],
       response_format=SentimentResponse
   )
   return response.choices[0].message

La línea clave de la función es response_format=SentimentResponse, que es lo que habilita realmente Structured Outputs.

Probémoslo con una de las reseñas:

# List of hotel reviews
reviews = [
   "The room was clean and the staff was friendly.",
   "The location was terrible and the service was slow.",
   "The food was amazing but the room was too small.",
]
result = classify_sentiment_with_structured_outputs(reviews[0])
>>> print(result.content)
{"sentiment":"positive"}

Aquí, result es un objeto de mensaje:

>>> type(result)
openai.types.chat.parsed_chat_completion.ParsedChatCompletionMessage[SentimentResponse]

Además de su atributo .content, que recupera la respuesta, tiene un atributo .parsed que devuelve la información parseada como una clase:

>>> result.parsed
SentimentResponse(sentiment='positive')

Como ves, obtenemos una instancia de la clase SentimentResponse. Esto significa que podemos acceder al sentimiento como una cadena, en lugar de un diccionario, a través del atributo .sentiment:

>>> result.parsed.sentiment
'positive'

Anidar modelos de Pydantic para definir esquemas complejos

En algunos casos, puede que necesites definir estructuras de salida más complejas que impliquen datos anidados. Pydantic permite anidar modelos entre sí, lo que te habilita a crear esquemas detallados para distintos casos de uso. Es especialmente útil con datos jerárquicos o cuando debes imponer una estructura específica para salidas complejas.

Veamos un ejemplo en el que necesitamos extraer información detallada de una persona, incluyendo su nombre, datos de contacto y una lista de direcciones. Cada dirección debe incluir calle, ciudad, estado y código postal. Para construir el esquema correcto, necesitamos más de un modelo de Pydantic.

Paso 1: define los modelos de Pydantic

Primero, definimos los modelos de Pydantic para la dirección y la información del usuario:

from pydantic import BaseModel
from typing import List
# Define the Pydantic model for an address
class Address(BaseModel):
   street: str
   city: str
   state: str
   zip_code: str
# Define the Pydantic model for user information
class UserInfo(BaseModel):
   name: str
   email: str
   phone: str
   addresses: List[Address]

En este ejemplo:

  • Address es un modelo de Pydantic que define la estructura de una dirección.
  • UserInfo es un modelo de Pydantic que incluye una lista de objetos Address, junto con los campos de nombre, email y teléfono del usuario.

Paso 2: usa los modelos anidados en llamadas a la API

A continuación, usamos estos modelos anidados de Pydantic para imponer la estructura de salida en una llamada a la API de OpenAI:

SYSTEM_PROMPT = "You are a user information extraction assistant."
PROMPT_TEMPLATE = """ Extract the user information from the following text:\n\n{text}"""
# Function to extract user information using OpenAI's chat completions API with structured outputs
def extract_user_info(text):
   response = client.beta.chat.completions.parse(
       model=MODEL,
       messages=[
           {"role": "system", "content": SYSTEM_PROMPT},
           {"role": "user", "content": PROMPT_TEMPLATE.format(text=text)},
       ],
       response_format=UserInfo
   )
   return response.choices[0].message
# Example text containing user information
text = """John DoeEmail: john.doe@example.comPhone: 123-456-7890Addresses:- 123 Main St, Springfield, IL, 62701- 456 Elm St, Shelbyville, IL, 62702"""
# Extract user information and print the results
user_info = extract_user_info(text)

El texto de ejemplo es totalmente ilegible y carece de espacios entre partes clave de la información. Veamos si el modelo acierta. Usaremos la librería json para embellecer la respuesta:

import json
data = json.loads(user_info.content)
pretty_response = json.dumps(data, indent=2)
print(pretty_response)
{
 "name": "John Doe",
 "email": "john.doe@example.com",
 "phone": "123-456-7890",
 "addresses": [
   {
     "street": "123 Main St",
     "city": "Springfield",
     "state": "IL",
     "zip_code": "62701"
   },
   {
     "street": "456 Elm St",
     "city": "Shelbyville",
     "state": "IL",
     "zip_code": "62702"
   }
 ]
}

Como puedes ver, el modelo capturó correctamente la información de una persona y sus dos direcciones por separado según el esquema indicado.

En resumen, al anidar modelos de Pydantic puedes definir esquemas complejos que gestionen datos jerárquicos e impongan estructuras específicas para salidas intrincadas.

Function calling con Structured Outputs

Una de las funciones más extendidas en los modelos de lenguaje recientes es function calling (también llamada tool calling). Esta capacidad te permite conectar los modelos de lenguaje con funciones definidas por el usuario, dándoles acceso al mundo exterior.

Algunos ejemplos comunes son:

  • Recuperar datos en tiempo real (p. ej., tiempo, cotizaciones, resultados deportivos)
  • Realizar cálculos o análisis de datos
  • Consultar bases de datos o APIs
  • Generar imágenes u otros medios
  • Traducir texto entre idiomas
  • Controlar dispositivos inteligentes o sistemas IoT
  • Ejecutar lógica de negocio o flujos personalizados

No entraremos aquí en detalle sobre cómo funciona function calling, pero puedes leer nuestro tutorial de OpenAI Function Calling.

Lo importante es que, con Structured Outputs, usar function calling con los modelos de OpenAI se vuelve mucho más sencillo. Antes, las funciones que pasabas a los modelos de OpenAI requerían escribir esquemas JSON complejos, detallando cada parámetro con tipos. Aquí tienes un ejemplo:

{
   "type": "function",
   "function": {
       "name": "get_current_weather",
       "description": "Get the current weather",
       "parameters": {
           "type": "object",
           "properties": {
               "location": {
                   "type": "string",
                   "description": "The city and state, e.g. San Francisco, CA",
               },
               "format": {
                   "type": "string",
                   "enum": ["celsius", "fahrenheit"],
                   "description": "The temperature unit to use. Infer this from the users location.",
               },
           },
           "required": ["location", "format"],
       },
   }
}

Aunque la función get_current_weather tiene dos parámetros, su esquema JSON se vuelve enorme y propenso a errores si lo escribes a mano.

Structured Outputs lo resuelve volviendo a usar modelos de Pydantic:

from pydantic import BaseModel
from typing import Literal
def get_weather(location: str, unit: str, condition: str):
   # Implementation details...
   pass
class WeatherData(BaseModel):
   location: str
   unit: Literal["celsius", "fahrenheit"]
   condition: Literal["sunny", "cloudy", "rainy", "snowy"]

Primero escribes la función y su lógica. Después, la defines de nuevo con un modelo de Pydantic que especifica los parámetros de entrada esperados.

Luego, para convertir el modelo de Pydantic en un esquema JSON compatible, llamas a pydantic_function_tool:

>>> import openai
>>> openai.pydantic_function_tool(WeatherData)
{'type': 'function',
'function': {'name': 'WeatherData',
 'strict': True,
 'parameters': {'properties': {'location': {'title': 'Location',
    'type': 'string'},
   'unit': {'enum': ['celsius', 'fahrenheit'],
    'title': 'Unit',
    'type': 'string'},
   'condition': {'enum': ['sunny', 'cloudy', 'rainy', 'snowy'],
    'title': 'Condition',
    'type': 'string'}},
  'required': ['location', 'unit', 'condition'],
  'title': 'WeatherData',
  'type': 'object',
  'additionalProperties': False}}}

Así es como se usa esta herramienta como parte de una petición:

import openai
client = OpenAI()
tools = [openai.pydantic_function_tool(WeatherData)]
messages = [
   {
       "role": "system",
       "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user.",
   },
   {
       "role": "user",
       "content": "What is the weather in Tokyo?",
   }
]
response = client.chat.completions.create(
   model=MODEL, messages=messages, tools=tools
)
tool_call = response.choices[0].message.tool_calls[0]
>>> tool_call
ChatCompletionMessageToolCall(id='call_QnZZ0DmNN2cxw3bN433JQNIC', function=Function(arguments='{"location":"Tokyo","unit":"celsius","condition":"sunny"}', name='WeatherData'), type='function')

Pasamos el modelo de Pydantic en un formato JSON compatible al parámetro tools de la API de Chat Completions. Luego, según la consulta, el modelo decide si llama a la herramienta o no.

Como nuestra consulta en el ejemplo es «What is the weather in Tokyo?», vemos una llamada en tool_calls del objeto de mensaje devuelto.

Recuerda: el modelo no llama a la función get_weather, sino que genera los argumentos para ella basándose en el esquema de Pydantic que le proporcionamos:

arguments = json.loads(tool_call.function.arguments)
>>> arguments
{'location': 'Tokyo', 'unit': 'celsius', 'condition': 'sunny'}

Depende de nosotros llamar a la función con los argumentos generados:

some_result = get_weather(**arguments)

Si quieres que el modelo genere los argumentos y llame a la función a la vez, lo que buscas es un agente de IA. 

Tenemos un tutorial de LangChain Agents específico si te interesa.

Buenas prácticas al usar Structured Outputs de OpenAI

Al usar Structured Outputs, conviene tener presentes varias recomendaciones y buenas prácticas. A continuación, resumimos algunas:

  1. Usa modelos de Pydantic para definir los esquemas de salida; ofrecen una forma clara y tipada de definir las estructuras esperadas.
  2. Mantén los esquemas simples y específicos para obtener resultados más precisos.
  3. Usa tipos de datos adecuados (str, int, float, bool, List, Dict) para representar con precisión tus datos.
  4. Usa tipos Literal para enums y así definir valores permitidos específicos para los campos.
  5. Gestiona las negativas del modelo. Al usar el nuevo método .parse(), los objetos de mensaje tienen un nuevo atributo .refusal para indicar una negativa:
text = """John DoeEmail: john.doe@example.comPhone: 123-456-7890Addresses:- 123 Main St, Springfield, IL, 62701- 456 Elm St, Shelbyville, IL, 62702"""
user_info = extract_user_info(text)
if user_info.refusal:
   print(user_info.refusal)
else:
   print(user_info.content)

Salida:

{"name":"John Doe","email":"john.doe@example.com","phone":"123-456-7890","addresses":[{"street":"123 Main St","city":"Springfield","state":"IL","zip_code":"62701"},{"street":"456 Elm St","city":"Shelbyville","state":"IL","zip_code":"62702"}]}

6. Proporciona descripciones claras y concisas para cada campo de tus modelos de Pydantic para mejorar la precisión de las salidas del modelo:

from pydantic import BaseModel, Field
class Person(BaseModel):
   name: str = Field(..., description="The person's full name")
   age: int = Field(..., description="The person's age in years")
   occupation: str = Field(..., description="The person's current job or profession")

Estas prácticas te ayudarán a sacar el máximo partido de Structured Outputs en tus aplicaciones.

Conclusión

En este tutorial hemos visto cómo empezar con una nueva función de la API de OpenAI: Structured Outputs. Hemos comprobado cómo esta función obliga a los modelos de lenguaje a producir salidas en el formato que indiquemos. También hemos aprendido a usarla junto con function calling y hemos repasado algunas buenas prácticas para aprovecharla al máximo.

Aquí tienes algunos recursos relacionados para profundizar:

Obtén una certificación superior en IA

Demuestra que puedes utilizar la IA de forma eficaz y responsable.

Preguntas frecuentes sobre Structured Outputs

¿Cómo funcionan los modelos de Pydantic con Structured Outputs?

Los modelos de Pydantic se usan para definir el esquema de la estructura de salida deseada, que luego se pasa a la API de OpenAI para imponer el formato de la respuesta.

¿Se pueden usar Structured Outputs con function calling?

Sí, puedes usar Structured Outputs con function calling para simplificar la definición de parámetros de función y salidas esperadas.

¿Cuáles son los beneficios de usar Structured Outputs?

Entre los beneficios se incluyen formatos de respuesta consistentes, menor necesidad de posprocesado, mayor fiabilidad en aplicaciones de IA e integración más sencilla con sistemas existentes.

¿Hay alguna limitación al usar Structured Outputs?

Aunque son muy útiles, Structured Outputs pueden limitar la flexibilidad de las respuestas del modelo y requieren diseñar bien el esquema para equilibrar estructura y nivel de detalle deseado.


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

Soy creador de contenidos sobre ciencia de datos con más de 2 años de experiencia y uno de los mayores seguimientos en Medium. Me gusta escribir artículos detallados sobre IA y ML con un toque sarcástico, porque hay que darle algo de vidilla al tema. He publicado más de 130 artículos y un curso en DataCamp, y tengo otro en marcha. Mis contenidos han sido vistos por más de 5 millones de personas; 20.000 de ellas se convirtieron en seguidores tanto en Medium como en LinkedIn. 

Temas
Inteligencia Artificial
OpenAI

Los mejores cursos de OpenAI

Curso

Trabajar con la API de OpenAI

3 h
172.6K
Desarrolla aplicaciones basadas en IA con la API OpenAI. Conoce la funcionalidad que sustenta aplicaciones populares de IA como ChatGPT.
Ver detallesRight Arrow
Iniciar Curso
Ver másRight Arrow
Relacionado
An AI juggles tasks

blog

Cinco proyectos que puedes crear con modelos de IA generativa (con ejemplos)

Aprende a utilizar modelos de IA generativa para crear un editor de imágenes, un chatbot similar a ChatGPT con pocos recursos y una aplicación clasificadora de aprobación de préstamos y a automatizar interacciones PDF y un asistente de voz con GPT.
Abid Ali Awan's photo

Abid Ali Awan

10 min

Tutorial

Guía para principiantes de la API de OpenAI: Tutorial práctico y prácticas recomendadas

Este tutorial te presenta la API de OpenAI, sus casos de uso, un enfoque práctico para utilizar la API y todas las prácticas recomendadas que debes seguir.
Arunn Thevapalan's photo

Arunn Thevapalan

13 min

Tutorial

Tutorial de la API de OpenAI Assistants

Una visión completa de la API Assistants con nuestro artículo, que ofrece una mirada en profundidad a sus características, usos en la industria, guía de configuración y las mejores prácticas para maximizar su potencial en diversas aplicaciones empresariales.
Zoumana Keita 's photo

Zoumana Keita

14 min

Tutorial

Tutorial de llamada a funciones de OpenAI

Descubra cómo la nueva capacidad de llamada a funciones de OpenAI permite a los modelos GPT generar salidas JSON estructuradas, resolviendo problemas comunes de desarrollo causados por salidas irregulares.
Abid Ali Awan's photo

Abid Ali Awan

8 min

Tutorial

Primeros pasos con Claude 3 y la API de Claude 3

Conozca los modelos Claude 3, las pruebas de rendimiento detalladas y cómo acceder a ellas. Además, descubra la nueva API Python de Claude 3 para generar texto, acceder a funciones de visión y streaming.
Abid Ali Awan's photo

Abid Ali Awan

Tutorial

Cómo ejecutar Stable Diffusion:

Explora la IA generativa con nuestro tutorial introductorio sobre Stable Diffusion. Aprende a ejecutar el modelo de aprendizaje profundo en línea y localmente para generar imágenes detalladas.
Kurtis Pykes 's photo

Kurtis Pykes

7 min

Ver MásVer Más