logo

구조화된 출력

JSON 형식과 Pydantic 모델 정의를 익히고, JSON 모드와 JSON Schema로 응답을 받는다.

구조화된 출력

  • 구조화된 출력: 프로그램이 처리할 수 있도록 필드·자료형·허용 값 등을 정한 응답
  • JSON: 문자열·숫자·배열·객체 등으로 데이터를 표현하는 형식
  • JSON Schema: JSON에 어떤 필드와 자료형, 값을 허용할지 정의하는 규칙

실습 준비

  • OpenRouter와 google/gemini-2.5-flash-lite 사용
  • 실행 전 모형 페이지에서 구조화 출력 지원 제공자 확인
  • API 키는 Colab 보안 비밀 OPENROUTER_API_KEY에 저장. 호출 시 사용량에 따라 크레딧 차감
!pip install -q openai "pydantic>=2,<3"
from google.colab import userdata
from openai import OpenAI

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=userdata.get("OPENROUTER_API_KEY"),
)
model = "google/gemini-2.5-flash-lite"

JSON으로만 받기

JSON 형식 이해하기

JSON(JavaScript Object Notation): 데이터를 텍스트로 주고받기 위한 형식. 다음은 감성 분석 결과를 표현한 작성 예시.

{
  "text": "생성형 AI 수업 너무 재밌어요.",
  "sentiment": "긍정",
  "keywords": ["재밌어요"]
}
  • 객체 {}: 이름과 값을 짝지은 데이터 묶음. 각 항목은 "이름": 값으로 쓰고 쉼표로 구분
    • text·sentiment·keywords는 항목의 이름인 키
  • 문자열 "": 텍스트를 큰따옴표로 감싼 값. 키에도 큰따옴표 사용
  • 배열 []: 값을 순서대로 나열한 목록. 위의 keywords는 문자열 하나가 들어 있는 배열
  • 그 밖의 값: 숫자, 참·거짓인 true·false, 값이 없음을 나타내는 null
  • 문법: 작은따옴표나 마지막 항목 뒤의 쉼표는 사용하지 않음

JSON은 데이터를 표현하는 형식. Python에서는 JSON 객체를 딕셔너리(dict), 배열을 리스트(list)로 표현하며, json.loads()로 JSON 문자열을 변환.

JSON 모드로 요청하기

문장을 긍정·부정·중립으로 평가하고 감정 표현을 추출하는 과제. 먼저 필드 규칙을 API에 전달하지 않고 JSON 형식만 지정.

review = "생성형 AI 수업 너무 재밌어요."
messages = [
    {
        "role": "system",
        "content": (
            "문장의 감성을 분석해 JSON 객체로만 답해라. "
            "text에는 원문, sentiment에는 긍정·부정·중립 중 하나, "
            "keywords에는 감정을 나타내는 표현의 목록을 넣어라."
        ),
    },
    {"role": "user", "content": review},
]

json_response = client.chat.completions.create(
    model=model,
    messages=messages,
    response_format={"type": "json_object"},  # JSON 모드. 프롬프트에도 JSON 출력 명시
    max_tokens=500,  # 짧은 감성 분석 결과의 생성 토큰 상한
    # JSON 모드를 지원하는 서버에만 요청
    extra_body={"provider": {"require_parameters": True}},
)
  • 응답에서 finish_reason이 stop이면 정상 완료
  • length면 토큰 상한을 조정하고 다시 호출
json_response.choices[0].finish_reason  # 응답 생성이 끝난 이유 확인
실행 결과
'stop'

응답을 JSON으로 파싱

import json

json.loads(json_response.choices[0].message.content)  # JSON 문자열을 Python 값으로 변환
실행 결과
{'text': '생성형 AI 수업 너무 재밌어요.',
 'sentiment': 'positive',
 'keywords': ['너무 재밌어요']}
  • 실행 결과는 유효한 JSON
  • sentiment는 요청한 세 한글 값 대신 positive를 반환(실행할 때마다 다를 수 있음)
    • JSON 모드에서는 필드 값의 범위까지 제한하지 않음

JSON Schema로 받기

  • 같은 감성 분석 과제에 필드·자료형·허용 값 규칙을 추가.
  • Pydantic으로 데이터 구조를 정의하고 JSON Schema로 변환해 API에 전달.

BaseModel로 데이터 구조 정의하기

  • BaseModel: 필드 정의를 바탕으로 데이터를 검증하고 JSON Schema를 만드는 Pydantic의 기본 클래스
    • 여기서 ‘모델’은 응답 데이터의 구조를 정의한 클래스
  • 정의 방법: class 클래스이름(BaseModel):로 클래스를 만들고, 들여쓴 본문에 필드이름: 자료형을 작성
  • SentimentAnalysis: 직접 정한 클래스 이름. 이 클래스에 감성 분석 결과의 규칙을 모아 정의
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field


class SentimentAnalysis(BaseModel):
    model_config = ConfigDict(extra="forbid")  # 정의하지 않은 필드 금지
    text: str  # 필드명은 text, 자료형은 문자열(str)
    sentiment: Literal["긍정", "부정", "중립"]  # sentiment는 긍정, 부정, 중립 중 하나
    keywords: list[str] = Field(description="원문에서 감정을 나타내는 표현 목록")
    # keywords는 문자열 리스트. description은 설명

sentiment_schema = SentimentAnalysis.model_json_schema()  # 정의한 구조를 JSON Schema로
sentiment_schema["required"]  # 필수 필드 목록
실행 결과
['text', 'sentiment', 'keywords']
  • 세 필드에는 기본값이 없으므로 모두 필수.

JSON 데이터 검증

텍스트로 된 JSON 데이터를 검증해 SentimentAnalysis 객체를 만들고, model_dump()로 Python 딕셔너리로 변환

sample_sentiment = SentimentAnalysis.model_validate_json(
    '{"text": "생성형 AI 수업 너무 재밌어요.", '
    '"sentiment": "긍정", "keywords": ["재밌어요"]}'
)
sample_sentiment.model_dump()
실행 결과
{'text': '생성형 AI 수업 너무 재밌어요.', 'sentiment': '긍정', 'keywords': ['재밌어요']}

JSON Schema로 전달하고 응답 검증하기

model_json_schema()로 만든 규칙을 response_format에 전달.

schema_response = client.chat.completions.create(
    model=model,
    messages=messages,
    response_format={
        "type": "json_schema",  # JSON Schema로 응답 구조 지정
        "json_schema": {
            "name": "sentiment_analysis",  # 스키마 이름
            "strict": True,  # 정의한 필드·자료형·허용 값에 맞춰 생성
            "schema": sentiment_schema,  # BaseModel에서 만든 JSON Schema 전달
        },
    },
    max_tokens=500,
    # JSON Schema를 지원하는 서버에만 요청
    extra_body={"provider": {"require_parameters": True}},
)

응답 생성이 끝난 이유 확인(stop이면 정상, length면 길이 한계에 도달)

schema_response.choices[0].finish_reason
실행 결과
'stop'

응답 JSON을 읽고 SentimentAnalysis의 필드 규칙으로 검증

sentiment_result = SentimentAnalysis.model_validate_json(
    schema_response.choices[0].message.content  # 모델이 생성한 JSON 문자열
)
sentiment_result.model_dump()  # 검증한 값을 Python 딕셔너리로 변환
실행 결과
{'text': '생성형 AI 수업 너무 재밌어요.', 'sentiment': '긍정', 'keywords': ['너무 재밌어요']}
  • 실행 결과: sentiment는 허용 값인 긍정, keywords는 문자열 목록으로 반환. 필수 필드와 자료형 검증 통과
  • 두 실습 모두 원문을 그대로 보존하고, 원문에 있는 ‘너무 재밌어요’를 감정 표현으로 추출

결과 확인

  • 구조 검증: 필수 필드·자료형·감성의 허용 값 확인
  • 내용 확인: 원문을 그대로 담았는지, 감성 판단이 원문에 맞는지, 키워드를 원문에서 추출했는지 대조
    • 스키마 검증은 필드 규칙을 검사하며 감성 판단의 정답을 판정하지 않음

참고: OpenRouter 구조화 출력, Pydantic 모델 검증

퀴즈

문제 1 / 3맞음: 0힌트: 0틀림: 0채점중: 0남음: 3

JSON 모드와 비교해 JSON Schema로 추가 지정할 수 있는 것은?

  • ○필수 필드와 각 필드의 자료형·허용 값
  • ○모델이 판단한 감성의 의미상 정답
  • ○감정 표현 추출의 내용상 정확성
  • ○같은 요청을 반복했을 때 문장까지 동일한 응답

퀴즈를 풀려면 대화형 기능을 불러와야 합니다.

Previous
인컨텍스트 학습