LLM의 응답이 개발자가 정한 JSON 스키마를 반드시 따르도록 강제하는 기능이다. 영수증에서 금액 뽑기, 문의 분류, 에이전트끼리 결과 넘기기처럼 AI 출력을 사람이 아니라 코드가 바로 받아 처리할 때 쓴다.
| 방식 | 보장하는 것 | 남는 문제 |
|---|---|---|
| 프롬프트로 부탁 | 없음 | 인사말·코드 블록 기호가 붙고 필드가 빠진다 |
| JSON 모드 | 문법이 맞는 JSON | 어떤 키가 들어가는지는 보장하지 않는다 |
| 구조화된 출력 | 스키마 그대로 | 내용이 맞는지는 따로 확인해야 한다 |
스키마를 문법으로 바꿔 두고, 모델이 글자를 하나씩 고를 때마다 형식을 깨는 후보를 지운다. 숫자가 와야 하는 자리에서는 숫자만 고를 수 있으니 실수할 수가 없다. 그래서 문법을 만드는 첫 요청은 조금 느리고, 그 뒤로는 만들어 둔 문법을 다시 쓴다.
| 어디서 | 켜는 방법 |
|---|---|
| OpenAI | text.format에 type: "json_schema", strict: true |
| Anthropic | 응답은 output_config.format, 도구 인자는 strict: true |
| Apple 기기 안 모델 | Swift 구조체에 @Generable |
먼저 스키마를 정하고 그 타입으로 결과를 받으면 된다. 파이썬이라면 Invoice 같은 Pydantic 클래스를 SDK에 넘기면 응답이 바로 그 객체로 돌아와서, 파싱하거나 타입을 바꾸는 코드를 따로 짤 필요가 없다. 에이전트를 만든다면 응답 형식과 도구 인자를 둘 다 고정해 두면 된다.
금액 자리에 숫자가 들어가는 건 보장되지만, 그 숫자가 영수증에 적힌 값과 같은지는 따로 확인해야 한다. 형식이 깨지는 경우도 아직 남아 있다.
| 상황 | 대응 |
|---|---|
모델이 거절함 (refusal) | 거절 응답을 따로 처리한다 |
max_tokens에 걸려 잘림 | 토큰 한도를 넉넉히 준다 |
| 최솟값·길이 제한은 지원하지 않음 | 응답을 받은 뒤 코드에서 검사한다 |
| Claude에서 필드 순서·enum 대소문자가 바뀜 | 순서에 기대지 말고, enum은 대소문자를 무시하고 비교한다 |
형식을 엄격하게 묶을수록 수학 같은 추론 성능이 떨어진다는 연구가 있다. 복잡한 판단이 필요하면 먼저 자유롭게 생각하게 한 뒤 최종 결과만 스키마로 받는다. 아니면 짧은 근거 필드를 답 필드보다 앞에 둔다.
가장 흔한 쓰임은 문서나 대화에서 정해진 항목을 뽑아 데이터베이스나 다른 코드로 바로 넘기는 일이다. 에이전트를 만들 때는 도구 인자와 단계별 결과를 스키마로 고정해서, 중간에 형식이 깨져 작업이 멈추는 일을 막는다.
| 어디서 | 어떻게 쓰나 |
|---|---|
| 문서 데이터 추출 | 영수증·계약서·이력서에서 금액·날짜·이름을 필드별로 뽑아 바로 저장한다 |
| 문의·리뷰 분류 | 카테고리를 enum으로 정해 두고, 분류 결과를 그대로 담당 부서 배정 코드에 넘긴다 |
| 코딩·업무 에이전트 | 도구 인자와 단계별 결과를 스키마로 고정해 다음 단계가 파싱 오류 없이 이어받게 한다 |
| 앱 안의 AI 기능 | 요약·추천 결과를 정해진 타입으로 받아 화면에 바로 띄운다 |