DEV Community

Cover image for Cách sử dụng đầu ra có cấu trúc của OpenAI
Sebastian Petrus
Sebastian Petrus

Posted on • Originally published at apidog.com

Cách sử dụng đầu ra có cấu trúc của OpenAI

Đến cuối hướng dẫn này, bạn sẽ biết cách gọi OpenAI Structured Outputs từ code của mình: truyền JSON Schema, bật strict: true, nhận JSON khớp đúng hình dạng đã khai báo, xử lý lỗi/từ chối, và tạo bộ kiểm thử API trong Apidog để xác minh payload thực tế luôn tuân thủ hợp đồng.

Dùng thử Apidog hôm nay

Chuẩn bị trước khi bắt đầu

Structured Outputs giới hạn đầu ra của mô hình theo JSON Schema bạn cung cấp. Khi đặt strict: true, mô hình phải trả về JSON hợp lệ và khớp schema: đủ trường bắt buộc, đúng kiểu dữ liệu, enum chỉ nhận giá trị đã khai báo.

So với prompt kiểu “chỉ trả về JSON”, cách này đáng tin cậy hơn nhiều. Prompt tự do vẫn có thể sinh thêm văn bản, thiếu field, sai kiểu hoặc trả về format không mong muốn. Structured Outputs chuyển “mong muốn” thành ràng buộc ở thời điểm sinh token.

Bạn cần:

  • API key OpenAI trong biến môi trường OPENAI_API_KEY.
  • Một model hỗ trợ strict schema.
  • Một JSON Schema mô tả output mong muốn.
  • Một cách kiểm thử response thực tế, ví dụ bằng Apidog.

Chọn model hỗ trợ Structured Outputs

Structured Outputs có trên các model OpenAI gần đây, bắt đầu từ dòng GPT-4o và tiếp tục qua dòng GPT-5. Tài liệu OpenAI hiện khuyến nghị bắt đầu dự án mới với flagship mới nhất của họ, ví dụ gpt-5.5 tại thời điểm bài viết gốc.

Các model cũ hơn, bao gồm dòng gpt-3.5, có thể hỗ trợ JSON mode nhưng không hỗ trợ schema strict. Nếu code của bạn phụ thuộc vào strict: true, hãy kiểm tra model ID cụ thể trước khi deploy.

Có hai tính năng dễ nhầm:

JSON mode

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

JSON mode chỉ đảm bảo output là JSON hợp lệ về cú pháp. Nó không đảm bảo đủ field, đúng kiểu, đúng enum hoặc đúng schema nghiệp vụ.

Structured Outputs

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

Structured Outputs đảm bảo output là JSON hợp lệ và khớp JSON Schema bạn truyền vào.

JSON mode Structured Outputs strict
Tham số response_format: {"type":"json_object"} response_format với type: "json_schema", strict: true
JSON hợp lệ
Khớp schema Không
Field bắt buộc được thực thi Không
Kiểu dữ liệu và enum được thực thi Không
Cần validate sau đó Luôn cần Vẫn nên làm ở tầng test/CI

Lưu ý API: Chat Completions dùng response_format. API Responses mới hơn biểu diễn cùng ý tưởng dưới text.format với type: "json_schema". Quy tắc schema giống nhau, chỉ khác wrapper.

Gửi request Structured Outputs đầu tiên

Ví dụ: bạn muốn trích xuất một ticket hỗ trợ từ nội dung người dùng.

Request dưới đây yêu cầu model trả về đúng schema support_ticket.

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": "Trích xuất phiếu hỗ trợ vào schema."
      },
      {
        "role": "user",
        "content": "Hệ thống thanh toán của tôi báo lỗi 500 mỗi khi tôi sử dụng thẻ đã lưu. Bắt đầu từ hôm nay. Tài khoản: 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

Đọc response

Model trả về message.content là một chuỗi JSON khớp schema, ví dụ:

{
  "summary": "Hệ thống thanh toán trả về HTTP 500 khi thanh toán bằng thẻ đã lưu",
  "category": "bug",
  "severity": 3,
  "account_id": "acct_8842"
}
Enter fullscreen mode Exit fullscreen mode

Trong code, bạn vẫn parse chuỗi JSON này như bình thường:

import json

msg = response.choices[0].message
ticket = json.loads(msg.content)

print(ticket["category"])
Enter fullscreen mode Exit fullscreen mode

Điểm quan trọng: account_id không bị bỏ qua khi thiếu dữ liệu. Vì mọi field đều phải nằm trong required, field “tùy chọn” nên được mô hình hóa bằng anyOf với null.

Viết schema đúng tập con được hỗ trợ

Structured Outputs chấp nhận một tập con của JSON Schema. Khi thiết kế schema, hãy tuân thủ các quy tắc sau.

1. Root phải là object

Không đặt schema cấp cao nhất là array hoặc string.

Không nên:

{
  "type": "array",
  "items": {
    "type": "string"
  }
}
Enter fullscreen mode Exit fullscreen mode

Nên bọc array trong object:

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

2. Mọi property phải nằm trong required

Structured Outputs không có “optional field” theo kiểu truyền thống. Nếu một field có thể không có giá trị, hãy cho phép null.

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

Sau đó vẫn đưa field này vào required.

{
  "required": ["account_id"]
}
Enter fullscreen mode Exit fullscreen mode

3. Luôn đặt additionalProperties: false

Mỗi object nên chặn key ngoài schema:

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

Điều này giúp model không sinh thêm field ngoài hợp đồng.

4. Giữ schema nhỏ và nông

Schema có giới hạn kích thước, khoảng 100 property object và tối đa 5 cấp lồng nhau. Nếu schema quá sâu hoặc quá lớn, hãy tách nhỏ hoặc làm phẳng.

5. Đừng xem mọi keyword JSON Schema là ràng buộc được model đảm bảo

Một số keyword dùng cho validation như:

  • pattern
  • format
  • minLength
  • minimum

không được đảm bảo bởi model. Nếu bạn cần kiểm tra regex, định dạng email, ngày tháng, range số hoặc rule nghiệp vụ, hãy validate sau khi nhận response.

Ví dụ:

if ticket["severity"] < 1 or ticket["severity"] > 5:
    raise ValueError("severity phải nằm trong khoảng 1-5")
Enter fullscreen mode Exit fullscreen mode

Nếu bạn từng làm việc với các trường tùy chọn hoặc oneOf/anyOf/allOf trong JSON Schema, cách tư duy tương tự vẫn áp dụng: schema khóa hình dạng, còn rule nghiệp vụ nên được kiểm tra riêng.

Xử lý refusal, truncation và tool calls

Có trường hợp output không khớp schema một cách có chủ đích: model từ chối yêu cầu không an toàn.

Khi đó response có thể chứa trường refusal thay vì JSON theo schema. Code của bạn nên kiểm tra nhánh này trước khi parse.

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

Ngoài refusal, còn hai tình huống cần xử lý:

  1. Response bị cắt do max_tokens

    JSON có thể bị cắt giữa chừng. Hãy kiểm tra trạng thái kết thúc response và tăng token limit nếu cần.

  2. Parallel tool calls

    Structured Outputs không hỗ trợ parallel tool calls. Khi kết hợp tool calling, đặt:

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

Kiểm thử schema trong Apidog

Strict mode giúp model sinh JSON theo schema, nhưng bạn vẫn nên kiểm thử response thực tế. Lý do:

  • Model có thể được thay thế.
  • Prompt có thể bị chỉnh sửa.
  • Schema có thể thay đổi.
  • Một field trong required có thể bị xóa nhầm.
  • Luồng refusal hoặc lỗi có thể thay đổi.

Đây là nơi Apidog hữu ích: bạn lưu request, chạy lại nhiều lần, validate response theo schema và đưa vào CI.

Phân chia trách nhiệm như sau:

  • OpenAI strict mode: sinh JSON theo schema.
  • Apidog: kiểm tra response nhận được có khớp schema/hợp đồng mong đợi hay không.

Quy trình kiểm thử đề xuất

Bước 1: Tạo request trong Apidog

Tạo request gọi Chat Completions và đặt phần body chứa response_format.

{
  "model": "gpt-5.5",
  "messages": [
    {
      "role": "system",
      "content": "Trích xuất phiếu hỗ trợ vào schema."
    },
    {
      "role": "user",
      "content": "Hệ thống thanh toán của tôi báo lỗi 500 mỗi khi tôi sử dụng thẻ đã lưu. Bắt đầu từ hôm nay. Tài khoản: 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

Lưu request này vào collection để chạy lại được.

Bước 2: Thêm assertion cho response

Bạn nên kiểm tra tối thiểu:

  • summary là string.
  • category thuộc enum billing | bug | account | other.
  • severity là integer.
  • account_id là string hoặc null.
  • Không có field ngoài schema.

Apidog có thể xác thực response dựa trên JSON Schema, giúp collection fail khi payload lệch hợp đồng.

Bước 3: Chạy trong CI

Đưa collection vào pipeline. Khi prompt, schema hoặc model thay đổi, test sẽ chạy lại. Nếu output không còn khớp schema, build fail sớm thay vì lỗi ở production.

Bước 4: Mock API cho consumer

Trước khi tích hợp OpenAI thật, hoặc khi muốn test frontend/backend consumer mà không tốn token, tạo mock API trả về response mẫu hợp lệ.

Ví dụ mock response:

{
  "summary": "Hệ thống thanh toán trả về HTTP 500 khi thanh toán bằng thẻ đã lưu",
  "category": "bug",
  "severity": 3,
  "account_id": "acct_8842"
}
Enter fullscreen mode Exit fullscreen mode

Consumer có thể phát triển dựa trên hợp đồng ổn định. Khi tích hợp OpenAI thật sẵn sàng, bạn thay mock bằng request thật. Bạn có thể tải xuống Apidog để quản lý request, assertion và mock trong cùng một nơi.

FAQ

JSON mode có còn cần thiết không?

Có, JSON mode vẫn đảm bảo output là JSON hợp lệ. Nhưng nó không thực thi schema.

Với code mới, hãy dùng Structured Outputs với strict: true nếu bạn có hình dạng dữ liệu cố định. Chỉ dùng JSON mode khi model không hỗ trợ strict schema hoặc khi bạn thật sự không cần schema cố định.

Root schema có thể là array không?

Không. Root phải là object.

Thay vì:

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

hãy dùng:

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

Làm sao tạo field tùy chọn?

Không bỏ field khỏi required. Hãy cho phép giá trị null.

{
  "type": "object",
  "properties": {
    "account_id": {
      "anyOf": [
        { "type": "string" },
        { "type": "null" }
      ]
    }
  },
  "required": ["account_id"],
  "additionalProperties": false
}
Enter fullscreen mode Exit fullscreen mode

Key luôn tồn tại, nhưng value có thể là null.

strict: true có nghĩa là bỏ hết validation không?

Không. Strict mode đảm bảo cấu trúc JSON khớp schema, nhưng các rule cấp giá trị như pattern, format, range số hoặc logic nghiệp vụ vẫn nên được validate trong code hoặc test.

Nếu bạn mới làm quen với schema, xem thêm hướng dẫn JSON Schema.

Nên dùng model nào?

Structured Outputs hoạt động trên GPT-4o trở lên, bao gồm dòng GPT-5. Hãy xác nhận model ID cụ thể hỗ trợ strict schema trước khi phụ thuộc vào tính năng này trong production.

Tổng kết

Quy trình triển khai thực tế:

  1. Chọn model hỗ trợ Structured Outputs.
  2. Gửi request với response_format.type = "json_schema".
  3. Đặt strict: true.
  4. Viết schema với root object, đầy đủ required, và additionalProperties: false.
  5. Dùng null để mô hình hóa field có thể thiếu giá trị.
  6. Kiểm tra refusal trước khi parse JSON.
  7. Validate thêm rule nghiệp vụ trong code/test.
  8. Dùng Apidog để lưu request, assertion, mock và chạy kiểm thử trong CI.

Model cam kết hình dạng dữ liệu. Test của bạn xác minh cam kết đó vẫn đúng sau mỗi thay đổi.

Top comments (0)