Saltar al contenido
Salidas estructuradas

Salidas estructuradas

Pide la respuesta como JSON: cualquier objeto JSON, o JSON que siga un esquema que envíes. Úsalo cuando un programa, y no una persona, lee la respuesta.

POST https://api.shannon-ai.com/v1/chat/completions

import json
from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1")

response = client.chat.completions.create(
    model="Kimi-K3-3BIT-REAP",
    messages=[
        {"role": "user", "content": "Extract the person: John Doe, 30 years old, engineer."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "occupation": {"type": "string"},
                },
                "required": ["name", "age", "occupation"],
            },
        },
    },
)

# The JSON arrives as text in message.content: parse it, then check it.
try:
    person = json.loads(response.choices[0].message.content)
except json.JSONDecodeError:
    person = None  # ask again, or report the failure

print(person)

El JSON es el texto de la respuesta, en choices[0].message.content:

200 JSON
{
  "id": "chatcmpl-0c4e7a1b9d2f4e6a8b3c5d7e9f1a2b3c",
  "object": "chat.completion",
  "created": 1791590400,
  "model": "Kimi-K3-3BIT-REAP",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"John Doe\",\"age\":30,\"occupation\":\"engineer\"}",
        "reasoning_content": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 96,
    "completion_tokens": 19,
    "total_tokens": 115,
    "prompt_tokens_details": {"cached_tokens": 0},
    "completion_tokens_details": {"reasoning_tokens": 0}
  }
}

Los modos de response_format

response_format es un campo de /v1/chat/completions. Sin él la respuesta es texto normal.

Modo Valor Qué obtienes
Objeto JSON {"type": "json_object"} La respuesta es JSON sin una forma prescrita. Indica en el prompt qué claves quieres.
Esquema JSON {"type": "json_schema", "json_schema": {"name": "…", "schema": {…}}} La respuesta sigue el esquema que envías: sus claves, tipos y campos obligatorios.
Estricto "strict": true Un campo de json_schema, no un tercer modo. Es true a menos que envíes false.
Campo Tipo Predeterminado Descripción Aplicado por
type string json_object o json_schema. Obligatorio dentro de response_format. text se acepta y da una respuesta de texto normal. Todo modelo que acepta el modo
json_schema.schema object El JSON Schema que sigue la respuesta. Envíalo siempre que type sea json_schema. Todo modelo que acepta el modo
json_schema.name string Una etiqueta para el esquema, como en el formato de OpenAI. Modelos de pesos abiertos alojados
json_schema.strict boolean true Si la respuesta debe seguir el esquema exactamente. Los modelos Shannon aplican todo esquema como estricto. Modelos de pesos abiertos alojados

El modo de objeto JSON no necesita esquema. Nombra las claves en el prompt:

Cuerpo de la solicitud
{
  "model": "shannon-3",
  "messages": [
    {
      "role": "user",
      "content": "Name the capital of France. Answer as JSON with the keys city and country."
    }
  ],
  "response_format": {"type": "json_object"}
}

Compatibilidad por modelo

Modelo json_object json_schema
Todos los modelos Shannon (shannon-*) Sí Sí
Modelos de pesos abiertos alojados, salvo los que se nombran abajo Sí Sí
Laguna-S-2.1-W4A16-AUTOROUND-REAP — —
inkling-W4A16-AUTOROUND-REAP Sí —

GET /v1/models informa lo mismo por id: capabilities.json_schema en todos los modelos, y capabilities.response_format en los modelos de pesos abiertos alojados. Modelos y precios

Palabras clave de esquema admitidas

Lo que un modelo recibe de tu esquema depende del modelo y del endpoint:

Modelos /v1/chat/completions /v1/responses
Modelos Shannon El subconjunto de palabras clave de abajo, aplicado como estricto. El esquema tal como lo envías, aplicado como estricto.
Modelos de pesos abiertos alojados El esquema tal como lo envías. strict es true a menos que lo envíes. El esquema tal como lo envías. strict es true a menos que lo envíes.

El subconjunto de palabras clave de los modelos Shannon en /v1/chat/completions:

Palabra clave Cómo se lee
Palabras clave que se leen type, format, title, description, nullable, enum, items, properties, required, minItems, maxItems, minimum, maximum, minLength, maxLength, pattern, anyOf, default
$ref Se resuelve desde $defs o definitions en el nivel superior del esquema. Las palabras clave escritas junto al $ref, como description, se conservan. Una referencia sin definición correspondiente se lee como {"type": "object"}.
allOf Los miembros se fusionan en un solo esquema. Donde dos miembros definen la misma palabra clave, cuenta la última.
oneOf Se lee como anyOf.
Esquemas que se refieren a sí mismos Se expanden hasta 24 niveles. Un nivel más profundo se lee como {"type": "object"}.
Fuera del subconjunto Cualquier otra palabra clave, por ejemplo additionalProperties, const, multipleOf, uniqueItems, exclusiveMinimum, exclusiveMaximum, patternProperties, prefixItems, minProperties, maxProperties, not e if / then / else. No forman parte del esquema que recibe el modelo.

En /v1/responses: text.format

/v1/responses escribe la misma solicitud como text.format. El esquema va directamente en format, junto a name y strict, no bajo una clave json_schema. Para el modo de objeto JSON envía {"format": {"type": "json_object"}}.

Cuerpo de la solicitud
{
  "model": "Kimi-K3-3BIT-REAP",
  "input": "Extract the person: John Doe, 30 years old, engineer.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "person",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "age": {"type": "integer"},
          "occupation": {"type": "string"}
        },
        "required": ["name", "age", "occupation"]
      }
    }
  }
}

El JSON es el text de la parte output_text del elemento message:

200 JSON
{
  "output": [
    {
      "id": "msg_7e1d3c5b9a2f4d6e8c0b1a3f5e7d9c2b",
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "{\"name\":\"John Doe\",\"age\":30,\"occupation\":\"engineer\"}",
          "annotations": []
        }
      ]
    }
  ]
}

La compatibilidad por modelo y los errores son los mismos que en /v1/chat/completions. API Responses

En /v1/messages: una herramienta como esquema

El formato Messages no tiene un campo de salida estructurada. Para obtener JSON de los mismos modelos, llama a /v1/chat/completions o /v1/responses con la misma clave.

Para quedarte en /v1/messages, describe el objeto como una herramienta: pon tu esquema en input_schema y nombra la herramienta en tool_choice. El input del bloque tool_use es entonces tu objeto, ya analizado. Los modelos de pesos abiertos alojados aplican un tool_choice con nombre.

Cuerpo de la solicitud
{
  "model": "Kimi-K3-3BIT-REAP",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "Extract the person: John Doe, 30 years old, engineer."
    }
  ],
  "tools": [
    {
      "name": "record_person",
      "description": "Record the extracted person.",
      "input_schema": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "age": {"type": "integer"},
          "occupation": {"type": "string"}
        },
        "required": ["name", "age", "occupation"]
      }
    }
  ],
  "tool_choice": {"type": "tool", "name": "record_person"}
}
200 JSON
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_4b8d2f6a1c3e4a5b9d7f0e2c4a6b8d1f",
      "name": "record_person",
      "input": {"name": "John Doe", "age": 30, "occupation": "engineer"}
    }
  ]
}

Comprueba que stop_reason sea tool_use antes de leer el bloque. Llamadas a funciones

Cómo llega el JSON

  • El JSON es una cadena. La respuesta no trae un objeto ya analizado a su lado: ejecuta tu analizador de JSON sobre message.content.
  • Con stream: true el JSON llega en fragmentos de delta.content. Únelos y analízalos una vez que el stream haya terminado; un fragmento por sí solo no es JSON válido.
  • Un modelo de pesos abiertos alojado responde una solicitud con response_format sin texto de razonamiento: reasoning_content es null.

Comprueba lo que recibes

El esquema guía cómo escribe el modelo. La API no compara la respuesta terminada con tu esquema, así que tu código hace la comprobación final:

  • Analiza dentro de un manejador de errores, como hace el ejemplo del principio.
  • Valida el objeto analizado con el mismo esquema en tu propio código, por ejemplo con una biblioteca de JSON Schema, Pydantic o Zod.
  • Una respuesta larga puede terminar antes de que el JSON esté completo. Una respuesta sin stream informa finish_reason solo como stop o tool_calls, así que un error de análisis es la señal. Pregunta de nuevo, o pide un objeto más pequeño.

Errores

Estado Tipo Mensaje Cuándo
400 invalid_request_error <id> does not accept response_format El modelo no acepta ninguno de los dos modos.
400 invalid_request_error <id> does not accept response_format json_schema; use json_object El modelo acepta json_object y enviaste json_schema.
422 invalid_request_error Failed to deserialize the JSON body into the target type: … response_format no tiene type, o no es un objeto JSON.

Las dos respuestas 400 se envían antes de descontar nada de tu saldo. Manejo de errores