API로 고객 문의를 분석해 DB에 저장하는 스크립트를 만들었는데, 가끔 오류가 났습니다. 원인을 보니 클로드가 JSON 앞에 "분석 결과는 다음과 같습니다:"라는 문장을 붙인 경우였습니다. 사람이 읽기엔 친절하지만 프로그램에게는 오류입니다. 이 문제를 해결하는 것이 구조화 출력(Structured Outputs)입니다.
프롬프트만으로 JSON을 요청할 때의 문제
다음 문의를 분석해서 JSON으로만 답해. 필드: category, urgency, summary
대부분 잘 되지만, 대량으로 돌리면 이런 일이 생깁니다.
- JSON 앞뒤에 설명 문장이 붙음
- 코드 블록(```json)으로 감싸서 옴
- 필드 이름이 가끔 바뀜 (
urgency→priority) - 정해진 값 외의 값이 들어옴 (
"높음"대신"매우 높음")
1,000건 중 몇 건만 이래도 예외 처리 코드가 복잡해집니다.
구조화 출력: 스키마로 형식을 강제
구조화 출력은 요청에 JSON 스키마를 함께 보내, 응답이 그 스키마에 맞도록 강제하는 기능입니다. 요청의 output_config.format에 스키마를 지정합니다.
import json
import anthropic
client = anthropic.Anthropic()
schema = {
"type": "object",
"properties": {
"category": {"type": "string", "enum": ["배송", "교환", "환불", "기타"]},
"urgency": {"type": "string", "enum": ["높음", "보통", "낮음"]},
"summary": {"type": "string"},
},
"required": ["category", "urgency", "summary"],
"additionalProperties": False,
}
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{
"role": "user",
"content": "다음 고객 문의를 분석해 줘: 주문한 지 일주일이 넘었는데 아직 배송 출발도 안 했어요. 선물이라 이번 주 안에 받아야 해요.",
}],
)
text = "".join(b.text for b in response.content if b.type == "text")
data = json.loads(text)
print(data)
# {'category': '배송', 'urgency': '높음', 'summary': '...'}
enum으로 허용 값을 정하면 "매우 높음" 같은 값이 들어오지 않습니다.
파이썬 SDK의 parse로 더 간단하게
파이썬 SDK는 Pydantic 모델을 스키마로 쓰고 결과를 객체로 받는 messages.parse()를 제공합니다.
from typing import Literal
from pydantic import BaseModel
class Inquiry(BaseModel):
category: Literal["배송", "교환", "환불", "기타"]
urgency: Literal["높음", "보통", "낮음"]
summary: str
result = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_format=Inquiry,
messages=[{"role": "user", "content": f"다음 고객 문의를 분석해 줘: {inquiry_text}"}],
)
inquiry = result.parsed_output
print(inquiry.category, inquiry.urgency)
SDK 버전에 따라 파라미터 이름이 다를 수 있으니, 사용하는 버전의 공식 예제를 확인하세요. 핵심은 스키마를 코드로 정의하고, 검증된 객체로 받는다는 것입니다.
툴 입력에도 엄격 모드
툴 사용에서 클로드가 툴에 넘기는 인수도 툴 정의에 strict: true를 지정하면 스키마에 정확히 맞게 받을 수 있습니다. 툴을 호출하는 에이전트를 만든다면 함께 쓰면 좋습니다.
스키마 설계 요령
- 필드는 꼭 필요한 것만: 필드가 많을수록 모든 값을 채우느라 품질이 흩어질 수 있습니다.
- enum을 적극 활용: 분류 결과처럼 값이 정해진 필드는 반드시 enum으로.
- 설명(description) 추가: 각 필드가 무엇을 뜻하는지 스키마에 설명을 넣으면 정확도가 올라갑니다.
- "모름" 선택지 두기: 판단할 수 없는 경우를 위한 값(예:
"판단 불가")이 없으면, 클로드가 억지로 아무 값이나 고를 수 있습니다.
주의할 점
- 형식 보장 ≠ 내용 보장: 스키마는 맞아도 분류 자체가 틀릴 수 있습니다. 샘플을 뽑아 사람이 정확도를 확인하세요.
- 값 범위 검증: 숫자 필드라면 음수나 비현실적인 값이 들어오지 않는지 코드에서 확인합니다.
- 거절 응답 처리: 요청이 정책상 거절되는 경우 등, 응답 상태(
stop_reason)를 먼저 확인한 뒤 파싱하세요.
형식이 완벽할수록 내용 검증을
구조화 출력을 적용한 뒤 파싱 오류 처리 코드를 거의 다 지울 수 있었습니다. 예전에는 "JSON 앞의 문장 제거", "코드 블록 벗기기" 같은 방어 코드가 잔뜩 있었는데, 이제는 스키마 하나로 끝납니다. 대신 내용 검증에 더 신경 씁니다. 형식이 완벽하니 오히려 틀린 내용이 그럴듯해 보일 수 있기 때문입니다.
마무리
- 프롬프트만으로 JSON을 요청하면 대량 처리 시 형식이 가끔 어긋납니다.
output_config.format의 JSON 스키마나 SDK의parse로 형식을 강제하세요.- enum·설명·"판단 불가" 값을 활용하고, 내용의 정확성은 따로 검증합니다.
출처·참고 링크

