PydanticOutputParser는 LangChain 프레임워크에서 제공하는 강력한 출력 파서로, 대형 언어 모델(LLM)의 텍스트 출력을 구조화된 Python 객체로 변환하는 데 사용됩니다. 이 파서는 Pydantic 라이브러리를 기반으로 동작하며, 사전에 정의된 데이터 스키마에 따라 LLM의 출력을 검증하고 매핑하여 개발자가 원하는 형식으로 데이터를 쉽게 활용할 수 있도록 돕습니다. 특히, 복잡한 텍스트 데이터를 JSON, 리스트, 또는 사용자 정의 클래스의 인스턴스로 변환하는 데 유용합니다.
주요 특징 및 기능
- 구조화된 데이터 변환
PydanticOutputParser는 LLM의 비구조화된 텍스트 출력을 JSON 객체, 리스트, 혹은 Pydantic 모델 기반의 사용자 정의 객체로 변환합니다. 이를 통해 데이터를 체계적으로 관리하고, 후속 처리나 분석 작업을 간소화할 수 있습니다. - Croatia 데이터 모델 정의**
Pydantic의 BaseModel 클래스를 사용하여 출력 데이터의 스키마를 정의합니다. 이 스키마에는 필드 이름, 데이터 타입, 필수 여부 등이 포함되며, 이를 통해 LLM의 출력이 기대한 형식을 충족하는지 검증합니다. 잘못된 데이터가 입력될 경우, Pydantic이 자동으로 오류를 감지하여 안정적인 데이터 처리를 보장합니다. - 자동 매핑 및 검증
정의된 스키마에 따라 LLM의 출력을 자동으로 매핑하고, 데이터의 유효성을 검증합니다. 예를 들어, 특정 필드가 문자열이어야 하거나 필수값이 누락되지 않도록 강제할 수 있습니다. 이 과정은 수작업으로 데이터를 처리하는 번거로움을 줄여줍니다. - 유연한 출력 형식 지정
PydanticOutputParser는 get_format_instructions() 메서드를 통해 LLM이 따라야 할 출력 형식 지침을 제공합니다. 이 지침은 JSON 스키마 형태로 제공되며, LLM이 구조화된 출력을 생성하도록 유도합니다.
PydanticOutputParser는 두 가지 주요 메서드를 통해 동작합니다:
- get_format_instructions()
이 메서드는 LLM이 출력해야 할 데이터의 형식과 구조를 설명하는 지침을 문자열 형태로 반환합니다. 예를 들어, JSON 스키마를 기반으로 필드 이름, 타입, 설명 등을 명시하여 LLM이 해당 형식에 맞는 출력을 생성하도록 안내합니다. 이 지침은 프롬프트에 포함되어 LLM의 출력 형식을 제어하는 데 중요한 역할을 합니다. - parse()
LLM이 생성한 텍스트 출력을 입력받아 이를 사전에 정의된 Pydantic 모델로 파싱합니다. 이 과정에서 출력이 스키마를 준수하는지 검증하고, 유효한 경우 해당 모델의 인스턴스로 변환합니다. 만약 출력이 스키마와 맞지 않으면, Pydantic이 오류를 발생시켜 문제를 즉시 파악할 수 있습니다.
예제: 이메일 내용 파싱
아래는 PydanticOutputParser를 사용하여 이메일 내용을 파싱하는 예제입니다. 이 예제에서는 이메일의 주요 정보를 추출하여 구조화된 형식으로 변환합니다.
이메일 본문
From: 김철수 (chulsoo.kim@bikecorporation.me)To: 이은채 (eunchae@teddyinternational.me)Subject: “ZENESIS” 자전거 유통 협력 및 미팅 일정 제안 안녕하세요, 이은채 대리님,저는 바이크코퍼레이션의 김철수 상무입니다. 최근 보도자료를 통해 귀사의 신규 자전거 “ZENESIS”에 대해 알게 되었습니다. 바이크코퍼레이션은 자전거 제조 및 유통 분야에서 혁신과 품질을 선도하는 기업으로, 이 분야에서의 장기적인 경험과 전문성을 가지고 있습니다.ZENESIS 모델에 대한 상세한 브로슈어를 요청드립니다. 특히 기술 사양, 배터리 성능, 그리고 디자인 측면에 대한 정보가 필요합니다. 이를 통해 저희가 제안할 유통 전략과 마케팅 계획을 보다 구체화할 수 있을 것입니다.또한, 협력 가능성을 더 깊이 논의하기 위해 다음 주 화요일(1월 15일) 오전 10시에 미팅을 제안합니다. 귀사 사무실에서 만나 이야기를 나눌 수 있을까요? 감사합니다. 김철수상무이사바이크코퍼레이션 |
출력 파서 없이 처리한 경우:
먼저, 출력 파서를 사용하지 않고 이메일 내용을 요약하는 코드를 살펴보겠습니다.
from langchain_core.prompts import PromptTemplate
from langchain_ollama import ChatOllama
llm_model = ChatOllama(model="exaone3.5", temperature=0)
prompt = PromptTemplate.from_template(
"다음의 이메일 내용 중 중요한 내용을 추출해 주세요.\n\n{email_conversation}"
)
chain = prompt | llm_model
response = chain.invoke({"email_conversation": email_conversation})
print(response.content)
이메일의 중요한 내용 요약:
발신자 및 수신자:
발신: 김철수 상무 (바이크코퍼레이션)
수신: 이은채 대리 (테디인터내셔널)
주요 목적:
“ZENESIS” 자전거 모델에 대한 정보 요청 (브로슈어, 특히 기술 사양, 배터리 성능, 디자인)
자전거 유통 협력 가능성 논의를 위한 미팅 제안
미팅 제안:
날짜: 1월 15일 화요일
시간: 오전 10시
장소: 테디인터내셔널 사무실
이러한 내용들이 협력 및 미팅 계획의 핵심입니다.
이 결과는 텍스트 형식으로 요약된 내용이지만, 구조화된 데이터로 활용하기에는 추가 처리가 필요합니다. 이를 해결하기 위해 PydanticOutputParser를 사용해 보겠습니다.
PydanticOutputParser를 사용한 구조화된 출력:
다음은 PydanticOutputParser를 활용하여 이메일의 주요 정보를 구조화된 객체로 변환하는 예제입니다.
from langchain_ollama import ChatOllama
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field
# Pydantic 모델 정의
class EmailSummary(BaseModel):
from_person: str = Field(description="메일을 보낸 사람의 이름")
from_email: str = Field(description="메일을 보낸 사람의 이메일 주소")
to_person: str = Field(description="메일을 받는 사람의 이름")
to_email: str = Field(description="메일을 받는 사람의 이메일 주소")
subject: str = Field(description="메일 제목")
summary: str = Field(description="메일 본문을 요약한 텍스트")
date: str = Field(description="메일 본문에 언급된 미팅 날짜와 시간")
# PydanticOutputParser 생성
parser = PydanticOutputParser(pydantic_object=EmailSummary)
# 프롬프트 정의
prompt = PromptTemplate.from_template("""
You are an assistant to make JSON format.
Please do not answer your opinion by adding ```.
Do not use quotes in strings within JSON.
QUESTION:
{question}
EMAIL CONVERSATION:
{email_conversation}
FORMAT:
Do not use quotes in strings within JSON.\n
{format}
""")
# 프롬프트에 포맷 지침 추가
prompt = prompt.partial(format=parser.get_format_instructions())
# LLM 모델 설정
llm_model = ChatOllama(model="exaone3.5", temperature=0)
# 체인 구성
chain = prompt | llm_model
# 체인 실행
result = chain.invoke({
"email_conversation": email_conversation,
"question": "이메일 내용 중 주요 내용을 추출해 주세요.",
})
# 결과 파싱
structured_output = parser.parse(result.content)
print(structured_output)
from_person=’김철수’ from_email=’chulsoo.kim@bikecorporation.me’ to_person=’이은채’ to_email=’eunchae@teddyinternational.me’ subject=’ZENESIS 자전거 유통 협력 및 미팅 일정 제안’ summary=’김철수 상무는 이은채 대리에게 바이크코퍼레이션의 자전거 제조 및 유통 전문성을 강조하며, ZENESIS 모델의 브로슈어 요청과 함께 기술 사양, 배터리 성능, 디자인 정보를 요청했습니다. 또한, 협력 가능성을 논의하기 위해 1월 15일 화요일 오전 10시에 미팅을 제안했습니다.’ date=’1월 15일 화요일 오전 10시’
이 결과는 EmailSummary 클래스의 인스턴스로 반환되며, 각 필드가 명확하게 구조화되어 있어 데이터베이스 저장, API 응답, 또는 추가 처리에 바로 활용할 수 있습니다.
출력 파서를 체인에 통합:
PydanticOutputParser를 체인에 직접 포함시켜 LLM의 출력을 바로 Pydantic 객체로 변환할 수 있습니다.
# 체인 구성 (파서 포함)
chain = prompt | llm_model | parser
# 체인 실행
response = chain.invoke({
"email_conversation": email_conversation,
"question": "이메일 내용 중 주요 내용을 추출해 주세요.",
})
print(response)
from_person=’김철수’ from_email=’chulsoo.kim@bikecorporation.me’ to_person=’이은채’ to_email=’eunchae@teddyinternational.me’ subject=’ZENESIS 자전거 유통 협력 및 미팅 일정 제안’ summary=’김철수 상무는 이은채 대리에게 바이크코퍼레이션의 자전거 제조 및 유통 전문성을 강조하며, ZENESIS 모델의 브로슈어 요청과 함께 기술 사양, 배터리 성능, 디자인 정보를 요청했습니다. 또한, 협력 가능성을 논의하기 위해 1월 15일 화요일 오전 10시에 미팅을 제안했습니다.’ date=’1월 15일 화요일 오전 10시’
이 방식은 별도의 파싱 단계를 생략하고, 체인 실행 결과로 바로 Pydantic 객체를 얻을 수 있어 더욱 간결합니다.
with_structured_output() 활용
LangChain의 with_structured_output() 메서드는 LLM의 출력을 구조화된 형식으로 반환하는 간편한 방법입니다. 이 메서드는 JSON 스키마 또는 Pydantic 모델을 입력으로 받아, LLM의 출력을 해당 형식으로 자동 변환합니다. 단, exaone3.5 모델은 구조화된 출력용 도구 호출 기능을 지원하지 않으므로, 이 예제에서는 llama3.2 모델을 사용합니다.
예제: with_structured_output() 사용
from langchain_ollama import ChatOllama
from pydantic import BaseModel, Field
# Pydantic 모델 정의
class EmailSummary(BaseModel):
from_person: str = Field(description="메일을 보낸 사람의 이름")
from_email: str = Field(description="메일을 보낸 사람의 이메일 주소")
to_person: str = Field(description="메일을 받는 사람의 이름")
to_email: str = Field(description="메일을 받는 사람의 이메일 주소")
subject: str = Field(description="메일 제목")
summary: str = Field(description="메일 본문을 요약한 텍스트")
date: str = Field(description="메일 본문에 언급된 미팅 날짜와 시간")
# LLM 모델 설정
llm_model_with_structured = ChatOllama(model="llama3.2", format="json").with_structured_output(EmailSummary)
# 실행
response = llm_model_with_structured.invoke(email_conversation)
print(response)
from_person=’kim, chulsoo.kim@bikecorporation.me’ from_email=’chulsoo.kim@bikecorporation.me’ to_person=’Lee Eun Chae, eunchae@teddyinternational.me’ to_email=’eunchae@teddyinternational.me’ subject=’ZENESIS 자전거 유통 협력 및 미팅 일정 제안’ summary=’바이크코퍼레이션의 김철수 상무가 이은채 대리님에게 ZENESIS 모델에 대한 브로슈어를 요청하여 discussion을 위한 미팅 연장을 제안합니다.’ date=’2023-01-15 10:00:00′
with_structured_output()의 장점
- 간편성: 프롬프트나 출력 파서를 별도로 설정할 필요 없이, 모델에 스키마를 전달하면 자동으로 구조화된 출력을 생성합니다.
- 통합성: LangChain의 다른 기능과 쉽게 결합하여 복잡한 워크플로우를 구성할 수 있습니다.
- 자동화: 스키마 포맷팅과 출력 파싱 과정을 자동으로 처리하여 개발자의 작업 부담을 줄입니다.
주의사항
- 모델 지원 여부
with_structured_output()은 도구 호출(tool calling)을 지원하는 모델에서만 동작합니다. exaone3.5는 이 기능을 지원하지 않으므로, 대체 모델(예: llama3.2)을 사용해야 합니다. - 정확한 스키마 정의
Pydantic 모델의 Field에 포함된 description은 LLM이 데이터를 올바르게 추출하는 데 중요한 역할을 합니다. 따라서 설명은 명확하고 구체적이어야 합니다. - 프롬프트 최적화
PydanticOutputParser를 사용할 때는 get_format_instructions()를 통해 제공되는 지침을 프롬프트에 포함해야 하며, 프롬프트의 질문과 지침이 LLM의 출력에 직접적인 영향을 미칩니다.
PydanticOutputParser와 with_structured_output()은 LangChain에서 LLM의 출력을 구조화된 데이터로 변환하는 데 매우 유용한 도구입니다. PydanticOutputParser는 프롬프트와 파서를 통해 세밀한 제어가 가능하며, with_structured_output()은 간단하고 자동화된 방식으로 구조화된 출력을 제공합니다. 이를 활용하면 이메일 파싱, 데이터 추출, API 응답 생성 등 다양한 작업을 효율적으로 수행할 수 있습니다. 특히, Pydantic의 강력한 데이터 검증 기능을 통해 안정적이고 신뢰할 수 있는 데이터 처리가 가능하다는 점에서 큰 장점을 가집니다.