For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegación principal

Resultados estructurados del modelo

Asegúrate de que las respuestas de texto del modelo se ajusten al esquema JSON que definas.

JSON es uno de los formatos más utilizados en el mundo para intercambiar datos entre aplicaciones.

Los resultados estructurados son una función que garantiza que el modelo siempre genere respuestas que se ajusten al JSON Schema que proporciones, por lo que no necesitas preocuparte de que el modelo omita una clave obligatoria o invente un valor de enumeración no válido.

Algunas ventajas de los resultados estructurados son:

  1. Seguridad de tipos confiable: no necesitas validar las respuestas con formato incorrecto ni volver a solicitarlas
  2. Rechazos explícitos: ahora puedes detectar mediante programación los rechazos del modelo por motivos de seguridad
  3. Diseño de prompts más sencillo: no necesitas prompts con instrucciones enfáticas para lograr un formato consistente

Además de admitir JSON Schema en la API REST, las bibliotecas de OpenAI para Python y JavaScript también permiten definir esquemas de objetos con pydantic.BaseModel y z.object, respectivamente. A continuación, puedes ver cómo extraer información de texto no estructurado para que se ajuste a un esquema definido en código.

El SDK de Ruby admite esquemas definidos con T::Struct de Sorbet y devuelve resultados analizados y tipados.

Obtener una respuesta estructurada
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()


class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract the event information."},
        {
            "role": "user",
            "content": "Alice and Bob are going to a science fair on Friday.",
        },
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed

Modelos compatibles

Los resultados estructurados están disponibles en nuestros modelos de lenguaje grandes más recientes, a partir de GPT-4o. Para proyectos nuevos, comienza con gpt-6-astra. Los modelos más antiguos, como gpt-4-turbo y los anteriores, pueden usar el modo JSON como alternativa.

Cuándo usar resultados estructurados mediante llamada a funciones o mediante text.format

Los resultados estructurados están disponibles de dos formas en la API de OpenAI:

  1. Al usar llamada a funciones
  2. Al usar un formato de respuesta json_schema

La llamada a funciones es útil cuando desarrollas una aplicación que conecta los modelos con las funcionalidades de tu aplicación.

Por ejemplo, puedes darle al modelo acceso a funciones que consulten una base de datos para crear un asistente de IA que ayude a los usuarios con sus pedidos, o a funciones que interactúen con la interfaz de usuario.

En cambio, los resultados estructurados mediante response_format son más adecuados cuando quieres indicar un esquema estructurado para que el modelo lo use al responder al usuario, en lugar de al llamar a una herramienta.

Por ejemplo, si desarrollas una aplicación de tutoría de matemáticas, quizá quieras que el asistente responda al usuario siguiendo un JSON Schema específico para poder generar una interfaz de usuario que muestre las distintas partes del resultado del modelo de diferentes maneras.

En la práctica:

  • Si conectas el modelo con herramientas, funciones, datos, etc. de tu sistema, debes usar la llamada a funciones - Si quieres estructurar el resultado del modelo cuando responde al usuario, debes usar text.format con un formato estructurado

El resto de esta guía se centrará en casos de uso sin llamada a funciones en la API Responses. Para obtener más información sobre cómo usar resultados estructurados con llamada a funciones, consulta

Llamada a funciones

, la guía correspondiente.

Resultados estructurados frente al modo JSON

Los resultados estructurados son la evolución del modo JSON. Aunque ambas opciones garantizan la generación de JSON válido, solo los resultados estructurados garantizan que se respete el esquema. Tanto los resultados estructurados como el modo JSON son compatibles con la API Responses, la API para completar chats, Assistants API, la API de ajuste fino y la API de procesamiento por lotes.

Recomendamos usar siempre resultados estructurados en lugar del modo JSON cuando sea posible.

Sin embargo, los resultados estructurados con response_format: {type: "json_schema", ...} solo son compatibles con las versiones de modelo gpt-4o-mini, gpt-4o-mini-2024-07-18 y gpt-4o-2024-08-06, y las posteriores.

Resultados estructuradosModo JSON
Genera JSON válido
Se ajusta al esquemaSí (consulta los esquemas compatibles)No
Modelos compatiblesgpt-4o-mini, gpt-4o-2024-08-06 y posterioresgpt-3.5-turbo, gpt-4-*, gpt-4o-* y los modelos GPT-5 compatibles
Activacióntext: { format: { type: "json_schema", "strict": true, "schema": ... } }text: { format: { type: "json_object" } }

Ejemplos

Cadena de pensamiento

Puedes pedirle al modelo que genere una respuesta estructurada, paso a paso, para guiar al usuario a través de la solución.

Resultados estructurados para tutorías de matemáticas con cadena de pensamiento
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()


class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {
            "role": "system",
            "content": "You are a helpful math tutor. Guide the user through the solution step by step.",
        },
        {"role": "user", "content": "how can I solve 8x + 7 = -23"},
    ],
    text_format=MathReasoning,
)

math_reasoning = response.output_parsed

Ejemplo de respuesta

{
  "steps": [
    {
      "explanation": "Start with the equation 8x + 7 = -23.",
      "output": "8x + 7 = -23"
    },
    {
      "explanation": "Subtract 7 from both sides to isolate the term with the variable.",
      "output": "8x = -23 - 7"
    },
    {
      "explanation": "Simplify the right side of the equation.",
      "output": "8x = -30"
    },
    {
      "explanation": "Divide both sides by 8 to solve for x.",
      "output": "x = -30 / 8"
    },
    {
      "explanation": "Simplify the fraction.",
      "output": "x = -15 / 4"
    }
  ],
  "final_answer": "x = -15 / 4"
}

Cómo usar resultados estructurados con text.format

Rechazos con resultados estructurados

Al usar resultados estructurados con entradas generadas por los usuarios, los modelos de OpenAI pueden negarse ocasionalmente a cumplir la solicitud por motivos de seguridad. Como un rechazo no necesariamente sigue el esquema que proporcionaste en response_format, la respuesta de la API incluirá un nuevo campo llamado refusal para indicar que el modelo se negó a cumplir la solicitud.

Cuando la propiedad refusal aparezca en el objeto de salida, puedes mostrar el rechazo en tu interfaz de usuario o incluir lógica condicional en el código que consume la respuesta para manejar los casos en que se rechace una solicitud.

class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {
            "role": "system",
            "content": "You are a helpful math tutor. Guide the user through the solution step by step.",
        },
        {"role": "user", "content": "how can I solve 8x + 7 = -23"},
    ],
    text_format=MathReasoning,
)

for output in response.output:
    if output.type != "message":
        continue

    for item in output.content:
        if item.type == "refusal":
            # If the model refuses to respond, you will get a refusal message
            print(item.refusal)
            continue

        if not item.parsed:
            raise Exception("Could not parse response")

        print(item.parsed)

La respuesta de la API en caso de rechazo tendrá un aspecto similar a este:

{
  "id": "resp_1234567890",
  "object": "response",
  "created_at": 1721596428,
  "status": "completed",
  "completed_at": 1721596429,
  "error": null,
  "incomplete_details": null,
  "input": [],
  "instructions": null,
  "max_output_tokens": null,
  "model": "gpt-4o-2024-08-06",
  "output": [{
    "id": "msg_1234567890",
    "type": "message",
    "role": "assistant",
    "content": [
      {
        "type": "refusal",
        "refusal": "I'm sorry, I cannot assist with that request."
      }
    ]
  }],
  "usage": {
    "input_tokens": 81,
    "output_tokens": 11,
    "total_tokens": 92,
    "output_tokens_details": {
      "reasoning_tokens": 0,
    }
  },
}

Consejos y prácticas recomendadas

Manejo de entradas generadas por los usuarios

Si tu aplicación usa entradas generadas por los usuarios, asegúrate de que tu prompt incluya instrucciones sobre cómo manejar las situaciones en las que la entrada no pueda dar lugar a una respuesta válida.

El modelo siempre intentará seguir el esquema proporcionado, lo que puede dar lugar a alucinaciones si la entrada no tiene ninguna relación con el esquema.

Podrías especificar en tu prompt que quieres que se devuelvan parámetros vacíos o una frase específica si el modelo detecta que la entrada es incompatible con la tarea.

Manejo de errores

Los resultados estructurados aún pueden contener errores. Si detectas errores, intenta ajustar tus instrucciones, proporcionar ejemplos en las instrucciones del sistema o dividir las tareas en subtareas más simples. Consulta la guía de ingeniería de prompts para obtener más orientación sobre cómo ajustar tus entradas.

Evita divergencias en el esquema JSON

Para evitar que tu JSON Schema y los tipos correspondientes en tu lenguaje de programación diverjan, te recomendamos encarecidamente usar las funciones auxiliares nativas del SDK para esquemas cuando estén disponibles.

Si prefieres especificar el esquema JSON directamente, podrías agregar reglas de CI que detecten cambios en el esquema JSON o en los objetos de datos subyacentes, o agregar un paso de CI que genere automáticamente el JSON Schema a partir de las definiciones de tipos (o viceversa).

Streaming

Puedes usar streaming para procesar las respuestas del modelo o los argumentos de las llamadas a funciones a medida que se generan y analizarlos como datos estructurados.

Así, no tienes que esperar a que se complete toda la respuesta para procesarla. Esto es especialmente útil si quieres mostrar los campos JSON uno por uno o procesar los argumentos de las llamadas a funciones en cuanto estén disponibles.

Recomendamos usar los SDK para gestionar el streaming con resultados estructurados.

from openai import OpenAI
from pydantic import BaseModel


class EntitiesModel(BaseModel):
    attributes: list[str]
    colors: list[str]
    animals: list[str]


client = OpenAI()

with client.responses.stream(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract entities from the input text"},
        {
            "role": "user",
            "content": "The quick brown fox jumps over the lazy dog with piercing blue eyes",
        },
    ],
    text_format=EntitiesModel,
) as stream:
    for event in stream:
        if event.type == "response.refusal.delta":
            print(event.delta, end="")
        elif event.type == "response.output_text.delta":
            print(event.delta, end="")
        elif event.type == "response.error":
            print(event.error, end="")
        elif event.type == "response.completed":
            print("Completed")  # print(event.response.output)

    final_response = stream.get_final_response()
    print(final_response)

Esquemas compatibles

Los resultados estructurados admiten un subconjunto del lenguaje JSON Schema.

Tipos admitidos

Los resultados estructurados admiten los siguientes tipos:

  • Cadena
  • Número
  • Booleano
  • Entero
  • Objeto
  • Arreglo
  • Enum
  • anyOf

Propiedades admitidas

Además de especificar el tipo de una propiedad, puedes establecer algunas restricciones adicionales:

Propiedades admitidas para string:

  • pattern — Una expresión regular con la que debe coincidir la cadena.
  • format — Formatos predefinidos para cadenas. Actualmente se admiten los siguientes:
    • date-time
    • time
    • date
    • duration
    • email
    • hostname
    • ipv4
    • ipv6
    • uuid

Propiedades admitidas para number:

  • multipleOf — El número debe ser múltiplo de este valor.
  • maximum — El número debe ser menor o igual que este valor.
  • exclusiveMaximum — El número debe ser menor que este valor.
  • minimum — El número debe ser mayor o igual que este valor.
  • exclusiveMinimum — El número debe ser mayor que este valor.

Propiedades admitidas para array:

  • minItems — El arreglo debe tener al menos esta cantidad de elementos.
  • maxItems — El arreglo debe tener como máximo esta cantidad de elementos.

Estos son algunos ejemplos de cómo puedes usar estas restricciones de tipo:

{
    "name": "user_data",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
                "description": "The name of the user"
            },
            "username": {
                "type": "string",
                "description": "The username of the user. Must start with @",
                "pattern": "^@[a-zA-Z0-9_]+$"
            },
            "email": {
                "type": "string",
                "description": "The email of the user",
                "format": "email"
            }
        },
        "additionalProperties": false,
        "required": [
            "name", "username", "email"
        ]
    }
}

Ten en cuenta que estas restricciones aún no se admiten en modelos con ajuste fino.

El nivel raíz debe ser un objeto y no debe usar anyOf

Ten en cuenta que el nivel raíz de un esquema debe ser un objeto y no debe usar anyOf. Un patrón que aparece en Zod, por ejemplo, es el uso de una unión discriminada, que genera un anyOf en el nivel superior. Por lo tanto, un código como el siguiente no funcionará:

import { z } from "zod";
import { zodResponseFormat } from "openai/helpers/zod";

const BaseResponseSchema = z.object({
  /* ... */
});
const UnsuccessfulResponseSchema = z.object({
  /* ... */
});

const finalSchema = z.discriminatedUnion("status", [
  BaseResponseSchema,
  UnsuccessfulResponseSchema,
]);

// Invalid JSON Schema for Structured Outputs
const json = zodResponseFormat(finalSchema, "final_schema");

Todos los campos deben especificarse como required

Para usar resultados estructurados, todos los campos o parámetros de función deben especificarse como required.

{
    "name": "get_weather",
    "description": "Fetches the weather in the given location",
    "strict": true,
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The location to get the weather for"
            },
            "unit": {
                "type": "string",
                "description": "The unit to return the temperature in",
                "enum": ["F", "C"]
            }
        },
        "additionalProperties": false,
        "required": ["location", "unit"]
    }
}

Aunque todos los campos deben ser obligatorios (y el modelo devolverá un valor para cada parámetro), es posible emular un parámetro opcional mediante un tipo de unión con null.

{
    "name": "get_weather",
    "description": "Fetches the weather in the given location",
    "strict": true,
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The location to get the weather for"
            },
            "unit": {
                "type": ["string", "null"],
                "description": "The unit to return the temperature in",
                "enum": ["F", "C"]
            }
        },
        "additionalProperties": false,
        "required": [
            "location", "unit"
        ]
    }
}

Los objetos tienen límites de profundidad de anidamiento y tamaño

Un esquema puede tener hasta 5000 propiedades de objetos en total, con hasta 10 niveles de anidamiento.

Límites de longitud total de las cadenas

En un esquema, la longitud total de las cadenas de todos los nombres de propiedades, nombres de definiciones, valores de enum y valores de const no puede superar los 120 000 caracteres.

Límites de tamaño de enum

Un esquema puede tener hasta 1000 valores de enum en total entre todas las propiedades enum.

Para una sola propiedad enum con valores de cadena, la longitud total de las cadenas de todos los valores de enum no puede superar los 15 000 caracteres cuando hay más de 250 valores de enum.

Siempre se debe establecer additionalProperties: false en los objetos

additionalProperties controla si se permite que un objeto contenga claves o valores adicionales que no se hayan definido en el esquema JSON Schema.

Los resultados estructurados solo permiten generar las claves y los valores especificados, por lo que exigimos que los desarrolladores establezcan additionalProperties: false para activar los resultados estructurados.

{
    "name": "get_weather",
    "description": "Fetches the weather in the given location",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The location to get the weather for"
            },
            "unit": {
                "type": "string",
                "description": "The unit to return the temperature in",
                "enum": ["F", "C"]
            }
        },
        "additionalProperties": false,
        "required": [
            "location", "unit"
        ]
    }
}

Orden de las claves

Al usar resultados estructurados, los resultados se generarán en el mismo orden que las claves del esquema.

Algunas palabras clave específicas de cada tipo aún no se admiten

  • Composición: allOf, not, dependentRequired, dependentSchemas, if, then, else

En los modelos con ajuste fino, tampoco se admite lo siguiente:

  • Para cadenas: minLength, maxLength, pattern, format
  • Para números: minimum, maximum, multipleOf
  • Para objetos: patternProperties
  • Para arreglos: minItems, maxItems

Si activas los resultados estructurados al proporcionar strict: true y llamas a la API con un esquema JSON Schema no compatible, recibirás un error.

En anyOf, cada esquema anidado debe ser un esquema JSON Schema válido según este subconjunto

Este es un ejemplo de un esquema anyOf compatible:

{
    "type": "object",
    "properties": {
        "item": {
            "anyOf": [
                {
                    "type": "object",
                    "description": "The user object to insert into the database",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name of the user"
                        },
                        "age": {
                            "type": "number",
                            "description": "The age of the user"
                        }
                    },
                    "additionalProperties": false,
                    "required": [
                        "name",
                        "age"
                    ]
                },
                {
                    "type": "object",
                    "description": "The address object to insert into the database",
                    "properties": {
                        "number": {
                            "type": "string",
                            "description": "The number of the address. Eg. for 123 main st, this would be 123"
                        },
                        "street": {
                            "type": "string",
                            "description": "The street name. Eg. for 123 main st, this would be main st"
                        },
                        "city": {
                            "type": "string",
                            "description": "The city of the address"
                        }
                    },
                    "additionalProperties": false,
                    "required": [
                        "number",
                        "street",
                        "city"
                    ]
                }
            ]
        }
    },
    "additionalProperties": false,
    "required": [
        "item"
    ]
}

Se admiten definiciones

Puedes usar definiciones para definir subesquemas a los que se hace referencia en distintas partes de tu esquema. A continuación se muestra un ejemplo sencillo.

{
    "type": "object",
    "properties": {
        "steps": {
            "type": "array",
            "items": {
                "$ref": "#/$defs/step"
            }
        },
        "final_answer": {
            "type": "string"
        }
    },
    "$defs": {
        "step": {
            "type": "object",
            "properties": {
                "explanation": {
                    "type": "string"
                },
                "output": {
                    "type": "string"
                }
            },
            "required": [
                "explanation",
                "output"
            ],
            "additionalProperties": false
        }
    },
    "required": [
        "steps",
        "final_answer"
    ],
    "additionalProperties": false
}

Se admiten esquemas recursivos

Ejemplo de un esquema recursivo que usa # para indicar recursión hacia la raíz.

{
    "name": "ui",
    "description": "Dynamically generated UI",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "type": {
                "type": "string",
                "description": "The type of the UI component",
                "enum": ["div", "button", "header", "section", "field", "form"]
            },
            "label": {
                "type": "string",
                "description": "The label of the UI component, used for buttons or form fields"
            },
            "children": {
                "type": "array",
                "description": "Nested UI components",
                "items": {
                    "$ref": "#"
                }
            },
            "attributes": {
                "type": "array",
                "description": "Arbitrary attributes for the UI component, suitable for any element",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name of the attribute, for example onClick or className"
                        },
                        "value": {
                            "type": "string",
                            "description": "The value of the attribute"
                        }
                    },
                    "additionalProperties": false,
                    "required": ["name", "value"]
                }
            }
        },
        "required": ["type", "label", "children", "attributes"],
        "additionalProperties": false
    }
}

Ejemplo de un esquema recursivo que usa recursión explícita:

{
    "type": "object",
    "properties": {
        "linked_list": {
            "$ref": "#/$defs/linked_list_node"
        }
    },
    "$defs": {
        "linked_list_node": {
            "type": "object",
            "properties": {
                "value": {
                    "type": "number"
                },
                "next": {
                    "anyOf": [
                        {
                            "$ref": "#/$defs/linked_list_node"
                        },
                        {
                            "type": "null"
                        }
                    ]
                }
            },
            "additionalProperties": false,
            "required": [
                "next",
                "value"
            ]
        }
    },
    "additionalProperties": false,
    "required": [
        "linked_list"
    ]
}

Modo JSON

El modo JSON es una versión más básica de la función de resultados estructurados. Mientras que el modo JSON garantiza que el resultado del modelo sea JSON válido, los resultados estructurados garantizan de forma confiable que el resultado del modelo siga el esquema que especifiques. Te recomendamos usar resultados estructurados si son compatibles con tu caso de uso.

Cuando el modo JSON está activado, se garantiza que el resultado del modelo sea JSON válido, excepto en algunos casos límite que debes detectar y manejar adecuadamente.

Para activar el modo JSON con la API Responses, puedes establecer text.format en { "type": "json_object" }. Si usas llamadas a funciones, el modo JSON siempre está activado.

Notas importantes:

  • Al usar el modo JSON, siempre debes indicarle al modelo que genere JSON mediante algún mensaje de la conversación, por ejemplo, el mensaje del sistema. Si no incluyes una instrucción explícita para generar JSON, el modelo puede generar un flujo interminable de espacios en blanco y la solicitud puede continuar ejecutándose hasta alcanzar el límite de tokens. Para ayudarte a no olvidarlo, la API generará un error si la cadena “JSON” no aparece en algún lugar del contexto.
  • El modo JSON no garantiza que el resultado siga un esquema específico, solo que sea válido y se pueda analizar sin errores. Debes usar resultados estructurados para garantizar que siga tu esquema o, si eso no es posible, usar una biblioteca de validación y, posiblemente, reintentos para garantizar que el resultado siga el esquema deseado.
  • Tu aplicación debe detectar y manejar los casos límite que pueden hacer que el resultado del modelo no sea un objeto JSON completo (ver más abajo)

Recursos

Para obtener más información sobre los resultados estructurados, te recomendamos consultar los siguientes recursos: