구조화 출력
JSON 스키마로 응답 형식을 고정한다
response_format 을 쓰면 자유 형식 텍스트 대신, 여러분이 정의한 스키마에 맞는 JSON 을 모델이 돌려주게 할 수 있습니다. 응답을 코드에서 파싱한다면 항상 쓸 가치가 있습니다 — "마크다운으로 감싸서 왔다", "필드 하나를 멋대로 만들어냈다" 같은 오류를 통째로 없애줍니다.
사용법
type 을 json_schema 로 설정하고 스키마에 name 을 붙입니다.
{
"model": "google/gemma-4-26b-a4b",
"messages": [
{ "role": "user", "content": "도시와 기온을 추출해줘: 오늘 부산은 21도야." }
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "weather",
"strict": true,
"schema": {
"type": "object",
"properties": {
"city": { "type": "string" },
"temperature_c": { "type": "number" }
},
"required": ["city", "temperature_c"],
"additionalProperties": false
}
}
}
}응답의 message.content 는 스키마에 맞는 JSON 문자열입니다.
{ "city": "Busan", "temperature_c": 21 }여전히 문자열이므로 직접 JSON.parse 해야 합니다. 우리가 대신 디코딩해주지 않는 이유는, 그렇게 하면 모델이 잘못된 JSON 을 만들었을 때 그 사실이 조용히 묻히기 때문입니다.
모델 지원 여부
우리 카탈로그의 두 모델 모두 supported_parameters 에 response_format 을 포함합니다.
curl -s "https://openrouter.myip.co.kr/api/v1/models" | jq '.data[] | {id, supported_parameters}'supported_parameters 에 없는 모델에 response_format 을 보내는 것 자체를 우리가 막지는 않습니다 — 그 필드를 지킬지, 무시할지, 오류를 낼지는 서빙 엔진이 정합니다. 지원하지 않는 후보를 조용히 평문으로 응답시키는 대신 체인에서 아예 빼고 싶다면 provider.require_parameters: true 를 쓰세요(Provider 라우팅).
스트리밍
response_format 은 stream: true 와도 그대로 동작합니다. 모델이 JSON 텍스트를 토큰 단위로 delta.content 에 스트리밍하므로, 델타를 이어 붙였다가 finish_reason 이 오면 일반 텍스트 스트리밍과 똑같이 한 번에 파싱하세요. 스트리밍 을 보세요.
let json = '';
for await (const chunk of stream) {
json += chunk.choices[0]?.delta?.content ?? '';
}
const data = JSON.parse(json);청크마다 아직 다 안 온 버퍼를 JSON.parse 하려 하지 마세요 — 스트림이 끝나기 전까지는 유효한 JSON 이 아닙니다.
전체 예제
curl https://openrouter.myip.co.kr/api/v1/chat/completions \
-H "Authorization: Bearer $MYIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-26b-a4b",
"messages": [
{"role": "user", "content": "도시와 기온을 추출해줘: 오늘 부산은 21도야."}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "weather",
"strict": true,
"schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"temperature_c": {"type": "number"}
},
"required": ["city", "temperature_c"],
"additionalProperties": false
}
}
}
}'잘 쓰는 법
- 스키마 안에 설명을 적으세요. 속성에
"description": "ISO 4217 통화 코드"를 붙이면 필드 이름만 있을 때보다 모델을 더 잘 이끕니다. - 호출하는 엔진이 지원한다면
strict: true를 쓰세요. 다만 그 모델로 직접 검증하기 전까지는 "항상 지켜진다"가 아니라 "대체로 지켜진다"로 취급하세요. - 가능하면 스키마를 평평하게 유지하세요. 깊은 중첩이나
oneOf,$ref같은 드문 JSON Schema 기능은 엔진의 strict 모드가 부분적으로만 지원할 가능성이 높습니다. - 그래도 검증하세요. 문자열을 파싱한 뒤, 신뢰하기 전에 여러분의 코드에서(
ajv,zod등으로) 스키마와 대조해 검증하세요.response_format은 형식이 어긋난 출력을 줄여줄 뿐, 그것을 처리할 필요를 없애주지는 않습니다.
오류
| 상황 | 결과 |
|---|---|
모델이 response_format 을 지원하지 않음 | 엔진마다 다릅니다 — 대개 그 필드를 무시하고 오류 없이 평문을 돌려줍니다 |
json_schema 가 유효한 JSON Schema 가 아님 | 업스트림 호출이 실패하고 502 provider_error 로 드러나며, 업스트림 메시지가 metadata 에 실립니다 |
strict: true 인데도 스키마와 맞지 않는 텍스트가 옴 | 우리가 잡아내지 않습니다 — 파싱한 객체를 직접 검증하세요 |
일반적인 오류 형식은 오류와 디버깅 을 보세요.
마지막 수정 2026. 9. 5.