Salta al contingut
Sortides estructurades

Sortides estructurades

Demana la resposta com a JSON: qualsevol objecte JSON, o JSON que segueixi un esquema que envies. Fes-ho servir quan és un programa, no una persona, qui llegeix la resposta.

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 és el text de la resposta, a 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}
  }
}

Els modes de response_format

response_format és un camp de /v1/chat/completions. Sense ell, la resposta és text normal.

Mode Valor Què obtens
Objecte JSON {"type": "json_object"} La resposta és JSON sense una forma prescrita. Digues al prompt quines claus vols.
Esquema JSON {"type": "json_schema", "json_schema": {"name": "…", "schema": {…}}} La resposta segueix l'esquema que envies: les seves claus, tipus i camps obligatoris.
Estricte "strict": true Un camp de json_schema, no un tercer mode. És true tret que enviïs false.
Camp Tipus Per defecte Descripció S'aplica a
type string json_object o json_schema. Obligatori dins de response_format. text s'accepta i dona una resposta de text normal. Tots els models que accepten el mode
json_schema.schema object El JSON Schema que segueix la resposta. Envia'l sempre que type sigui json_schema. Tots els models que accepten el mode
json_schema.name string Una etiqueta per a l'esquema, com al format d'OpenAI. Models open-weight hostejats
json_schema.strict boolean true Si la resposta ha de seguir l'esquema exactament. Els models Shannon apliquen tots els esquemes com a estrictes. Models open-weight hostejats

El mode d'objecte JSON no necessita esquema. Anomena les claus al prompt:

Cos de la sol·licitud
{
  "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"}
}

Suport per model

Model json_object json_schema
Tots els models Shannon (shannon-*) Sí Sí
Models open-weight hostejats, excepte els que s'anomenen a sota Sí Sí
Laguna-S-2.1-W4A16-AUTOROUND-REAP — —
inkling-W4A16-AUTOROUND-REAP Sí —

GET /v1/models informa el mateix per id: capabilities.json_schema a tots els models, i capabilities.response_format als models open-weight hostejats. Models i preus

Paraules clau d'esquema admeses

El que un model rep del teu esquema depèn del model i de l'endpoint:

Models /v1/chat/completions /v1/responses
Models Shannon El subconjunt de paraules clau de sota, aplicat com a estricte. L'esquema tal com l'envies, aplicat com a estricte.
Models open-weight hostejats L'esquema tal com l'envies. strict és true si no l'envies. L'esquema tal com l'envies. strict és true si no l'envies.

El subconjunt de paraules clau dels models Shannon a /v1/chat/completions:

Paraula clau Com es llegeix
Paraules clau que es llegeixen type, format, title, description, nullable, enum, items, properties, required, minItems, maxItems, minimum, maximum, minLength, maxLength, pattern, anyOf, default
$ref Es resol a partir de $defs o definitions al nivell superior de l'esquema. Les paraules clau escrites al costat de $ref, com description, es conserven. Una referència sense cap definició corresponent es llegeix com a {"type": "object"}.
allOf Els membres es fusionen en un sol esquema. Quan dos membres defineixen la mateixa paraula clau, compta l'últim.
oneOf Es llegeix com a anyOf.
Esquemes que es refereixen a si mateixos S'expandeixen fins a 24 nivells. Un nivell més profund es llegeix com a {"type": "object"}.
Fora del subconjunt Totes les altres paraules clau, per exemple additionalProperties, const, multipleOf, uniqueItems, exclusiveMinimum, exclusiveMaximum, patternProperties, prefixItems, minProperties, maxProperties, not i if / then / else. No formen part de l'esquema que rep el model.

A /v1/responses: text.format

/v1/responses escriu la mateixa sol·licitud com a text.format. L'esquema és directament a format, al costat de name i strict, no sota una clau json_schema. Per al mode d'objecte JSON envia {"format": {"type": "json_object"}}.

Cos de la sol·licitud
{
  "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 és el text de la part output_text de l'element 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": []
        }
      ]
    }
  ]
}

El suport per model i els errors són els mateixos que a /v1/chat/completions. API Responses

A /v1/messages: una eina com a esquema

El format Messages no té cap camp de sortida estructurada. Per obtenir JSON dels mateixos models, crida /v1/chat/completions o /v1/responses amb la mateixa clau.

Per quedar-te a /v1/messages, descriu l'objecte com una eina: posa el teu esquema a input_schema i anomena l'eina a tool_choice. L'input del bloc tool_use és llavors el teu objecte, ja analitzat. Un tool_choice amb nom l'apliquen els models open-weight hostejats.

Cos de la sol·licitud
{
  "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"}
    }
  ]
}

Comprova que stop_reason sigui tool_use abans de llegir el bloc. Crida de funcions

Com arriba el JSON

  • El JSON és una cadena. La resposta no porta cap objecte analitzat al costat: executa el teu analitzador JSON sobre message.content.
  • Amb stream: true, el JSON arriba en troços de delta.content. Uneix-los i analitza'ls quan el stream hagi acabat; un troç per si sol no és JSON vàlid.
  • Un model open-weight hostejat respon una sol·licitud amb response_format sense text de raonament: reasoning_content és null.

Comprova el que reps

L'esquema guia com escriu el model. L'API no compara la resposta acabada amb el teu esquema, així que és el teu codi qui fa la comprovació final:

  • Analitza dins d'un gestor d'errors, com fa l'exemple de dalt de tot.
  • Valida l'objecte analitzat amb el mateix esquema al teu propi codi, per exemple amb una biblioteca de JSON Schema, Pydantic o Zod.
  • Una resposta llarga pot acabar abans que el JSON sigui complet. Una resposta sense streaming només informa finish_reason com a stop o tool_calls, així que un error d'anàlisi és el senyal. Torna a demanar-ho, o demana un objecte més petit.

Errors

Estat Tipus Missatge Quan
400 invalid_request_error <id> does not accept response_format El model no accepta cap dels dos modes.
400 invalid_request_error <id> does not accept response_format json_schema; use json_object El model accepta json_object i has enviat json_schema.
422 invalid_request_error Failed to deserialize the JSON body into the target type: … response_format no té type, o no és un objecte JSON.

Les dues respostes 400 s'envien abans que es descompti res del teu saldo. Gestió d’errors