Đến cuối hướng dẫn này, bạn sẽ có thể gọi các đầu ra có cấu trúc của OpenAI từ mã của riêng mình: cung cấp cho mô hình một JSON Schema, đặt strict: true và nhận lại một phản hồi được đảm bảo khớp với hình dạng bạn yêu cầu. Bạn sẽ gửi yêu cầu đầu tiên, đọc phản hồi, xử lý các trường hợp ngoại lệ và tạo bộ sưu tập kiểm thử API trong Apidog để khẳng định rằng payload thực sự tuân thủ.
Những gì bạn cần trước khi bắt đầu
Đầu ra có cấu trúc giới hạn việc tạo của mô hình để đầu ra tuân thủ một JSON Schema mà bạn cung cấp. Khi bạn truyền một schema với strict: true, mô hình không thể phát ra một trường vi phạm nó. Mọi khóa bắt buộc đều có mặt, mọi kiểu đều khớp và mọi giá trị enum đều là một trong những giá trị bạn đã liệt kê. Bạn ngừng viết mã phân tích cú pháp phòng thủ và bắt đầu tin tưởng vào payload.
Đó là một sự nâng cấp thực sự so với các lựa chọn thay thế. Các lời nhắc văn bản tự do như “chỉ phản hồi bằng JSON” hoạt động cho đến khi chúng không còn hoạt động nữa. Một sai sót trong lý luận và bạn nhận được văn xuôi bao quanh đối tượng của mình, hoặc một ngày tháng trong khi bạn mong đợi một số nguyên. Các đầu ra có cấu trúc di chuyển hợp đồng từ một hướng dẫn đầy hy vọng thành một ràng buộc được thực thi tại thời điểm giải mã.
Để làm theo, bạn cần:
- Một khóa API OpenAI được đặt là
OPENAI_API_KEY. - Một mô hình hỗ trợ thực thi schema nghiêm ngặt (thêm về các mô hình nào bên dưới).
- Một JSON Schema mô tả hình dạng bạn muốn trả về.
Chọn mô hình phù hợp
Các đầu ra có cấu trúc có sẵn trên các mô hình gần đây của OpenAI, bắt đầu với dòng GPT-4o và tiếp tục qua dòng GPT-5. Tài liệu của OpenAI hiện khuyến nghị bắt đầu các dự án mới trên flagship mới nhất của họ (gpt-5.5 tại thời điểm viết bài này). Các mô hình cũ hơn và các mô hình thời gpt-3.5 hỗ trợ chế độ JSON nhưng không hỗ trợ thực thi schema nghiêm ngặt. Nếu bạn phụ thuộc vào strict: true, hãy xác nhận ID mô hình cụ thể hỗ trợ nó trước khi bạn triển khai, vì hỗ trợ được gắn với các phiên bản mô hình và OpenAI sẽ cập nhật chúng.
Điều quan trọng là phải biết bạn đang thực sự tìm kiếm tính năng nào, vì OpenAI cung cấp hai tính năng liên quan dễ gây nhầm lẫn.
Chế độ JSON (response_format: { "type": "json_object" }) đảm bảo đầu ra là JSON hợp lệ về mặt cú pháp. Chỉ vậy thôi. Nó không biết các trường của bạn, các kiểu của bạn, hoặc các khóa bắt buộc của bạn. Bạn vẫn phải tự xác thực hình dạng.
Đầu ra có cấu trúc (response_format với type: "json_schema" và strict: true) đảm bảo đầu ra là JSON hợp lệ và khớp với schema của bạn. OpenAI mô tả đầu ra có cấu trúc là sự phát triển của chế độ JSON: cả hai đều tạo ra JSON hợp lệ, nhưng chỉ đầu ra có cấu trúc mới thực thi sự tuân thủ schema. Đối với công việc mới, đầu ra có cấu trúc là cái bạn muốn.
| Chế độ JSON | Đầu ra có cấu trúc (nghiêm ngặt) | |
|---|---|---|
| Tham số | response_format: {"type":"json_object"} |
response_format với type: "json_schema", strict: true |
| JSON hợp lệ | Có | Có |
| Khớp với schema của bạn | Không | Có |
| Các trường bắt buộc được thực thi | Không | Có |
| Kiểu và enum được thực thi | Không | Có |
| Bạn vẫn xác thực sau đó | Luôn luôn | Khuyến nghị (xem bên dưới) |
Một lưu ý về API: điểm cuối Chat Completions sử dụng response_format như đã hiển thị ở trên. API Responses mới hơn thể hiện điều tương tự dưới text.format với type: "json_schema". Các quy tắc schema giống hệt nhau; chỉ có wrapper khác. Kiểm tra tài liệu hiện tại để biết đường dẫn trường chính xác trên điểm cuối bạn gọi.
Thực hiện yêu cầu đầu tiên của bạn
Giả sử bạn đang trích xuất một phiếu hỗ trợ vào một bản ghi được gán kiểu. Dưới đây là một yêu cầu Chat Completions với một schema nghiêm ngặt.
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
}
}
}
}'
Đọc phản hồi
Mô hình trả về một thông báo có content là một chuỗi JSON khớp với 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"
}
Hãy chú ý account_id sử dụng anyOf với null. Đó là cách bạn mô hình hóa một trường tùy chọn, điều này dẫn thẳng đến các quy tắc bạn phải tuân theo khi bạn viết schema của riêng mình.
Giữ trong tập hợp con của schema
Đầu ra có cấu trúc chấp nhận một tập hợp con của JSON Schema. Tập hợp con tồn tại để OpenAI có thể thực thi các ràng buộc một cách đáng tin cậy và lưu vào bộ nhớ cache schema đã biên dịch. Các quy tắc đáng ghi nhớ:
- Root phải là một đối tượng. Bạn không thể đặt cấp cao nhất là một mảng hoặc một chuỗi trần. Bọc một danh sách trong một thuộc tính đối tượng.
- Mọi thuộc tính phải được liệt kê trong
required. Không có "tùy chọn" theo nghĩa thông thường. Để làm cho một trường có thể nhận giá trị null, hãy đặt nóanyOfvới kiểunull, như trong ví dụ trên. additionalPropertiesphải làfalsetrên mọi đối tượng. Điều này ngăn mô hình tạo ra các khóa bổ sung.- Áp dụng giới hạn kích thước. Một schema có thể chứa khoảng 100 thuộc tính đối tượng với tối đa 5 cấp độ lồng ghép. Các schema lồng ghép sâu hoặc quá lớn sẽ bị từ chối, vì vậy hãy làm phẳng khi có thể.
- Một số từ khóa không được thực thi. Các từ khóa chỉ dùng để xác thực như
pattern,format,minLengthvàminimumkhông được mô hình đảm bảo. Nếu bạn cần một biểu thức chính quy hoặc một phạm vi số được tuân thủ, bạn vẫn phải tự kiểm tra sau khi nhận được phản hồi. - Độ trễ cuộc gọi đầu tiên. Yêu cầu đầu tiên với một schema mới sẽ mất thời gian biên dịch (thường là vài giây, đôi khi lên đến một phút đối với các schema phức tạp). Sau đó, nó được lưu vào bộ nhớ cache và nhanh chóng.
Vì các từ khóa xác thực không được thực thi, "JSON được đảm bảo" không có nghĩa là "được đảm bảo hợp lệ về mặt nghiệp vụ". Cấu trúc được khóa. Các giá trị bên trong vẫn cần được kiểm tra. Nếu bạn đã từng mô hình hóa các trường tùy chọn hoặc trường hợp nhất với oneOf/anyOf/allOf, mô hình tư duy này đã quen thuộc: một schema ràng buộc hình dạng, nhưng bạn khẳng định các giá trị thực riêng biệt.
Xử lý từ chối và cắt ngắn
Có một trường hợp mà đầu ra sẽ không khớp với schema của bạn một cách có chủ đích. Nếu mô hình từ chối một yêu cầu không an toàn, nó sẽ trả về một trường refusal trên thông báo thay vì nội dung có hình dạng schema. Mã của bạn nên phân nhánh theo điều đó trước khi phân tích cú pháp:
msg = response.choices[0].message
if msg.refusal:
handle_refusal(msg.refusal)
else:
ticket = json.loads(msg.content)
Các từ chối giờ đây có thể được phát hiện theo chương trình, điều này rõ ràng hơn là quét văn bản để tìm lời xin lỗi. Hai cách khác mà phản hồi có thể không đạt được schema: đạt max_tokens giữa đối tượng (JSON bị cắt ngắn), hoặc sử dụng các lệnh gọi công cụ song song, điều mà đầu ra có cấu trúc không hỗ trợ. Đặt parallel_tool_calls thành false khi bạn kết hợp cả hai.
Cách kiểm thử trong Apidog
Chế độ nghiêm ngặt thực thi schema tại thời điểm tạo. Nó không giải phóng bạn khỏi việc kiểm thử. Các mô hình được thay thế, các schema thay đổi, một đồng đội chỉnh sửa mảng required, hoặc một đường dẫn từ chối thay đổi. Bạn muốn một bài kiểm thử thất bại một cách rõ ràng khi phản hồi không còn khớp với hợp đồng. Đó là lúc Apidog phù hợp.

Để chính xác về phân chia công việc: Chế độ nghiêm ngặt của OpenAI là thứ tạo ra JSON hợp lệ theo schema. Apidog không thực thi schema tại mô hình. Điều mà Apidog làm là xác thực phản hồi bạn nhận được so với schema bạn mong đợi, để bạn phát hiện sự lệch lạc trong CI thay vì trong sản xuất.
Dưới đây là quy trình làm việc:
- Gửi yêu cầu. Xây dựng cuộc gọi Chat Completions trong Apidog với khối
response_formatcủa bạn. Lưu nó vào một bộ sưu tập để có thể lặp lại. - Khẳng định hình dạng. Thêm các khẳng định phản hồi trong Apidog. Kiểm tra xem
categorylà một trong các giá trị enum của bạn, rằngseveritylà một số nguyên, rằngaccount_idlà một chuỗi hoặc null. Apidog có thể xác thực phản hồi dựa trên JSON Schema để bạn đính kèm schema chính xác và làm cho quá trình chạy thất bại khi payload bị lệch. - Chạy nó trong CI. Đặt bộ sưu tập vào pipeline của bạn để mọi thay đổi mô hình hoặc lời nhắc đều kiểm tra lại sự tuân thủ. Một lỗi schema âm thầm sẽ trở thành một bản dựng đỏ.
- Giả lập hợp đồng. Trước khi cuộc gọi thực sự tồn tại, hoặc để kiểm tra các consumer phía sau mà không tốn token, hãy thiết lập một mock API trả về các phản hồi mẫu hợp lệ theo schema. Frontend và các bài kiểm thử của bạn chạy dựa trên một hình dạng ổn định trong khi tích hợp được củng cố.
Điểm cuối cùng là điểm bị đánh giá thấp. Bạn có thể phát triển và kiểm thử mọi thứ tiêu thụ đầu ra có cấu trúc dựa trên một mock tuân thủ cùng một schema, sau đó thay thế bằng cuộc gọi OpenAI trực tiếp khi bạn sẵn sàng. Tải xuống Apidog và bạn có thể xây dựng yêu cầu, các khẳng định và mock ở một nơi.
Các câu hỏi thường gặp
Chế độ JSON có bị lỗi thời không khi có đầu ra có cấu trúc? Chế độ JSON vẫn hoạt động và vẫn đảm bảo JSON hợp lệ. Nó chỉ không thực thi một schema. Đối với mã mới, đầu ra có cấu trúc với strict: true là lựa chọn mạnh mẽ hơn. Chỉ sử dụng chế độ JSON thông thường trên các mô hình không hỗ trợ schema nghiêm ngặt, hoặc khi bạn thực sự không có một hình dạng cố định.
Root của schema của tôi có thể là một mảng không? Không. Cấp cao nhất phải là một đối tượng. Bọc danh sách của bạn trong một thuộc tính, như { "items": [...] }, và đặt items vào required. Điều này gây khó khăn cho nhiều người ngay từ ngày đầu.
Làm thế nào để tôi tạo một trường tùy chọn? Đầu ra có cấu trúc yêu cầu mọi thuộc tính phải có trong required, vì vậy không có trường tùy chọn cổ điển. Mô hình hóa "thiếu" dưới dạng có thể null: sử dụng anyOf với một string (hoặc bất kỳ kiểu nào) và một null. Khóa luôn hiện diện; giá trị của nó có thể là null.
Chế độ nghiêm ngặt có nghĩa là tôi có thể bỏ qua hoàn toàn xác thực không? Cấu trúc được đảm bảo, vì vậy bạn có thể bỏ qua kiểm tra hình dạng. Nhưng các từ khóa như pattern, format và các phạm vi số không được mô hình thực thi, và các từ chối hoặc cắt ngắn vẫn có thể tạo ra các phản hồi không đúng đặc tả. Một bài kiểm thử tuân thủ vẫn giữ vai trò của nó. Nếu bạn mới làm quen với định dạng này, hướng dẫn về JSON Schema này bao gồm các khối xây dựng, và bạn có thể chạy kiểm tra trên mọi lần triển khai.
Tôi nên sử dụng mô hình nào? Đầu ra có cấu trúc hoạt động trên GPT-4o trở lên, bao gồm dòng GPT-5. Tài liệu của OpenAI hướng các dự án mới đến flagship hiện tại của họ. Xác nhận ID mô hình chính xác hỗ trợ chế độ nghiêm ngặt trước khi phụ thuộc vào nó, vì hỗ trợ theo dõi các phiên bản mô hình.
Tổng kết
Giờ đây bạn đã có một vòng lặp hoàn chỉnh: chọn một mô hình hỗ trợ chế độ nghiêm ngặt, gửi yêu cầu Chat Completions với json_schema và strict: true, giữ schema của bạn trong tập hợp con được hỗ trợ, phân nhánh dựa trên refusal, và nhớ rằng các quy tắc cấp giá trị vẫn cần được kiểm tra. Sau đó, chứng minh điều đó bằng một bài kiểm thử: xây dựng yêu cầu trong Apidog, khẳng định phản hồi dựa trên schema của bạn, và giả lập nó để phần còn lại của hệ thống của bạn có thể hoạt động trong khi tích hợp được ổn định. Mô hình hứa hẹn hình dạng. Các bài kiểm thử của bạn chứng minh nó giữ nguyên hình dạng đó.
