AI API를 활용할 때 가장 까다로운 문제 중 하나는 출력 형식의 일관성입니다. "JSON을 반환해줘"라고 요청했는데, 모델이 때로는 유효한 JSON을, 때로는 일반 텍스트를 반환한다면?
이번 튜토리얼에서는 Structured Output(구조화 출력)과 JSON Mode를 활용해 안정적으로 예측 가능한 데이터 구조를 얻는 방법을 다룹니다. HolySheep AI의 단일 API 키로 다양한 모델의 구조화 출력을 통합 관리하는 실전 방법을 소개합니다.
왜 구조화 출력이 필요한가?
실무에서 자주 마주치는 고통스러운 시나리오를 살펴보겠습니다:
시나리오 1: ParsingError로 인한 서버 장애
# ❌ JSON Mode 없이 AI 응답을 파싱하려 할 때 발생하는 오류
import openai
import json
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": "사용자 정보를 JSON으로 반환: 이름, 나이, 이메일"}]
)
모델이 반환한 텍스트
raw_text = response.choices[0].message.content
print(f"반환 타입: {type(raw_text)}") # <class 'str'>
print(f"원본 응답: {raw_text}")
❌ 파싱 실패 시나리오
"사용자 정보는 다음과 같습니다: {이름: '김철수', 나이: 30, ...}"
json.loads() 실행 시 JSONDecodeError 발생
try:
user_data = json.loads(raw_text)
except json.JSONDecodeError as e:
print(f"파싱 오류: {e}")
# 결과: {'이름': '김철수', '나이': 30, ...} — 키가 따옴표 없이 반환됨
시나리오 2: 응답 형식 불일치로 인한 데이터 무결성 문제
# ❌ 일관성 없는 응답 형식으로 인한 로직 오류
responses = []
여러 요청의 응답 형식이 제각각
response_1 = '{"status": "success", "data": {"id": 1}}'
response_2 = '{"success": true, "result": {"user_id": 1}}'
response_3 = '{"code": 200, "payload": {"userId": 1}}'
for resp in [response_1, response_2, response_3]:
data = json.loads(resp)
# ❌ 각각 다른 키 구조 -> 일관된 접근 불가
# data['data']['id'] vs data['result']['user_id'] vs data['payload']['userId']
print(f"응답: {resp}")
이러한 문제들을 Structured Output과 JSON Mode를 통해 원천적으로 해결할 수 있습니다.
Structured Output과 JSON Mode의 차이점
JSON Mode
- 역할: 응답을 유효한 JSON 형식으로 강제
- 스키마: 구조를 완전히 보장하지 않음
- 지원 모델: GPT-4, Claude, Gemini 등 대부분의 최신 모델
Structured Output (严格 구조화 출력)
- 역할: 사전 정의된 스키마를 100% 준수하는 출력 보장
- 스키마: Pydantic, JSON Schema 정의 시 정확한 구조 강제
- 지원 모델: GPT-4-1106-preview, Claude 3.5 Sonnet 이상, Gemini 1.5 Pro 이상
HolySheep AI에서 구조화 출력 사용하기
HolySheep AI는 지금 가입하면 단일 API 키로 다양한 모델의 구조화 출력을 동일한 인터페이스로 관리할 수 있습니다.
1. Python + OpenAI SDK (GPT-4o, GPT-4 Turbo)
# HolySheep AI - Structured Output 예제
pip install openai
from openai import OpenAI
from pydantic import BaseModel
from typing import List, Optional
응답 스키마 정의 (Pydantic)
class UserProfile(BaseModel):
user_id: int
username: str
email: str
preferences: Optional[dict] = None
created_at: str
class UserListResponse(BaseModel):
total: int
users: List[UserProfile]
page: int
HolySheep AI 클라이언트 초기화
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1" # HolySheep AI 엔드포인트
)
✅ Structured Output 사용 - Pydantic 스키마 전달
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "당신은 사용자 정보 관리 어시스턴트입니다."},
{"role": "user", "content": "테스트용 사용자 3명의 정보를 생성해주세요."}
],
response_format=UserListResponse # Pydantic 모델로 스키마 지정
)
✅ 자동 파싱 - 바로 Python 객체로 변환
result = response.choices[0].message.parsed
print(f"총 사용자 수: {result.total}")
print(f"첫 번째 사용자: {result.users[0].username}")
isinstance 확인
print(f"반환 타입: {type(result)}") # <class '__main__.UserListResponse'>
print(f"users 타입: {type(result.users)}") # <class 'list'>
2. JSON Mode (범용)
# HolySheep AI - JSON Mode 예제 (모든 모델 호환)
from openai import OpenAI
import json
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
✅ JSON Mode 활성화 - response_format 사용
response = client.chat.completions.create(
model="gpt-4o-mini", # 또는 "claude-3-5-sonnet-20240620"
messages=[
{"role": "system", "content": "응답은 반드시 유효한 JSON 형식으로만 반환하세요."},
{"role": "user", "content": """
다음 제품 정보를 JSON으로 반환해주세요:
- product_id: 정수
- name: 문자열
- price: 숫자
- in_stock: 불리언
- tags: 문자열 배열
"""}
],
response_format={"type": "json_object"}, # JSON Mode 활성화
max_tokens=500,
temperature=0.3
)
✅ 항상 유효한 JSON 반환 보장
raw_response = response.choices[0].message.content
product_data = json.loads(raw_response)
print(f"제품명: {product_data['name']}")
print(f"가격: {product_data['price']}")
print(f"재고: {product_data['in_stock']}")
JSON Schema와 함께 사용
response_with_schema = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "뉴스 기사를 분석해서 다음 스키마에 맞춰 JSON을 반환:\n{\n 'headline': str,\n 'sentiment': 'positive'|'negative'|'neutral',\n 'key_points': list[str],\n 'source': str\n}"}
],
response_format={"type": "json_object"},
temperature=0.1
)
3. JavaScript / Node.js
// HolySheep AI - JavaScript Structured Output
// npm install openai
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_HOLYSHEEP_API_KEY',
baseURL: 'https://api.holysheep.ai/v1'
});
// JSON Schema 정의
const jsonSchema = {
type: 'json_object',
schema: {
type: 'object',
properties: {
order_id: { type: 'string' },
customer: {
type: 'object',
properties: {
name: { type: 'string' },
email: { type: 'string' }
},
required: ['name', 'email']
},
items: {
type: 'array',
items: {
type: 'object',
properties: {
product_id: { type: 'string' },
quantity: { type: 'integer' },
price: { type: 'number' }
}
}
},
total_amount: { type: 'number' },
status: {
type: 'string',
enum: ['pending', 'processing', 'shipped', 'delivered']
}
},
required: ['order_id', 'customer', 'items', 'total_amount', 'status']
}
};
async function createOrderSummary() {
const completion = await client.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: '당신은 이커머스 주문 분석 시스템입니다.'
},
{
role: 'user',
content: '최근 7일간의 주문 데이터를 분석해서 요약해주세요.'
}
],
response_format: jsonSchema,
temperature: 0.1
});
const orderData = JSON.parse(completion.choices[0].message.content);
console.log('주문 ID:', orderData.order_id);
console.log('고객명:', orderData.customer.name);
console.log('총액:', orderData.total_amount);
console.log('상태:', orderData.status);
return orderData;
}
createOrderSummary().catch(console.error);
4. cURL
# HolySheep AI - cURL로 Structured Output 요청
HolySheep AI의 단일 엔드포인트로 모든 모델 지원
GPT-4o로 JSON Mode 요청
curl https://api.holysheep.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_HOLYSHEEP_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "응답은 유효한 JSON으로만 반환하세요."
},
{
"role": "user",
"content": "제품 리뷰를 감정 분석해서 {\"sentiment\": string, \"score\": number, \"keywords\": array} 형식으로 반환"
}
],
"response_format": {
"type": "json_object"
},
"max_tokens": 300,
"temperature": 0.1
}'
Claude로 JSON Mode 요청 (동일 엔드포인트)
curl https://api.holysheep.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_HOLYSHEEP_API_KEY" \
-d '{
"model": "claude-3-5-sonnet-20240620",
"messages": [
{
"role": "system",
"content": "You must respond only with valid JSON matching the schema."
},
{
"role": "user",
"content": "Analyze this code and return: {\"language\": string, \"complexity\": \"low\"|\"medium\"|\"high\", \"issues\": array}"
}
],
"response_format": {
"type": "json_object"
}
}'
모델별 구조화 출력 지원 현황
| 모델 | JSON Mode | Structured Output | 권장 사용 |
|---|---|---|---|
| GPT-4o | ✅ | ✅ (Pydantic) | 일반 추천 |
| GPT-4 Turbo | ✅ | ✅ | 비용 최적화 |
| Claude 3.5 Sonnet | ✅ | ✅ | 정확도 높은 작업 |
| Claude 3 Opus | ✅ | ✅ | 복잡한 분석 |
| Gemini 1.5 Pro | ✅ | ✅ | 장문 처리 |
| DeepSeek V3 | ✅ | ✅ | 비용 효율적 |
자주 발생하는 오류 해결
1. "400 Bad Request: response_format not supported"
# ❌ 해당 모델이 response_format을 지원하지 않는 경우
에러 메시지: "response_format is not supported for this model"
✅ 해결 방법 1: HolySheep AI에서 지원하는 모델로 변경
response = client.chat.completions.create(
model="gpt-4o", # Structured Output 지원 모델로 교체
messages=[...],
response_format={"type": "json_object"}
)
✅ 해결 방법 2: JSON Mode 대신 프롬프트 엔지니어링 사용
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "응답 형식: {\"key\": \"value\"}"},
{"role": "user", "content": "..."}
],
# response_format 없이 프롬프트로 구조 유도
)
2. "JSONDecodeError: Expecting value"
# ❌ JSON 파싱 실패 - 모델이 유효하지 않은 JSON 반환
import json
raw = response.choices[0].message.content
try:
data = json.loads(raw)
except json.JSONDecodeError:
# ❌ 흔한 원인들:
# 1. ```json 코드 블록으로 감싸서 반환
# 2. 텍스트 설명이 함께 반환
# 3. 불완전한 JSON (중간에 잘림)
# ✅ 해결 방법 1: 코드 블록 파싱
import re
json_match = re.search(r'``(?:json)?\s*(\{.*?\})\s*``', raw, re.DOTALL)
if json_match:
data = json.loads(json_match.group(1))
# ✅ 해결 방법 2: response_format 강제 설정
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
response_format={"type": "json_object"}, # 반드시 설정
max_tokens=1000 # 충분한 토큰 확보
)
# ✅ 해결 방법 3: HolySheep AI 사용 시 추가 검증
raw_content = response.choices[0].message.content
if raw_content.startswith('```'):
raw_content = raw_content.split('```')[1]
if raw_content.startswith('json'):
raw_content = raw_content[4:]
data = json.loads(raw_content.strip())
3. "ValidationError: Missing required field"
# ❌ Pydantic 모델의 필수 필드가 누락된 경우
에러: "ValidationError: Field required: user_id"
from pydantic import BaseModel, field_validator
class UserProfile(BaseModel):
user_id: int
username: str
email: str
@field_validator('user_id')
@classmethod
def validate_user_id(cls, v):
if v <= 0:
raise ValueError('user_id must be positive')
return v
✅ 해결 방법 1: Optional 필드로 변경
class UserProfile(BaseModel):
user_id: Optional[int] = None # 필수 -> 선택
username: str
email: Optional[str] = None
✅ 해결 방법 2: 재시도 로직 구현
def fetch_user_with_retry(client, max_retries=3):
for attempt in range(max_retries):
try:
response = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[...],
response_format=UserProfile
)
return response.choices[0].message.