DEV Community

Cover image for วิธีใช้งาน OpenAI Structured Outputs
Thanawat Wongchai
Thanawat Wongchai

Posted on • Originally published at apidog.com

วิธีใช้งาน OpenAI Structured Outputs

เมื่อจบคู่มือนี้ คุณจะสามารถเรียกใช้ structured outputs ของ OpenAI จากโค้ดของคุณเองได้: ส่ง JSON Schema ให้โมเดล, ตั้งค่า strict: true และรับผลตอบกลับที่ตรงกับรูปแบบที่ต้องการ จากนั้นคุณจะได้ส่งคำขอแรก, parse ผลลัพธ์, จัดการกรณีปฏิเสธ/ตัดทอน และสร้างชุดการทดสอบ API ใน Apidog เพื่อยืนยันว่า payload ตรงตาม schema จริง

ลองใช้ Apidog วันนี้

สิ่งที่คุณต้องมีก่อนเริ่มต้น

Structured outputs จำกัดการสร้างผลลัพธ์ของโมเดลให้เป็นไปตาม JSON Schema ที่คุณกำหนด เมื่อส่ง schema พร้อม strict: true โมเดลจะสร้างผลลัพธ์ที่มีคีย์, ประเภทข้อมูล และค่า enum ตรงตามข้อกำหนด แทนที่จะต้องเขียนโค้ดกัน parsing พังจาก JSON ที่รูปร่างไม่แน่นอน

Structured outputs ต่างจากการ prompt แบบ “ตอบกลับเป็น JSON เท่านั้น” เพราะคำสั่งแบบนั้นรับประกันได้แค่ในเชิงพฤติกรรม ไม่ใช่ข้อบังคับระดับ schema โมเดลอาจเพิ่มข้อความอธิบาย, คืนชนิดข้อมูลผิด หรือขาดฟิลด์ที่จำเป็นได้ ส่วน structured outputs บังคับใช้ข้อจำกัด ณ เวลาถอดรหัส

ก่อนเริ่ม คุณต้องมี:

  • OpenAI API key ที่ตั้งค่าไว้ใน environment variable ชื่อ OPENAI_API_KEY
  • โมเดลที่รองรับ schema แบบเข้มงวด
  • JSON Schema ที่อธิบาย output ที่ต้องการ

เลือกโมเดลที่เหมาะสม

Structured outputs ใช้งานได้ในโมเดล OpenAI รุ่นใหม่ตั้งแต่ตระกูล GPT-4o และต่อเนื่องถึงซีรีส์ GPT-5 เอกสารของ OpenAI แนะนำให้เริ่มโปรเจกต์ใหม่ด้วยโมเดลเรือธงล่าสุด (gpt-5.5 ในขณะที่เขียน)

โมเดลรุ่นเก่าและโมเดลยุค gpt-3.5 รองรับ JSON mode แต่ไม่รองรับการบังคับใช้ schema แบบเข้มงวด หากคุณต้องพึ่ง strict: true ให้ตรวจสอบ model ID ที่ใช้จริงก่อน deploy เพราะความสามารถนี้ผูกกับเวอร์ชันของโมเดล

OpenAI มี 2 ฟีเจอร์ที่คล้ายกันแต่ไม่เหมือนกัน:

JSON mode

{
  "response_format": {
    "type": "json_object"
  }
}
Enter fullscreen mode Exit fullscreen mode

JSON mode รับประกันว่า output เป็น JSON ที่ถูกต้องตามไวยากรณ์ แต่ไม่รับประกันว่าฟิลด์, ชนิดข้อมูล หรือ enum จะตรงกับ schema ของคุณ

Structured outputs

{
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "schema_name",
      "strict": true,
      "schema": {}
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Structured outputs รับประกันทั้ง JSON ที่ถูกต้องและรูปร่างที่ตรงกับ schema

โหมด JSON Structured outputs แบบเข้มงวด
พารามิเตอร์ response_format: {"type":"json_object"} response_format พร้อม type: "json_schema" และ strict: true
JSON ถูกต้อง ใช่ ใช่
ตรงกับ schema ไม่ ใช่
บังคับฟิลด์ที่จำเป็น ไม่ ใช่
บังคับ type และ enum ไม่ ใช่
ยังควร validate ต่อไหม ควรเสมอ ควรสำหรับกฎระดับค่าและ regression test

หมายเหตุเรื่อง API: Chat Completions ใช้ response_format ส่วน Responses API รุ่นใหม่แสดงแนวคิดเดียวกันภายใต้ text.format พร้อม type: "json_schema" กฎของ schema เหมือนกัน ต่างกันแค่ตำแหน่งของฟิลด์ใน request

ส่งคำขอ structured output ครั้งแรก

ตัวอย่างนี้ดึงข้อมูลจากข้อความ ticket support ให้เป็น object ที่มี schema ชัดเจน

curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.5",
    "messages": [
      {
        "role": "system",
        "content": "Extract the ticket into the schema."
      },
      {
        "role": "user",
        "content": "My checkout 500s every time I use a saved card. Started today. Account: acct_8842."
      }
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "support_ticket",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "summary": {
              "type": "string"
            },
            "category": {
              "type": "string",
              "enum": ["billing", "bug", "account", "other"]
            },
            "severity": {
              "type": "integer"
            },
            "account_id": {
              "anyOf": [
                { "type": "string" },
                { "type": "null" }
              ]
            }
          },
          "required": ["summary", "category", "severity", "account_id"],
          "additionalProperties": false
        }
      }
    }
  }'
Enter fullscreen mode Exit fullscreen mode

จุดที่ต้องสังเกต:

  • type: "json_schema" เปิด structured outputs
  • strict: true บังคับให้ output ตรง schema
  • ทุก property อยู่ใน required
  • additionalProperties: false ป้องกันคีย์ส่วนเกิน
  • ฟิลด์ที่อาจไม่มีค่าใช้ anyOf ร่วมกับ null

อ่านผลตอบกลับ

โมเดลจะคืน message.content เป็น string JSON ที่ตรงกับ schema เช่น:

{
  "summary": "Checkout returns HTTP 500 when paying with a saved card",
  "category": "bug",
  "severity": 3,
  "account_id": "acct_8842"
}
Enter fullscreen mode Exit fullscreen mode

ในแอปจริง คุณควร parse JSON หลังจากตรวจสอบว่าไม่มี refusal และผลลัพธ์ไม่ถูกตัดทอน

ตัวอย่าง Python:

import json

msg = response.choices[0].message

if msg.refusal:
    handle_refusal(msg.refusal)
else:
    ticket = json.loads(msg.content)
    process_ticket(ticket)
Enter fullscreen mode Exit fullscreen mode

เขียน schema ให้อยู่ใน subset ที่รองรับ

Structured outputs รองรับเพียง subset ของ JSON Schema เพื่อให้ OpenAI บังคับใช้ข้อจำกัดได้อย่างน่าเชื่อถือและ cache schema ที่ compile แล้วได้

กฎสำคัญ:

  1. root ต้องเป็น object

ใช้ไม่ได้:

   {
     "type": "array"
   }
Enter fullscreen mode Exit fullscreen mode

ใช้ object ห่อแทน:

   {
     "type": "object",
     "properties": {
       "items": {
         "type": "array",
         "items": {
           "type": "string"
         }
       }
     },
     "required": ["items"],
     "additionalProperties": false
   }
Enter fullscreen mode Exit fullscreen mode
  1. ทุก property ต้องอยู่ใน required

Structured outputs ไม่มี optional field แบบดั้งเดิม หากค่าขาดได้ ให้ทำเป็น nullable:

   {
     "account_id": {
       "anyOf": [
         { "type": "string" },
         { "type": "null" }
       ]
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. ทุก object ต้องมี additionalProperties: false

วิธีนี้ทำให้โมเดลไม่สร้างคีย์ที่ schema ไม่ได้ประกาศไว้

  1. schema มีข้อจำกัดด้านขนาด

โดยทั่วไป schema รองรับ object properties ได้ประมาณ 100 รายการ และซ้อนกันได้สูงสุด 5 ระดับ หาก schema กว้างหรือลึกเกินไป อาจถูกปฏิเสธ ควร flatten เท่าที่เหมาะสม

  1. บาง keyword ไม่ได้ถูกบังคับใช้โดยโมเดล

keyword อย่าง pattern, format, minLength และ minimum ไม่ได้รับประกันจากโมเดล ถ้าคุณต้องการ regex, date format หรือช่วงตัวเลขที่เข้มงวด ให้ validate ซ้ำในแอปหรือ test pipeline

ดังนั้น “JSON ที่รับประกัน” หมายถึงรูปร่างของ object ถูกล็อก ไม่ได้หมายความว่ากฎทางธุรกิจถูกต้องทั้งหมด ถ้าคุณเคยใช้ oneOf/anyOf/allOf เพื่อสร้าง optional หรือ union fields แนวคิดจะคล้ายกัน: schema คุมโครงสร้าง ส่วน business validation ต้องทำต่อเอง

จัดการ refusal และ output ที่ถูกตัดทอน

มีกรณีที่ output จะไม่ตรง schema โดยตั้งใจ: เมื่อโมเดลปฏิเสธคำขอที่ไม่ปลอดภัย โมเดลจะคืน refusal แทน content ที่เป็น JSON ตาม schema

ตรวจสอบ refusal ก่อน parse เสมอ:

import json

msg = response.choices[0].message

if msg.refusal:
    handle_refusal(msg.refusal)
    return

ticket = json.loads(msg.content)
Enter fullscreen mode Exit fullscreen mode

กรณีอื่นที่ต้องระวัง:

  • response ถูกตัดกลางคันเพราะถึง max_tokens
  • ใช้ parallel tool calls ร่วมกับ structured outputs

ถ้าคุณใช้ tool calls ร่วมด้วย ให้ตั้งค่า:

{
  "parallel_tool_calls": false
}
Enter fullscreen mode Exit fullscreen mode

ทดสอบ structured outputs ใน Apidog

โหมดเข้มงวดช่วยบังคับ schema ตอนโมเดลสร้างผลลัพธ์ แต่ยังไม่แทนที่การทดสอบ เพราะ model ID อาจถูกเปลี่ยน, schema อาจถูกแก้, prompt อาจเปลี่ยน หรือเส้นทาง refusal อาจทำงานต่างจากเดิม

คุณสามารถใช้ Apidog เพื่อสร้าง request, assertion และ mock สำหรับ workflow นี้

Apidog interface

การแบ่งหน้าที่ควรเป็นแบบนี้:

  • OpenAI structured outputs: สร้าง JSON ให้ตรง schema
  • Apidog: ตรวจสอบ response ที่ได้รับเทียบกับ schema และทำให้ความผิดพลาดถูกจับได้ใน CI

ขั้นตอนใช้งาน

  1. สร้าง request

สร้าง request ไปยัง Chat Completions ใน Apidog ใส่ headers และ body ที่มี response_format ตามตัวอย่างด้านบน จากนั้นบันทึกไว้ใน collection

  1. เพิ่ม assertion สำหรับ response

ตรวจสอบค่าหลัก เช่น:

  • category ต้องเป็น billing, bug, account หรือ other
  • severity ต้องเป็น integer
  • account_id ต้องเป็น string หรือ null

Apidog สามารถตรวจสอบ response เทียบกับ JSON Schema ได้โดยแนบ schema เดียวกับที่คุณส่งให้ OpenAI

  1. รันใน CI

ใส่ collection เข้า pipeline เพื่อให้ทุกการเปลี่ยน model, prompt หรือ schema ถูกทดสอบซ้ำ หาก response ผิด contract ให้ build fail ทันที

  1. สร้าง mock API

ก่อนต่อ OpenAI จริง หรือเมื่อต้องการทดสอบ frontend/consumer โดยไม่ใช้ token ให้ตั้งค่า mock API ที่คืนตัวอย่าง response ตาม schema เดียวกัน

แนวทางนี้ช่วยให้ทีมที่ consume structured output ทำงานต่อได้โดยไม่ต้องรอ integration เสร็จทั้งหมด คุณสามารถดาวน์โหลด Apidog แล้วจัดการ request, assertion และ mock ในที่เดียว

คำถามที่พบบ่อย

JSON mode ถูกแทนที่ด้วย structured outputs แล้วหรือยัง?

ยังไม่ถูกแทนที่ JSON mode ยังใช้ได้และยังรับประกัน JSON ที่ valid แต่ไม่บังคับ schema สำหรับโค้ดใหม่ที่มีรูปแบบข้อมูลชัดเจน ควรใช้ structured outputs พร้อม strict: true

root schema เป็น array ได้ไหม?

ไม่ได้ ระดับบนสุดต้องเป็น object ให้ห่อ array ไว้ใน property เช่น:

{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": ["items"],
  "additionalProperties": false
}
Enter fullscreen mode Exit fullscreen mode

ทำ optional field อย่างไร?

Structured outputs กำหนดให้ทุก property อยู่ใน required ถ้าค่าหายได้ ให้ใช้ nullable field:

{
  "nickname": {
    "anyOf": [
      { "type": "string" },
      { "type": "null" }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

คีย์ nickname จะมีเสมอ แต่ค่าเป็น null ได้

ใช้ strict mode แล้วข้าม validation ได้ไหม?

ข้ามการ validate รูปร่างพื้นฐานได้ในหลายกรณี แต่ยังควร validate กฎระดับค่า เช่น regex, format, min/max และ business rules เพราะ keyword บางตัวไม่ได้ถูกบังคับใช้โดยโมเดล นอกจากนี้ refusal และ truncation ยังต้องจัดการแยกต่างหาก หากยังไม่คุ้นกับ schema อ่านพื้นฐานได้ที่บทนำ JSON Schema

ควรใช้โมเดลใด?

Structured outputs ใช้งานได้กับ GPT-4o และรุ่นใหม่กว่า รวมถึงซีรีส์ GPT-5 เอกสาร OpenAI แนะนำให้ใช้โมเดลเรือธงปัจจุบันสำหรับโปรเจกต์ใหม่ แต่คุณควรตรวจสอบ model ID ที่ใช้จริงว่ารองรับ strict: true ก่อนนำไป production

สรุป

workflow ที่ควรใช้คือ: เลือกโมเดลที่รองรับ structured outputs, ส่ง schema ผ่าน response_format พร้อม strict: true, เขียน schema ให้อยู่ใน subset ที่รองรับ, ตรวจ refusal ก่อน parse, และ validate กฎระดับค่าซ้ำเมื่อจำเป็น จากนั้นใช้ Apidog เพื่อยืนยัน response เทียบกับ schema และสร้าง mock สำหรับส่วนอื่นของระบบ โมเดลช่วยรับประกันรูปร่าง ส่วน test suite ช่วยพิสูจน์ว่าสัญญานั้นยังไม่พังเมื่อระบบเปลี่ยนแปลง

Top comments (0)