프롬프팅
verified · type: how-to · verified: 2026-07 · review: 90d · updated: 2026-07-25 · [prompting, api]

구조화 출력 강제하기

모델 응답을 파싱 가능한 형태(대개 JSON)로 받아내야 할 때. 어떤 방법을 고를지, 스키마에서 뭐가 막히는지, 파싱이 깨질 때 뭘 하는지.


무엇을 언제 쓰나

방법 강제 수준 쓸 때 안 쓸 때
JSON outputs (output_config.format) 하드 (constrained decoding) 응답 전체가 스키마여야 함. 데이터 추출, 최종 JSON 응답 자유 산문이 섞여야 함
Strict tool use (strict: true) 하드 (constrained decoding) 툴 호출의 파라미터를 정확히 강제. 신뢰성 있는 tool call 응답 자체를 구조화하고 싶음 → JSON outputs
XML 태그 (<result>...) 소프트 (스티어링만) 약한 구조 + 자유 산문 혼합, 여러 필드 구분 기계 파싱 신뢰성 필요 → 위 둘
  • 세 방법은 배타적이 아니다. agentic 워크플로우에서 JSON outputs + strict tool use 를 한 요청에 병용할 수 있다 (verified 2026-07): "파라미터가 보장된 툴 호출"과 "구조화된 최종 응답"을 동시에.
  • XML 태그는 constrained decoding 이 아니다. 모델이 태그를 빠뜨릴 수 있으니 파서는 관용적으로 짜라.
  • 예전에 assistant prefill({"role":"assistant","content":"{"})로 JSON 을 강제하던 패턴은 현행 Claude 에서 400 에러다 (§7). structured outputs / tool calling / XML 로 대체하라.

Anthropic — JSON outputs

응답 전체를 스키마에 맞춘다. 동작 원리는 grammar-constrained sampling(constrained decoding): 샘플링 시점에 문법으로 제약되어 스키마 위반이 원천 불가능하다. JSON.parse 에러가 나지 않고 재시도가 필요 없다.

from anthropic import Anthropic
client = Anthropic()

schema = {"type": "object",
    "properties": {"name": {"type": "string"},
                   "priority": {"type": "string", "enum": ["low", "high"]}},
    "required": ["name", "priority"],
    "additionalProperties": False}   # 모든 object 에 필수

resp = client.messages.create(model="claude-opus-5", max_tokens=1024,
    output_config={"format": {"type": "json_schema", "schema": schema}},
    messages=[{"role": "user", "content": "회의록에서 액션아이템 뽑아줘: ..."}])
  • 모델 지원: Claude 4.5 and later + Mythos Preview (verified 2026-07). Vertex AI 가 지원 폭이 가장 넓다.
  • 구버전 top-level output_format 파라미터와 베타 헤더 structured-outputs-2025-11-13output_config.format 로 이동했다. 구 문법도 전환기 동안은 동작하지만 새 코드는 output_config 를 써라.

Pydantic / Zod 연동

파싱까지 SDK 가 해준다.

from pydantic import BaseModel
class ActionItem(BaseModel):
    name: str
    priority: str

resp = client.messages.parse(model="claude-opus-5", max_tokens=1024,
    output_format=ActionItem, messages=[...])
item = resp.parsed_output   # ActionItem 인스턴스

TypeScript 는 zodOutputFormat(schema)output_config.format 에 넣고 resp.parsed_output 을 받는다.


Anthropic — 스키마 제약 (400 유발 지점)

모든 object 에 additionalProperties: falserequired반드시 있어야 한다. 아래 미지원 기능을 넣으면 400 이다.

지원 미지원 (400)
타입 object/array/string/integer/number/boolean/null
enum 스칼라(문자열·숫자·bool·null) enum 안 복합 타입
조합 anyOf, allOf, const allOf + $ref 조합
참조 내부 $ref/$def/definitions external $ref(http://…)
재귀 재귀 스키마 전면 미지원
수치 제약 minimum/maximum/multipleOf
문자열 제약 string formats(date, email, uri, uuid …) minLength/maxLength
배열 제약 minItems 0 또는 1 그 외 minItems 값, maxItems
  • 재귀 스키마가 필요하면 (트리·중첩 댓글 등) Anthropic JSON outputs 로는 못 한다. 깊이를 고정한 평탄화 스키마로 바꾸거나 XML/후처리로 우회하라.
  • 값 범위·길이·정규식은 스키마가 강제하지 못한다 → 앱단에서 검증하라 (Pydantic validator 등).

Anthropic — Strict tool use

툴의 이름과 입력 파라미터를 스키마에 강제한다. strict 없이는 모델이 enum 자리에 임의 문자열을, 정수 자리에 "2" 를 넣는 등 타입 불일치가 생길 수 있다.

tools=[{"name": "create_ticket", "description": "Create a support ticket.",
    "strict": True,                       # top-level 필드 (tool_choice 아님)
    "input_schema": {"type": "object",
        "properties": {"title": {"type": "string"},
                       "severity": {"type": "string", "enum": ["p1", "p2"]}},
        "required": ["title", "severity"], "additionalProperties": False}}]

스키마 제약은 JSON outputs 와 동일하다.


함정 (gotcha)

  • max_tokens 절단: constrained decoding 은 "유효한 JSON"을 보장하지만 "완결된 JSON"을 보장하지 않는다. max_tokens 에 걸리면 중간에서 잘려 파싱 불가다. 응답의 stop_reason == "max_tokens"반드시 가드하고, 넉넉히 잡거나 스트리밍하라 (§7: 비스트리밍 ~16000, 128K 출력은 스트리밍 필수).
  • 거절(refusal): stop_reason == "refusal" 이면 스키마에 맞는 본문이 안 온다. 이때만 stop_details 가 채워지고 다른 경우엔 null 이니 접근 전에 가드하라.
  • 문법 컴파일 지연 & 24시간 캐시: 첫 요청은 문법 컴파일로 지연이 붙는다. 컴파일된 문법은 마지막 사용 기준 약 24시간 캐시된다 (재확인 필요 — 정확 TTL 미검증). 캐시는 스키마 구조나 (병용 시) 툴 셋을 바꾸면 무효화되지만, 툴/필드의 name·description 만 바꾸면 유지된다.
  • prompt cache 무효화: output_config.format 을 바꾸면 그 스레드의 prompt cache 가 깨진다. 형식을 자주 토글하지 마라. → prompt-caching
  • format-via-example 안티패턴: "이렇게 생긴 JSON 줘" 하고 예시만 던지면 구조가 다른 입력에서 파서가 깨진다. 예시 대신 스키마를 명시하고 structured outputs 를 써라.

다른 프로바이더 (비교용)

아래 OpenAI 값은 aggregator 출처 [MED]. openai.com 공식 페이지가 확인 안 됨.

  • OpenAI: response_format{type:"json_schema", ..., strict:true}. 모든 필드가 required (optional 은 anyOfnull 유니온으로 우회), 모든 object 에 additionalProperties:false, 중첩 최대 5단계 (재확인 필요), root 를 anyOf 로 두는 것 불가. pattern·길이·수치·format 은 강제 안 함.
  • Anthropic 은 재귀를 전면 미지원인데 OpenAI 는 제한적 재귀(CFG)가 된다는 서술이 있으나 엇갈려서 미확정 (재확인 필요). 상세 대조는 openai-api-parity.

참고

출처: Anthropic 사실은 first-party [HIGH]·규격 §7 기준. OpenAI 비교는 aggregator [MED]. 원본 → 2026-07-25-structured-output-antipatterns