เอาต์พุตแบบมีโครงสร้าง
ขอให้คำตอบเป็น JSON: ออบเจ็กต์ JSON ใดก็ได้ หรือ JSON ที่เป็นไปตาม schema ที่คุณส่ง ใช้เมื่อโปรแกรม ไม่ใช่คน เป็นผู้อ่านคำตอบ
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"]
}
}
}
}' JSON คือข้อความของคำตอบ ใน 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}
}
} โหมดของ response_format
response_format เป็นฟิลด์ของ /v1/chat/completions หากไม่มี คำตอบจะเป็นข้อความธรรมดา
| โหมด | ค่า | สิ่งที่คุณได้รับ |
|---|---|---|
| JSON object | {"type": "json_object"} | คำตอบเป็น JSON ที่ไม่กำหนดรูปร่าง ให้บอกใน prompt ว่าต้องการคีย์ใด |
| JSON schema | {"type": "json_schema", "json_schema": {"name": "…", "schema": {…}}} | คำตอบเป็นไปตาม schema ที่คุณส่ง: คีย์ ชนิด และฟิลด์ที่จำเป็น |
| Strict | "strict": true | ฟิลด์ของ json_schema ไม่ใช่โหมดที่สาม เป็น true เว้นแต่คุณส่ง false |
| ฟิลด์ | ประเภท | ค่าเริ่มต้น | คำอธิบาย | ใช้โดย |
|---|---|---|---|---|
type | string | json_object หรือ json_schema จำเป็นภายใน response_format รับ text ไว้ด้วย และให้คำตอบเป็นข้อความธรรมดา | ทุกโมเดลที่รองรับโหมดนี้ | |
json_schema.schema | object | JSON Schema ที่คำตอบต้องเป็นไปตาม ให้ส่งเมื่อ type เป็น json_schema | ทุกโมเดลที่รองรับโหมดนี้ | |
json_schema.name | string | ป้ายชื่อของ schema เหมือนในรูปแบบของ OpenAI | โมเดล open-weight ที่โฮสต์ | |
json_schema.strict | boolean | true | ระบุว่าคำตอบต้องเป็นไปตาม schema อย่างเคร่งครัดหรือไม่ โมเดล Shannon ใช้ทุก schema แบบ strict | โมเดล open-weight ที่โฮสต์ |
โหมด JSON object ไม่ต้องใช้ schema ให้ระบุชื่อคีย์ใน 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"}
} การรองรับแยกตามโมเดล
| โมเดล | json_object | json_schema |
|---|---|---|
ทุกโมเดล Shannon (shannon-*) | ใช่ | ใช่ |
| โมเดล open-weight ที่โฮสต์ ยกเว้นที่ระบุด้านล่าง | ใช่ | ใช่ |
Laguna-S-2.1-W4A16-AUTOROUND-REAP | — | — |
inkling-W4A16-AUTOROUND-REAP | ใช่ | — |
GET /v1/models รายงานแบบเดียวกันต่อ id: capabilities.json_schema บนทุกโมเดล และ capabilities.response_format บนโมเดล open-weight ที่โฮสต์ โมเดลและราคา
คีย์เวิร์ดของ schema ที่รองรับ
สิ่งที่โมเดลได้รับจาก schema ของคุณขึ้นกับโมเดลและ endpoint:
| โมเดล | /v1/chat/completions | /v1/responses |
|---|---|---|
| โมเดล Shannon | ชุดย่อยของคีย์เวิร์ดด้านล่าง ใช้แบบ strict | schema ตามที่คุณส่ง ใช้แบบ strict |
| โมเดล open-weight ที่โฮสต์ | schema ตามที่คุณส่ง strict เป็น true เว้นแต่คุณส่งค่า | schema ตามที่คุณส่ง strict เป็น true เว้นแต่คุณส่งค่า |
ชุดย่อยของคีย์เวิร์ดของโมเดล Shannon บน /v1/chat/completions:
| คีย์เวิร์ด | วิธีอ่าน |
|---|---|
| คีย์เวิร์ดที่อ่าน | type, format, title, description, nullable, enum, items, properties, required, minItems, maxItems, minimum, maximum, minLength, maxLength, pattern, anyOf, default |
$ref | ถูก resolve จาก $defs หรือ definitions ที่ระดับบนสุดของ schema คีย์เวิร์ดที่เขียนข้าง $ref เช่น description จะถูกเก็บไว้ การอ้างอิงที่ไม่มีนิยามตรงกันถูกอ่านเป็น {"type": "object"} |
allOf | สมาชิกจะถูกรวมเป็น schema เดียว เมื่อสมาชิกสองรายการตั้งคีย์เวิร์ดเดียวกัน รายการหลังจะมีผล |
oneOf | อ่านเป็น anyOf |
| schema ที่อ้างถึงตัวเอง | ขยายถึง 24 ระดับ ระดับที่ลึกกว่านั้นถูกอ่านเป็น {"type": "object"} |
| นอกชุดย่อย | คีย์เวิร์ดอื่นทุกตัว เช่น additionalProperties, const, multipleOf, uniqueItems, exclusiveMinimum, exclusiveMaximum, patternProperties, prefixItems, minProperties, maxProperties, not และ if / then / else ไม่รวมอยู่ใน schema ที่โมเดลได้รับ |
การใช้ text.format ขอเอาต์พุตแบบมีโครงสร้างบน /v1/responses
/v1/responses เขียนคำขอเดียวกันเป็น text.format schema อยู่ใน format โดยตรง ข้าง name และ strict ไม่ได้อยู่ใต้คีย์ json_schema สำหรับโหมด JSON object ให้ส่ง {"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"]
}
}
}
} JSON คือ text ของส่วน output_text ในรายการ message:
{
"output": [
{
"id": "msg_7e1d3c5b9a2f4d6e8c0b1a3f5e7d9c2b",
"type": "message",
"role": "assistant",
"status": "completed",
"content": [
{
"type": "output_text",
"text": "{\"name\":\"John Doe\",\"age\":30,\"occupation\":\"engineer\"}",
"annotations": []
}
]
}
]
} การรองรับแยกตามโมเดลและข้อผิดพลาดเหมือนกับบน /v1/chat/completions Responses API
การใช้ tool เป็น schema ของเอาต์พุตบน /v1/messages
รูปแบบ Messages ไม่มีฟิลด์เอาต์พุตแบบมีโครงสร้าง หากต้องการ JSON จากโมเดลเดียวกัน ให้เรียก /v1/chat/completions หรือ /v1/responses ด้วยคีย์เดียวกัน
หากต้องการใช้ /v1/messages ต่อ ให้อธิบายออบเจ็กต์เป็น tool: ใส่ schema ของคุณใน input_schema และระบุชื่อ tool ใน tool_choice จากนั้น input ของบล็อก tool_use คือออบเจ็กต์ของคุณที่ parse แล้ว โมเดล open-weight ที่โฮสต์ใช้ tool_choice ที่ระบุชื่อ
{
"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"}
}
]
} ตรวจว่า stop_reason เป็น tool_use ก่อนอ่านบล็อก การเรียกฟังก์ชัน
JSON มาถึงอย่างไร
- JSON เป็นสตริง คำตอบไม่มีออบเจ็กต์ที่ parse แล้วอยู่ข้างๆ ให้ใช้ JSON parser กับ
message.content - เมื่อใช้
stream: trueJSON จะมาถึงเป็นชิ้นในdelta.contentให้ต่อกันแล้ว parse เมื่อสตรีมจบ ชิ้นเดี่ยวไม่ใช่ JSON ที่ถูกต้อง - โมเดล open-weight ที่โฮสต์ตอบคำขอ
response_formatโดยไม่มีข้อความการให้เหตุผล:reasoning_contentเป็นnull
ตรวจสิ่งที่คุณได้รับ
schema ชี้นำวิธีที่โมเดลเขียน API ไม่ได้เปรียบเทียบคำตอบที่เสร็จแล้วกับ schema ของคุณ โค้ดของคุณจึงต้องตรวจขั้นสุดท้าย:
- parse ภายในตัวจัดการข้อผิดพลาด ดังตัวอย่างด้านบนสุด
- ตรวจสอบออบเจ็กต์ที่ parse แล้วกับ schema เดียวกันในโค้ดของคุณเอง เช่น ด้วยไลบรารี JSON Schema, Pydantic หรือ Zod
- คำตอบที่ยาวอาจจบก่อนที่ JSON จะสมบูรณ์ คำตอบที่ไม่ใช่สตรีมรายงาน
finish_reasonเป็นstopหรือtool_callsเท่านั้น ดังนั้นข้อผิดพลาดในการ parse คือสัญญาณ ให้ถามอีกครั้ง หรือขอออบเจ็กต์ที่เล็กลง
ข้อผิดพลาด
| สถานะ | ประเภท | ข้อความ | เมื่อใด |
|---|---|---|---|
400 | invalid_request_error | <id> does not accept response_format | โมเดลไม่รองรับทั้งสองโหมด |
400 | invalid_request_error | <id> does not accept response_format json_schema; use json_object | โมเดลรองรับ json_object แต่คุณส่ง json_schema |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | response_format ไม่มี type หรือไม่ใช่ออบเจ็กต์ JSON |
คำตอบ 400 ทั้งสองแบบถูกส่งก่อนที่จะหักอะไรจากยอดคงเหลือของคุณ การจัดการข้อผิดพลาด