LangChain 챗봇4장 · 대화형 챗봇과 메모리 전략6 / 22← 이전목차다음 →MetaContents

4장. 대화형 챗봇과 메모리 전략

with_structured_output 으로 틀째 받기

상담원에게 손님의 말을 줄글로 받아 적게 한 뒤 내가 카드에 옮겨 적을 수도 있고, 처음부터 빈 상담 카드를 건네며 "이 칸을 채워 주세요"라고 할 수도 있습니다. 뒤의 방법이 덜 틀립니다. 칸의 이름과 뜻이 상담원 눈앞에 있기 때문입니다. with_structured_output 은 모델에게 빈 카드(Pydantic 틀)를 건네는 방법입니다.

Pydantic 틀을 모델에 건네고, 모델이 틀의 칸을 채우고, TripRequest 객체로 돌려받는 세 걸음을 이은 흐름도

첫 상자에서 model.with_structured_output(TripRequest) 로 틀을 붙인 모델을 만듭니다. 이 모델을 부르면 LangChain 이 틀을 JSON 스키마(칸의 이름, 자료형, 설명, 조건을 적은 설계도)로 바꿔 함께 보내고, 모델은 그 칸에 맞춰 답을 쓰고, 돌아온 답은 검사를 거쳐 TripRequest 객체가 됩니다. 여기서 빈 상담 카드가 Pydantic 틀, 카드를 채워 돌려주는 상담원이 틀을 붙인 모델입니다. 비유와 다른 점은, 모델이 칸은 꼬박꼬박 채워도 그 안의 값(예: 1인당 예산과 전체 예산)을 잘못 읽을 수는 있다는 것입니다.

구조화된 출력을 받는 세 방법

방법쓰는 모양돌려받는 것값 검사어울리는 때
JsonOutputParserprompt | model | JsonOutputParser()사전하지 않는다칸이 자주 바뀌는 가벼운 일
PydanticOutputParserprompt | model | PydanticOutputParser(pydantic_object=틀)틀의 객체한다구조화된 출력을 지원하지 않는 모델
with_structured_outputprompt | model.with_structured_output(틀)틀의 객체한다OpenAI 처럼 지원하는 모델(이 장의 기본)

with_structured_output 의 method 인자는 기본값이 "json_schema"(스키마를 모델에 직접 건네는 방식)입니다. 이 교안의 langchain-openai 1.7 판에서 확인한 값이며, 따로 바꾸지 않습니다.

짧은 예시

모델을 부르지 않고, 모델에게 건네지는 스키마를 들여다봅니다. 그리고 틀을 붙인 모델이 돌려줄 결과를 PydanticOutputParser 로 흉내 냅니다.

Python
import json

from langchain_core.messages import AIMessage
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field


class TripRequest(BaseModel):
    """손님의 여행 조건"""
    destination: str = Field(description="여행 목적지")
    nights: int = Field(description="몇 박인지. 2박 3일이면 2", ge=1)


# model_json_schema: 틀을 JSON 스키마(사전)로 바꾼다. 모델에게는 이 설계도가 간다
schema = TripRequest.model_json_schema()
print(json.dumps(schema["properties"], ensure_ascii=False))
print("꼭 채울 칸:", schema["required"])

# 모델이 스키마대로 JSON 을 썼다고 보고, 틀의 객체로 바꾼다
parser = PydanticOutputParser(pydantic_object=TripRequest)
trip = parser.invoke(AIMessage('{"destination": "부산", "nights": 1}'))
print(repr(trip))
print("점(.)으로 꺼내기:", trip.destination, trip.nights)
출력
{"destination": {"description": "여행 목적지", "title": "Destination", "type": "string"}, "nights": {"description": "몇 박인지. 2박 3일이면 2", "minimum": 1, "title": "Nights", "type": "integer"}}
꼭 채울 칸: ['destination', 'nights']
TripRequest(destination='부산', nights=1)
점(.)으로 꺼내기: 부산 1

스키마에는 칸마다 자료형(type), 설명(description), 조건(minimum)이 들어 있습니다. ge=1 이 JSON 스키마에서는 minimum: 1 로 적힙니다. with_structured_output 으로 부른 모델도 마지막 줄처럼 TripRequest 객체를 돌려주므로, 사전의 ["destination"] 대신 trip.destination 처럼 점으로 꺼냅니다.

코드가 의미하는 것

이 코드는 상담원에게 건넬 빈 카드가 어떻게 생겼는지 펼쳐 보고, 상담원이 채워 돌려준 카드를 받는 일을 흉내 냅니다. 진짜 모델 대신 PydanticOutputParser 가 돌려받는 쪽을 맡습니다.

코드하는 일챗봇에서 뜻하는 것
TripRequest.model_json_schema()틀을 JSON 스키마 사전으로 바꾼다with_structured_output 이 모델에게 함께 보내는 설계도가 이것입니다. 모델은 파이썬 클래스가 아니라 이 설계도를 읽습니다
schema["properties"], schema["required"]칸마다의 설명과 꼭 채울 칸을 꺼낸다모델이 받는 것이 칸 이름, 자료형, 설명, 조건뿐이라는 것을 눈으로 확인합니다
PydanticOutputParser(pydantic_object=TripRequest)JSON 글을 틀의 객체로 바꾸는 파서를 만든다틀을 붙인 모델이 마지막에 하는 일을 대신합니다. 구조화된 출력을 지원하지 않는 모델에서는 이 파서를 직접 씁니다
trip.destination객체에서 점으로 값을 꺼낸다돌려받은 것이 사전이 아니라 틀의 객체라는 뜻입니다

결과에서 읽을 것은 두 가지입니다.

  • `ge=1` 이 `minimum: 1` 로 바뀌었다. 파이썬에서 적은 조건이 모델이 읽는 스키마의 말로 옮겨졌습니다. description 도 한국어 그대로 들어 있습니다.
  • 꼭 채울 칸은 둘. 기본값이 없는 destination 과 nights 가 required 에 들었습니다. 모델은 이 두 칸을 반드시 채워야 합니다.

주의할 점

  • with_structured_output 은 모델마다 지원 여부가 다릅니다. 1장의 가짜 모델(FakeListChatModel)에서 부르면 "지원하지 않는다"는 오류(NotImplementedError)가 납니다. 그래서 이 절의 예시는 파서로 흉내 냈고, 진짜 모델로는 실습 2에서 부릅니다.
  • 틀을 붙인 모델은 줄글 답을 쓰지 않습니다. 손님에게 보여 줄 말과 조건을 함께 받고 싶으면, 틀 안에 reply: str 같은 칸을 하나 더 둡니다.
← 이전 절4장 · Pydantic 으로 틀 정하기다음 절 →4장 · 대화 기억의 원리