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) import OpenAI from "openai";
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1" });
const response = await 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.
let person = null;
try {
person = JSON.parse(response.choices[0].message.content);
} catch {
// ask again, or report the failure
}
console.log(person); curl https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"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"]
}
}
}
}' El JSON és el text de la resposta, a choices[0].message.content:
{
"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:
{
"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"}}.
{
"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:
{
"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.
{
"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"}
} {
"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 dedelta.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_formatsense text de raonament:reasoning_contentésnull.
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_reasoncom astopotool_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