RAG & LANCHAIN (3)- Langchain 개념 & 문법 정리 ch.03 출력 파서(Output Parsers)

이영락·2024년 8월 27일
0

인공지능 공부

목록 보기
6/33

목차

CH03 출력 파서(Output Parsers)
01. Pydantic 출력 파서(PydanticOutputParser)
02. 콤마 구분자 출력 파서(CommaSeparatedListOutputParser)
03. 구조화된 출력 파서(StructuredOutputParser)
04. JSON 출력 파서(JsonOutputParser)
05. 데이터프레임 출력 파서(PandasDataFrameOutputParser)
06. 날짜 형식 출력 파서(DatetimeOutputParser)
07. 열거형 출력 파서(EnumOutputParser)
08. 출력 수정 파서(OutputFixingParser)

🏖️ CH03 출력 파서(Output Parsers)

출력 파서(Output Parser)

출력 파서(Output Parser)는 LangChain 프레임워크 내에서 언어 모델(LLM)의 출력을 보다 유용하고 구조화된 형태로 변환하는 중요한 컴포넌트이다. 이 컴포넌트는 LLM이 생성한 텍스트 출력을 특정한 형식으로 변환하여, 다양한 애플리케이션에서 효율적으로 활용할 수 있도록 돕는다.

출력파서의 역할

출력 파서는 LLM의 자유 형식 텍스트 출력을 받아 더 적합한 형식으로 변환하는 역할을 한다. 이를 통해 구조화된 데이터를 생성할 수 있으며, 특히 LangChain 프레임워크에서 다양한 종류의 출력 데이터를 파싱하고 처리하는 데 매우 유용하다. 출력 파서의 주요 역할은 다음과 같다:

  • 출력 형식 변환: LLM의 출력을 JSON, 리스트, 딕셔너리 등의 구조화된 데이터 형식으로 변환한다.
  • 데이터 처리 용이성 제공: 변환된 데이터를 후속 처리가 용이하도록 구조화한다.
  • 특정 요구 사항에 맞는 출력 제공: 애플리케이션에서 요구하는 특정 형식으로 출력을 맞춤화한다.

주요 특징

출력 파서는 LangChain 프레임워크 내에서 다양한 형태와 기능을 가지고 있으며, 각기 다른 요구에 맞춰 사용할 수 있다. 주요 특징은 다음과 같다:

  • 다양성: LangChain은 다양한 종류의 출력 파서를 제공하여, 다양한 출력 형식을 지원한다. 사용자는 필요에 따라 다양한 출력 파서를 선택하여 사용할 수 있다.
  • 스트리밍 지원: 많은 출력 파서들이 스트리밍을 지원한다. 이는 대용량 데이터 처리나 실시간 데이터 처리가 필요한 경우에 매우 유용하다.
  • 확장성: LangChain의 출력 파서는 최소한의 모듈부터 복잡한 모듈까지 확장 가능한 인터페이스를 제공한다. 이는 사용자가 필요에 따라 출력 파서를 확장하여 사용할 수 있게 한다.

출력파서의 이점

출력 파서를 사용함으로써 얻을 수 있는 주요 이점은 다음과 같다:

  • 구조화: LLM의 자유 형식 텍스트 출력을 구조화된 데이터로 변환한다. 이는 데이터가 정형화되어 후속 작업이나 분석이 용이해진다.
  • 일관성: 출력 파서를 사용하면 출력 형식이 일관되게 유지되므로, 데이터 처리 과정에서의 혼란을 줄일 수 있다. 이는 여러 출력 데이터를 일관되게 관리하는 데 매우 중요하다.
  • 유연성: 출력 파서는 다양한 출력 형식으로 변환이 가능하다. 예를 들어, JSON, 리스트, 딕셔너리 등의 형식으로 데이터를 변환하여, 다양한 애플리케이션 요구 사항을 충족시킬 수 있다.

출력파서를 사용할 때와 사용하지 않을 때

출력 파서를 사용하는 것과 사용하지 않는 경우의 차이를 명확히 이해하는 것이 중요하다. 다음은 출력 파서를 사용하지 않을 때와 사용하는 경우의 예시이다:

아무런 출력파서를 사용하지 않을 때의 예시

  • LLM의 출력을 그대로 받아 사용하는 경우, 자유 형식의 텍스트로 결과가 반환된다. 이러한 경우, 데이터는 구조화되지 않으며, 후속 작업이나 다른 시스템과의 연계에 있어 비효율적일 수 있다.

예시:

**중요 내용 추출:**

1. **발신자:** 김철수 (chulsoo.kim@bikecorporation.me)
2. **수신자:** 이은채 (eunchae@teddyinternational.me)
3. **제목:** "ZENESIS" 자전거 유통 협력 및 미팅 일정 제안
4. **요청 사항:**
   - ZENESIS 모델의 상세한 브로슈어 요청 (기술 사양, 배터리 성능, 디자인 정보 포함)
5. **미팅 제안:**
   - 날짜: 다음 주 화요일 (1월 15일)
   - 시간: 오전 10시
   - 장소: 귀사 사무실
6. **발신자 정보:**
   - 김철수, 상무이사, 바이크코퍼레이션

출력파서를 사용하여 JSON 형식으로 구조화된 출력 예시

  • 출력 파서를 사용하여 LLM의 출력을 JSON 형식으로 변환하면, 구조화된 데이터를 얻을 수 있다. 이러한 데이터는 애플리케이션이나 다른 시스템에서 쉽게 처리할 수 있으며, 분석 및 관리가 용이하다.

예시:

{
  "person": "김철수",
  "email": "chulsoo.kim@bikecorporation.me",
  "subject": "\"ZENESIS\" 자전거 유통 협력 및 미팅 일정 제안",
  "summary": "바이크코퍼레이션의 김철수 상무가 테디인터내셔널의 이은채 대리에게 신규 자전거 'ZENESIS' 모델에 대한 브로슈어 요청과 기술 사양, 배터리 성능, 디자인 정보 요청. 또한, 협력 논의를 위해 1월 15일 오전 10시에 미팅 제안.",
  "date": "1월 15일 오전 10시"
}

🏖️ 01. Pydantic 출력 파서(PydanticOutputParser)

PydanticOutputParser

PydanticOutputParser는 언어 모델(LLM)의 출력을 더 구조화된 정보로 변환하는 데 도움이 되는 클래스이다. 이 클래스는 단순한 텍스트 형태의 응답 대신, 사용자가 필요로 하는 정보를 명확하고 체계적인 형태로 제공할 수 있도록 설계되었다. 이를 통해 언어 모델의 출력을 특정 데이터 모델에 맞게 변환하여, 더 용이하게 정보를 처리하고 활용할 수 있다.

PydanticOutputParser의 핵심 메서드

PydanticOutputParser는 대부분의 OutputParser에 해당되는 두 가지 핵심 메서드를 구현해야 한다.

  1. get_format_instructions():

    • 이 메서드는 언어 모델이 출력해야 할 정보의 형식을 정의하는 지침(instruction)을 제공한다.
    • 예를 들어, 언어 모델이 출력해야 할 데이터의 필드와 그 형태를 설명하는 지침을 문자열로 반환한다.
    • 이 지침의 역할은 매우 중요하다. 설정된 지침에 따라 언어 모델은 출력을 구조화하고, 이를 특정 데이터 모델에 맞게 변환할 수 있다.
  2. parse():

    • 이 메서드는 언어 모델의 출력을 받아 이를 특정 구조로 분석하고 변환한다.
    • Pydantic과 같은 도구를 사용하여, 입력된 문자열을 사전 정의된 스키마에 따라 검증하고, 해당 스키마를 따르는 데이터 구조로 변환한다.

PydanticOutputParser 사용 예시

출력 파서를 사용하지 않는 경우

먼저, 출력 파서를 사용하지 않은 상태에서 이메일 본문에서 중요한 내용을 추출하는 예시를 살펴보자.

from itertools import chain
from langchain_core.prompts import PromptTemplate

# 프롬프트 템플릿을 정의합니다.
prompt = PromptTemplate.from_template(
    "다음의 이메일 내용중 중요한 내용을 추출해 주세요.\n\n{email_conversation}"
)

# 언어 모델을 설정합니다.
llm = ChatOpenAI(temperature=0, model_name="gpt-4o")

# 체인을 생성합니다.
chain = prompt | llm

# 이메일 본문 예시
email_conversation = """From: 김철수 (chulsoo.kim@bikecorporation.me)
To: 이은채 (eunchae@teddyinternational.me)
Subject: "ZENESIS" 자전거 유통 협력 및 미팅 일정 제안

안녕하세요, 이은채 대리님,

저는 바이크코퍼레이션의 김철수 상무입니다. 최근 보도자료를 통해 귀사의 신규 자전거 "ZENESIS"에 대해 알게 되었습니다. 바이크코퍼레이션은 자전거 제조 및 유통 분야에서 혁신과 품질을 선도하는 기업으로, 이 분야에서의 장기적인 경험과 전문성을 가지고 있습니다.

ZENESIS 모델에 대한 상세한 브로슈어를 요청드립니다. 특히 기술 사양, 배터리 성능, 그리고 디자인 측면에 대한 정보가 필요합니다. 이를 통해 저희가 제안할 유통 전략과 마케팅 계획을 보다 구체화할 수 있을 것입니다.

또한, 협력 가능성을 더 깊이 논의하기 위해 다음 주 화요일(1월 15일) 오전 10시에 미팅을 제안합니다. 귀사 사무실에서 만나 이야기를 나눌 수 있을까요?

감사합니다.

김철수
상무이사
바이크코퍼레이션
"""

# 체인을 실행하여 결과를 스트리밍합니다.
answer = chain.stream({"email_conversation": email_conversation})

# 스트리밍 응답을 받아서 출력합니다.
output = stream_response(answer, return_output=True)

이와 같은 방식으로 언어 모델의 출력을 사용하여 중요한 내용을 추출할 수 있다. 그러나 이 경우 출력은 단순한 텍스트 형태로 제공된다.

**중요한 내용 추출:**

1. **발신자:** 김철수 (chulsoo.kim@bikecorporation.me)
2. **수신자:** 이은채 (eunchae@teddyinternational.me)
3. **제목:** "ZENESIS" 자전거 유통 협력 및 미팅 일정 제안
4. **요청 사항:**
   - ZENESIS 모델에 대한 상세한 브로슈어 요청 (기술 사양, 배터리 성능, 디자인 정보 포함)
5. **미팅 제안:**
   - 날짜: 다음 주 화요일 (115)
   - 시간: 오전 10- 장소: 귀사 사무실

PydanticOutputParser를 사용한 경우

이제 PydanticOutputParser를 사용하여 위와 같은 이메일의 내용을 보다 구조화된 데이터로 변환하는 과정을 살펴보자. PydanticOutputParser는 다음과 같은 방식으로 사용될 수 있다:

from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field

# EmailSummary 클래스 정의
class EmailSummary(BaseModel):
    person: str = Field(description="메일을 보낸 사람")
    email: str = Field(description="메일을 보낸 사람의 이메일 주소")
    subject: str = Field(description="메일 제목")
    summary: str = Field(description="메일 본문을 요약한 텍스트")
    date: str = Field(description="메일 본문에 언급된 미팅 날짜와 시간")

# PydanticOutputParser 생성
parser = PydanticOutputParser(pydantic_object=EmailSummary)

# instruction 을 출력합니다.
print(parser.get_format_instructions())

위의 코드에서 EmailSummary 클래스는 Pydantic을 사용하여 정의되었으며, 이메일의 주요 정보를 구조화된 형태로 표현하기 위해 사용된다. PydanticOutputParser는 이 클래스를 기반으로, LLM의 출력을 자동으로 변환해준다.

# 프롬프트 템플릿을 정의합니다.
prompt = PromptTemplate.from_template(
    """
You are a helpful assistant. Please answer the following questions in KOREAN.

QUESTION:
{question}

EMAIL CONVERSATION:
{email_conversation}

FORMAT:
{format}
"""
)

# format 에 PydanticOutputParser의 부분 포맷팅(partial) 추가
prompt = prompt.partial(format=parser.get_format_instructions())

# chain 을 생성합니다.
chain = prompt | llm

# chain 을 실행하고 결과를 출력합니다.
response = chain.stream(
    {
        "email_conversation": email_conversation,
        "question": "이메일 내용중 주요 내용을 추출해 주세요.",
    }
)

# 결과는 JSON 형태로 출력됩니다.
output = stream_response(response, return_output=True)

이와 같이 PydanticOutputParser를 사용하면 LLM의 출력을 JSON 형식으로 쉽게 변환할 수 있다. 변환된 결과는 다음과 같이 구조화된 데이터를 제공한다:

{
  "person": "김철수",
  "email": "chulsoo.kim@bikecorporation.me",
  "subject": "\"ZENESIS\" 자전거 유통 협력 및 미팅 일정 제안",
  "summary": "바이크코퍼레이션의 김철수 상무가 테디인터내셔널의 이은채 대리에게 신규 자전거 \"ZENESIS\" 모델에 대한 상세한 브로슈어 요청 및 협력 논의를 위한 미팅 제안을 보냈습니다. 미팅은 다음 주 화요일(1월 15일) 오전 10시에 귀사 사무실에서 진행될 예정입니다.",
  "date": "다음 주 화요일(1월 15일) 오전 10시"
}

구조화된 출력 파싱

마지막으로, PydanticOutputParser를 사용하여 구조화된 출력으로 변환한 결과를 EmailSummary 객체로 변환할 수 있다.

# PydanticOutputParser 를 사용하여 결과를 파싱합니다.
structured_output = parser.parse(output)
print(structured_output)

위 코드의 실행 결과는 EmailSummary 객체로 변환된 데이터를 출력한다.

EmailSummary(
    person='김철수', 
    email='chulsoo.kim@bikecorporation.me', 
    subject='"ZENESIS" 자전거 유통 협력 및 미팅 일정 제안', 
    summary='바이크코퍼레이션의 김철수 상무가 테디인터내셔널의 이은채 대리에게 신규 자전거 "ZENESIS" 모델에 대한 상세한 브로슈어 요청 및 협력 논의를 위한 미팅 제안을 보냈습니다. 미팅은 다음 주 화요일(115) 오전 10시에 귀사 사무실에서 진행될 예정
  
겠습니까.',
    date='다음 주 화요일(1월 15일) 오전 10시'
)

위와 같이 PydanticOutputParser를 사용하여 얻은 출력은 EmailSummary 객체로 변환되어, 보다 구조화되고 일관된 데이터를 제공한다. 이는 데이터 처리가 필요한 후속 작업이나 다른 시스템과의 통합에 매우 유용하다.

PydanticOutputParser를 사용한 체인 생성

PydanticOutputParser를 사용하여 전체 체인을 재구성할 수 있다. 이때, 체인은 프롬프트, LLM, 그리고 출력 파서를 포함하며, 이를 통해 완전한 구조화된 출력을 생성할 수 있다.

# 출력 파서를 추가하여 전체 체인을 재구성합니다.
chain = prompt | llm | parser

# chain 을 실행하고 결과를 출력합니다.
response = chain.invoke(
    {
        "email_conversation": email_conversation,
        "question": "이메일 내용중 주요 내용을 추출해 주세요.",
    }
)

# 결과는 EmailSummary 객체 형태로 출력됩니다.
print(response)

이 경우, 결과는 EmailSummary 객체 형태로 반환되며, 다음과 같이 출력된다:

EmailSummary(
    person='김철수',
    email='chulsoo.kim@bikecorporation.me',
    subject='"ZENESIS" 자전거 유통 협력 및 미팅 일정 제안',
    summary='바이크코퍼레이션의 김철수 상무가 테디인터내셔널의 이은채 대리에게 신규 자전거 "ZENESIS" 모델에 대한 상세한 브로슈어 요청 및 협력 논의를 위한 미팅 제안을 보냈습니다. 미팅은 다음 주 화요일(1월 15일) 오전 10시에 귀사 사무실에서 진행될 예정입니다.',
    date='1월 15일 화요일 오전 10시'
)

with_structured_output() 함수 사용

with_structured_output(Pydantic) 함수는 언어 모델과 Pydantic을 연동하여, 출력을 Pydantic 객체로 변환하는 기능을 제공한다. 이를 통해 복잡한 데이터 처리를 더욱 쉽게 수행할 수 있다.

llm_with_structered = ChatOpenAI(
    temperature=0, model_name="gpt-4o"
).with_structured_output(EmailSummary)

# invoke() 함수를 호출하여 결과를 출력합니다.
answer = llm_with_structered.invoke(email_conversation)

# 결과는 EmailSummary 객체 형태로 출력됩니다.
print(answer)

이 방법을 사용하면, 출력이 자동으로 EmailSummary 객체로 변환된다:

EmailSummary(
    person='김철수',
    email='chulsoo.kim@bikecorporation.me',
    subject='"ZENESIS" 자전거 유통 협력 및 미팅 일정 제안',
    summary='김철수 상무는 바이크코퍼레이션에서 이은채 대리에게 ZENESIS 자전거 모델에 대한 브로슈어와 기술 사양, 배터리 성능, 디자인 정보를 요청하며, 유통 전략과 마케팅 계획을 구체화하기 위해 다음 주 화요일(1월 15일) 오전 10시에 미팅을 제안했습니다.',
    date='1월 15일 오전 10시'
)

참고 사항

한 가지 주의할 점은, with_structured_output() 함수는 stream() 기능을 지원하지 않는다. 이는 실시간 데이터 처리나 스트리밍이 필요한 경우, 해당 기능을 사용할 수 없음을 의미한다. 이 점을 고려하여 사용할 시나리오에 따라 적절한 방법을 선택하는 것이 중요하다.


🏖️ 02. 콤마 구분자 출력 파서(CommaSeparatedListOutputParser)

CommaSeparatedListOutputParser

CommaSeparatedListOutputParser는 여러 개의 항목을 쉼표로 구분된 목록 형태로 반환할 필요가 있을 때 유용한 출력 파서이다. 이 출력 파서를 사용하면, 입력된 데이터나 요청된 정보를 쉼표로 구분하여 명확하고 간결한 리스트 형태로 제공받을 수 있다. 이는 여러 데이터 포인트, 이름, 항목 또는 기타 값들을 나열할 때 효과적으로 정보를 정리하고, 사용자에게 전달할 수 있는 방법이다.

이 출력 파서는 정보를 구조화하고 가독성을 높이는 데 특히 유용하다. 특히, 데이터를 다루거나 리스트 형태의 결과를 요구하는 경우에 매우 효과적으로 활용될 수 있다.

CommaSeparatedListOutputParser 사용 예시

다음은 CommaSeparatedListOutputParser를 사용하여 데이터를 쉼표로 구분된 목록으로 반환하는 예시이다.

필요한 라이브러리 로드

먼저 필요한 라이브러리를 로드하고 환경을 설정한다.

from dotenv import load_dotenv

# 환경 변수를 로드합니다.
load_dotenv()
True

로그 설정 및 프로젝트 시작

LangSmith 추적을 설정하고, 프로젝트를 초기화한다.

from langchain_teddynote import logging

# 프로젝트 이름을 입력합니다.
logging.langsmith("CH03-OutputParser")
LangSmith 추적을 시작합니다.

CommaSeparatedListOutputParser 초기화

CommaSeparatedListOutputParser를 초기화하여 쉼표로 구분된 리스트를 생성할 수 있는 준비를 한다.

from langchain_core.output_parsers import CommaSeparatedListOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# 콤마로 구분된 리스트 출력 파서 초기화
output_parser = CommaSeparatedListOutputParser()

# 출력 형식 지침 가져오기
format_instructions = output_parser.get_format_instructions()

프롬프트 템플릿 설정

쉼표로 구분된 리스트를 생성하기 위한 프롬프트 템플릿을 설정한다. 예를 들어, 주어진 주제에 대해 다섯 가지 항목을 나열하는 템플릿을 설정할 수 있다.

# 프롬프트 템플릿 설정
prompt = PromptTemplate(
    template="List five {subject}.\n{format_instructions}",  # 주제에 대한 다섯 가지를 나열하라는 템플릿
    input_variables=["subject"],  # 입력 변수로 'subject' 사용
    partial_variables={"format_instructions": format_instructions},  # 부분 변수로 형식 지침 사용
)

모델 초기화 및 체인 생성

ChatOpenAI 모델을 초기화하고, 프롬프트, 모델, 출력 파서를 연결하여 체인을 생성한다.

# ChatOpenAI 모델 초기화
model = ChatOpenAI(temperature=0)

# 프롬프트, 모델, 출력 파서를 연결하여 체인 생성
chain = prompt | model | output_parser

체인 실행 및 결과 출력

생성된 체인을 실행하고, 결과를 출력한다. 예를 들어, '대한민국 관광명소'를 주제로 다섯 가지 항목을 나열해본다.

# "대한민국 관광명소"에 대한 체인 호출 실행
result = chain.invoke({"subject": "대한민국 관광명소"})

# 결과 출력
print(result)

실행 결과는 다음과 같이 쉼표로 구분된 리스트로 반환된다:

['경복궁', '인사동', '부산 해운대해수욕장', '제주도', '남산타워']

스트림 사용하여 반복 처리

chain.stream을 사용하여 스트림 형식으로 결과를 처리할 수도 있다. 이 방법은 각 항목이 개별적으로 반환될 때 유용하다.

# "대한민국 관광명소"에 대한 스트림 호출 실행
for s in chain.stream({"subject": "대한민국 관광명소"}):
    print(s)  # 스트림의 내용을 출력합니다.

스트림을 통해 반환된 결과는 다음과 같다:

['경복궁']
['인사동']
['부산 해운대해수욕장']
['제주도']
['남산타워']

이와 같은 방식으로 CommaSeparatedListOutputParser를 활용하면, 데이터를 명확하고 간결하게 나열할 수 있으며, 특히 리스트 형태의 출력을 요구하는 경우에 매우 유용하게 사용할 수 있다.


🏖️ 03. 구조화된 출력 파서(StructuredOutputParser)

StructuredOutputParser

StructuredOutputParser는 언어 모델(LLM)의 출력을 dict 형식으로 구성하여 여러 필드를 key/value 쌍으로 반환하는 데 사용되는 출력 파서이다. Pydantic/JSON 파서에 비해 덜 강력하지만, 로컬 모델이나 인텔리전스가 낮은 모델에서 유용하게 사용할 수 있다. 특히 Pydantic 파서가 동작하지 않는 경우, StructuredOutputParser는 좋은 대안이 된다.

StructuredOutputParser 사용 예시

응답 스키마 정의 및 파서 초기화

먼저, ResponseSchema 클래스를 사용하여 사용자의 질문에 대한 답변과 그 출처를 포함하는 응답 스키마를 정의한다. 그런 다음, StructuredOutputParser를 초기화하여 정의된 스키마에 따라 출력을 구조화한다.

from langchain.output_parsers import ResponseSchema, StructuredOutputParser

# 응답 스키마 정의
response_schemas = [
    ResponseSchema(name="answer", description="사용자의 질문에 대한 답변"),
    ResponseSchema(
        name="source",
        description="사용자의 질문에 답하기 위해 사용된 출처나 웹사이트 주소",
    ),
]

# 구조화된 출력 파서 초기화
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

프롬프트 템플릿 및 모델 설정

응답이 어떻게 포맷되어야 하는지에 대한 지시사항을 포함한 프롬프트 템플릿을 설정하고, 모델과 출력 파서를 연결하여 체인을 생성한다.

from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# 출력 형식 지침 가져오기
format_instructions = output_parser.get_format_instructions()

# 프롬프트 템플릿 설정
prompt = PromptTemplate(
    template="answer the user's question as best as possible.\n{format_instructions}\n{question}",
    input_variables=["question"],
    partial_variables={"format_instructions": format_instructions},
)

# 모델 초기화 및 체인 생성
model = ChatOpenAI(temperature=0)
chain = prompt | model | output_parser

체인 실행 및 결과 출력

체인을 실행하여 예를 들어 '대한민국의 수도'에 대해 질문하고, 결과를 출력한다.

# 대한민국의 수도에 대한 질문 실행
result = chain.invoke({"question": "대한민국의 수도는 어디인가요?"})
print(result)

결과는 다음과 같이 dict 형식으로 반환된다:

{'answer': '서울', 'source': 'https://ko.wikipedia.org/wiki/%EC%84%9C%EC%9A%B8'}

스트림을 통한 반복 처리

chain.stream 메소드를 사용하여 스트림 형식으로 결과를 처리할 수도 있다.

# 스트림을 사용한 결과 출력
for s in chain.stream({"question": "세종대왕의 업적은 무엇인가요?"}):
    print(s)

스트림을 통해 각 항목이 개별적으로 반환된다:

{'answer': '세종대왕은 한글을 창제하고 문화를 발전시키는 등 다양한 업적을 가지고 있습니다.', 'source': 'https://ko.wikipedia.org/wiki/%EC%84%B8%EC%A2%85%EB%8C%80%EC%99%95'}

🏖️ 04. JSON 출력 파서(JsonOutputParser)

JsonOutputParser

JsonOutputParser는 사용자가 지정한 JSON 스키마에 맞춰 LLM에서 데이터를 조회하고 결과를 도출하는 출력 파서이다. 이 파서는 LLM이 데이터를 정확하게 처리하여 원하는 형태의 JSON을 생성하도록 돕는다. 이때, 모델의 인텔리전스가 충분히 높아야 데이터 처리 및 JSON 생성이 원활히 이루어진다.

JsonOutputParser 사용 예시

파서 초기화 및 데이터 구조 정의

먼저, LLM에서 생성할 JSON 데이터 구조를 정의하고 JsonOutputParser를 초기화한다.

from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.output_parsers import JsonOutputParser

# 원하는 데이터 구조 정의
class Topic(BaseModel):
    description: str = Field(description="주제에 대한 간결한 설명")
    hashtags: str = Field(description="해시태그 형식의 키워드(2개 이상)")

# JSON 출력 파서 초기화
parser = JsonOutputParser(pydantic_object=Topic)

프롬프트 템플릿 및 모델 설정

ChatPromptTemplate을 사용하여 프롬프트 템플릿을 설정하고, 모델과 파서를 연결하여 체인을 구성한다.

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# 프롬프트 템플릿 설정
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "당신은 친절한 AI 어시스턴트입니다. 질문에 간결하게 답변하세요."),
        ("user", "#Format: {format_instructions}\n\n#Question: {question}"),
    ]
)

# 지시사항을 프롬프트에 주입
prompt = prompt.partial(format_instructions=parser.get_format_instructions())

# 모델 초기화 및 체인 구성
model = ChatOpenAI(temperature=0, model_name="gpt-4o")
chain = prompt | model | parser

체인 실행 및 결과 출력

체인을 실행하여 예를 들어 '지구 온난화의 심각성'에 대해 질문하고, 결과를 JSON 형식으로 출력한다.

# 질문 실행
response = chain.invoke({"question": "지구 온난화의 심각성에 대해 알려주세요."})

# 결과 출력
print(response)

결과는 다음과 같이 JSON 형식으로 반환된다:

{'description': '지구 온난화는 지구의 평균 기온이 지속적으로 상승하는 현상으로, 이는 주로 인간 활동에 의해 발생하는 온실가스 배출로 인해 발생합니다.', 'hashtags': '#지구온난화 #기후변화 #온실가스'}

Pydantic 없이 사용하기

Pydantic 없이도 JsonOutputParser를 사용할 수 있으며, 이 경우에도 JSON 형식으로 출력을 받을 수 있다.

# Pydantic 없이 JSON 출력 파서 사용
parser = JsonOutputParser()

# 질문 실행 및 출력 확인
response = chain.invoke({"question": "지구 온난화에 대해 알려주세요. 온난화에 대한 설명은 `description`에, 관련 키워드는 `hashtags`에 담아주세요."})
print(response)

이 경우, 결과는 다음과 같이 출력된다:

{'description': '지구 온난화는 대기 중 온실가스 농도의 증가로 인해 지구의 평균 기온이 상승하는 현상을 말합니다.', 'hashtags': ['#지구온난화', '#기후변화', '#온실가스', '#해수면상승', '#환경문제']}

🏖️ 06. 날짜 형식 출력 파서(DatetimeOutputParser)

DatetimeOutputParser

DatetimeOutputParser는 LLM의 출력을 datetime 형식으로 파싱하는 데 사용되는 출력 파서이다. 이를 통해 날짜 및 시간을 특정 형식에 맞춰 반환할 수 있다.

DatetimeOutputParser 사용 예시

파서 초기화 및 설정

먼저 DatetimeOutputParser를 초기화하고, 날짜 형식을 지정한다.

from langchain.output_parsers import DatetimeOutputParser

# 날짜 및 시간 출력 파서 초기화
output_parser = DatetimeOutputParser()
output_parser.format = "%Y-%m-%d"

프롬프트 템플릿 설정

사용자 질문에 대한 답변을 위한 프롬프트 템플릿을 설정하고, 날짜 형식 지침을 템플릿에 적용한다.

from langchain.prompts import PromptTemplate

# 사용자 질문에 대한 답변 템플릿
template = """Answer the users question:\n\n#Format Instructions: \n{format_instructions}\n\n#Question: \n{question}\n\n#Answer:"""

prompt = PromptTemplate.from_template(
    template,
    partial_variables={
        "format_instructions": output_parser.get_format_instructions()
    },
)

체인 생성 및 실행

ChatOpenAI 모델과 연결하여 체인을 생성하고, 질문에 대한 답변을 받아 결과를 출력한다.

from langchain_openai import ChatOpenAI

# Chain 생성
chain = prompt | ChatOpenAI() | output_parser

# 체인을 호출하여 질문에 대한 답변을 받습니다.
output = chain.invoke({"question": "Google 이 창업한 연도는?"})

# 결과를 문자열로 변환하여 출력
output.strftime("%Y-%m-%d")

결과:

'1998-09-04'

참고: 날짜 형식 코드

아래는 날짜 형식을 정의할 때 사용할 수 있는 몇 가지 형식 코드의 예시이다.

형식 코드설명예시
%Y4자리 연도2024
%m2자리 월07
%d2자리 일04
%H24시간제 시간14
%M2자리 분45
%S2자리 초08
%a요일 약어Thu
%A요일 전체Thursday
%b월 약어Jul
%B월 전체July

🏖️ 07. 열거형 출력 파서(EnumOutputParser)

EnumOutputParser

EnumOutputParser는 LLM의 출력을 특정 열거형(enum) 값으로 파싱하는 데 사용되는 출력 파서이다. 이 파서는 사용자가 정의한 열거형 클래스에 따라 출력 결과를 제어할 수 있다.

EnumOutputParser 사용 예시

열거형 클래스 정의

먼저, Python의 enum 모듈을 사용하여 열거형 클래스 Colors를 정의한다. 이 클래스는 Enum을 상속받으며, 세 가지 색상 값을 가진다.

from enum import Enum

class Colors(Enum):
    RED = "빨간색"
    GREEN = "초록색"
    BLUE = "파란색"

EnumOutputParser 초기화

정의한 열거형 클래스를 기반으로 EnumOutputParser의 인스턴스를 생성한다.

from langchain.output_parsers.enum import EnumOutputParser

# EnumOutputParser 인스턴스 생성
parser = EnumOutputParser(enum=Colors)

프롬프트 템플릿 및 모델 설정

프롬프트에 물체의 정보와 파싱 지침을 포함시키고, ChatOpenAI 모델과 EnumOutputParser를 연결하여 체인을 구성한다.

from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# 프롬프트 템플릿 생성
prompt = PromptTemplate.from_template(
    """다음의 물체는 어떤 색깔인가요?

Object: {object}

Instructions: {instructions}"""
).partial(instructions=parser.get_format_instructions())

# 프롬프트와 ChatOpenAI, 파서를 연결합니다.
chain = prompt | ChatOpenAI() | parser

체인 실행 및 결과 출력

체인을 실행하여 특정 물체에 대한 색깔 정보를 요청하고, 그 결과를 출력한다.

# "하늘"에 대한 체인 호출 실행
response = chain.invoke({"object": "하늘"})
print(response)

결과:

Colors.BLUE

🏖️ 08. 출력 수정 파서(OutputFixingParser)

OutputFixingParser

OutputFixingParser는 출력 파싱 과정에서 발생할 수 있는 오류를 자동으로 수정하는 기능을 제공하는 파서이다. 이 파서는 기본적으로 다른 파서(예: PydanticOutputParser)를 래핑하며, 파서가 처리할 수 없는 형식의 출력이나 오류가 발생할 경우, 추가적인 LLM 호출을 통해 오류를 수정하도록 설계되었다.

이 파서의 핵심 기능은, 첫 번째 시도에서 스키마를 준수하지 않는 결과가 나올 때, OutputFixingParser가 이를 인식하고 자동으로 수정 지시를 포함한 새로운 명령어를 LLM에 제출하는 것이다.

OutputFixingParser 사용 예시

PydanticOutputParser를 사용한 파싱 오류 처리

먼저, PydanticOutputParser를 사용하여 특정 데이터 스키마를 준수하는 출력을 생성하려고 시도한다.

from langchain.output_parsers import PydanticOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from typing import List

# Actor 모델 정의
class Actor(BaseModel):
    name: str = Field(description="name of an actor")
    film_names: List[str] = Field(description="list of names of films they starred in")

# 잘못된 형식을 일부러 입력
misformatted = "{'name': 'Tom Hanks', 'film_names': ['Forrest Gump']}"

# PydanticOutputParser 초기화
parser = PydanticOutputParser(pydantic_object=Actor)

# 잘못된 형식의 데이터를 파싱하려고 시도
try:
    parser.parse(misformatted)
except Exception as e:
    print(e)

이 경우, PydanticOutputParser는 잘못된 형식의 데이터를 파싱하려고 할 때 오류가 발생한다.

OutputFixingParser로 오류 수정

이제 OutputFixingParser를 사용하여 잘못된 형식을 바로잡도록 한다.

from langchain.output_parsers import OutputFixingParser
from langchain_openai import ChatOpenAI

# OutputFixingParser 초기화
new_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())

# OutputFixingParser를 사용하여 잘못된 형식의 출력을 파싱
actor = new_parser.parse(misformatted)

# 파싱된 결과 출력
print(actor)

결과:

Actor(name='Tom Hanks', film_names=['Forrest Gump'])

결론

OutputFixingParser는 출력 파싱 중 발생할 수 있는 오류를 자동으로 수정하는 데 매우 유용한 도구이다. 특히, 데이터 스키마를 엄격히 준수해야 하는 상황에서 잘못된 형식의 출력이 발생했을 때, 이 파서는 LLM을 통해 오류를 수정하고 올바른 형식으로 결과를 반환하도록 돕는다.


profile
AI Engineer / 의료인공지능

0개의 댓글

관련 채용 정보