OpenAI-compatible API의 Structured Outputs: JSON Schema 가이드
Responses와 Chat Completions에서 strict JSON Schema를 사용하고 결과 검증, 예외 처리, route 호환성을 점검하는 실전 가이드입니다.
Structured Outputs를 사용하면 모델에 단순히 “JSON으로 응답하라”고 요청하는 대신 정의된 Schema를 따르는 JSON을 요구할 수 있습니다. OpenAI-compatible API에서는 같은 JSON Schema를 Responses와 Chat Completions에서 사용할 수 있지만 wrapper field가 다르며, 선택한 모델과 route에서 실제 지원 여부를 검증해야 합니다.
안전한 production pattern은 세 단계입니다. 지원 모델에서는 strict Schema를 사용하고, 애플리케이션에서 반환 JSON을 다시 parse·validate한 뒤, 값에 deterministic business rule을 적용합니다. Schema 준수는 형식 오류를 줄이지만 사실성과 의미 정확성을 보장하지 않습니다.
Structured Outputs와 JSON mode의 차이
| 방법 | 유효한 JSON | Schema 강제 | 일반적인 용도 |
|---|---|---|---|
| Prompt만 사용 | 보장되지 않음 | 아니요 | Parsing 실패를 허용하는 prototype |
| JSON mode | 지원되고 완료되면 예 | 아니요 | 애플리케이션이 shape를 검증하는 유연한 JSON |
strict: true Structured Outputs |
완료와 refusal 처리가 필요 | 지원되는 JSON Schema subset 내에서 예 | Typed extraction과 application workflow |
OpenAI Structured Outputs 가이드는 모델과 endpoint가 지원하면 JSON mode보다 Structured Outputs 사용을 권장합니다. 모델이 예측 가능한 data object로 답해야 하면 structured response format을, 애플리케이션 동작을 요청해야 하면 Function Calling을 사용합니다. 두 방식은 strict argument schema를 공유할 수 있지만 Call ID와 result message contract는 다릅니다.
Endpoint보다 Schema를 먼저 정의하기
고객 지원 분류에 다음 네 field가 필요하다고 가정합니다.
{
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
}
모든 property를 required로 지정하고 additionalProperties: false를 설정하면 downstream code가 안정된 object를 받을 수 있습니다. 그렇다고 모든 JSON Schema keyword가 portable한 것은 아닙니다. OpenAI는 문서화된 subset을 지원하며 다른 OpenAI-compatible provider는 다른 subset 또는 strict mode 자체를 지원하지 않을 수 있습니다.
Responses API로 Schema 보내기
Responses API는 Schema를 text.format 아래에 둡니다.
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"input": "The customer was charged twice and wants a refund.",
"text": {
"format": {
"type": "json_schema",
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
Authorization: Bearer <YOUR_API_KEY> Header와 함께 POST https://modelflare.dev/v1/responses로 전송합니다. Responses의 생성 결과는 typed output item에 있습니다. SDK helper가 aggregated text 또는 parsed output을 제공할 수 있지만, HTTP client는 top-level typed object를 가정하지 말고 completed response에서 text output을 찾아 JSON으로 parse해야 합니다.
Chat Completions로 같은 Schema 보내기
Chat Completions는 response_format.json_schema를 사용합니다.
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"messages": [
{ "role": "user", "content": "The customer was charged twice and wants a refund." }
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
JSON text는 일반적으로 choices[0].message.content에 있으며 typed object가 아니므로 parse가 필요합니다.
| 목적 | Responses API | Chat Completions |
|---|---|---|
| Schema container | text.format |
response_format.json_schema |
| Format type | text.format.type |
response_format.type |
| Schema name | text.format.name |
response_format.json_schema.name |
| Schema body | text.format.schema |
response_format.json_schema.schema |
| Strict flag | text.format.strict |
response_format.json_schema.strict |
| Result | Typed output items / helper | choices[0].message.content |
Modelflare의 OpenAI compatibility layer는 지원되는 OpenAI/Codex route에서 두 wrapper를 변환하지만 upstream model에 없는 Structured Outputs 기능을 만들 수는 없습니다. 다른 model family가 raw Chat Completions pass-through로 제공된다면 provider의 정확한 request contract가 Source of Truth입니다.
구조와 의미를 별도로 검증하기
{
"category": "billing",
"priority": 2,
"requires_human": true,
"summary": "Customer reports a duplicate charge and requests a refund."
}
구조 검증
JSON Schema validator 또는 typed SDK helper로 required property, unexpected property, type, enum, range를 확인합니다. Strict Adherence를 약속받았더라도 애플리케이션 검증은 미지원 route, integration mistake, truncation, contract change로부터 시스템을 보호합니다.
의미와 Business Rule 검증
Schema는 실제 중복 결제가 있었는지, priority: 2가 적절한지, 사람이 환불을 승인해야 하는지 판단하지 못합니다. 영향이 큰 workflow에서는 모델 object를 제안된 classification으로 취급하고 authoritative record와 비교해야 합니다. 권한과 재무 limit은 deterministic code에서 강제하며, 구조적으로 유효한 object가 authorization, billing, security boundary를 우회하게 해서는 안 됩니다.
불완전하거나 예외적인 출력 처리하기
Refusal
JSON을 찾기 전에 response status와 refusal representation을 확인합니다. 안전 거부를 Schema parsing error로 보고하면 안 됩니다.
Truncation과 Output Limit
Object가 끝나기 전에 generation이 중단되면 Schema가 누락된 suffix를 복원할 수 없습니다. Completion Status와 Stop Reason을 확인하고 가장 큰 정상 object를 수용할 bounded Output Limit을 설정합니다.
지원되지 않는 Schema Keyword
Strict output은 대개 JSON Schema subset만 지원합니다. Object, Array, Primitive, Enum, Required, 명시적 Additional Properties부터 시작하고 Reference, Recursion, Complex Union 또는 advanced keyword는 provider 문서를 확인한 뒤 사용합니다.
첫 Schema 사용 지연
일부 provider는 새 Schema를 preprocess하고 cache합니다. 첫 request가 이후 request보다 느릴 수 있으므로 안정되고 versioned Schema를 재사용하고 first-use와 steady-state latency를 분리합니다.
Model 또는 Route 비호환
OpenAI-compatible endpoint가 일반 Chat Completions는 받으면서 json_schema를 거절하거나 strict를 무시할 수 있습니다. JSON이 포함된 200도 Strict Adherence 증거가 아닙니다. Invalid input과 edge case를 포함해 테스트해야 합니다.
Production 전에 호환성 매트릭스 실행하기
| 테스트 | 기록할 증거 |
|---|---|
| 최소 Required Object | Status, model, route, parsed object, validation result |
| 모든 Enum Value | 각 값의 생성 및 parse 여부 |
| 정보 부족 | 제한된 fallback value 또는 clarification 요청 |
| Safety input | Refusal format과 application handling |
| 낮은 Output Limit | Completion Status와 truncation 처리 |
| 미지원 Schema Feature | 명시적 error 또는 silent ignore |
| Responses와 Chat Wrapper | application object 동등성 |
| Stable Schema 반복 | First-use와 steady-state timing |
Schema, Prompt, Region, Streaming Mode, Output Limit이 다른 상태에서 모델 품질만 비교하면 안 됩니다. Provider behavior와 model availability가 바뀔 수 있으므로 날짜도 기록합니다.
Workflow 검증 후 Endpoint 선택하기
Typed output item, Responses streaming events 또는 더 큰 Responses Tool workflow를 사용한다면 Responses가 적합합니다. Message-based integration이 안정적이고 route가 response_format을 지원하면 Chat Completions를 사용할 수 있습니다. 자세한 비교는 Responses API와 Chat Completions를 참고하십시오.
어느 endpoint를 선택해도 다음 원칙은 같습니다.
- Versioned JSON Schema Source of Truth를 하나만 유지합니다.
- Endpoint wrapper만 바꾸고 의미는 바꾸지 않습니다.
- Completed response를 애플리케이션에서 검증합니다.
- 구조 검증 후 deterministic business rule을 적용합니다.
- Parsing, refusal, truncation, compatibility failure를 별도로 관찰합니다.
Client migration은 OpenAI-Compatible API 가이드에서 시작하고, 불완전한 event 처리는 AI API Streaming 가이드를 참고하십시오. 모델 및 가격에서 현재 surface를 확인한 뒤 API Key가 실제 사용할 route에서 Structured Outputs를 검증해야 합니다.
Structured Outputs는 모델 응답을 신뢰 가능한 application interface로 좁혀 줍니다. 그러나 사실 검증, authorization, billing logic을 대체하지는 않습니다. “OpenAI-compatible”은 Base URL에서 추론할 보장이 아니라 feature별로 시험할 주장입니다.