Pydantic, Json parser 정리(AI 생성 콘텐츠의 구조적 일관성 확보)

[9기 문과생도 AI 캠프]에서 네이버 블로그 포스팅 자동화 코드를 작성하면서 겪었던 경험을 바탕으로 Langchain의 output parsing과 관련된 몇 가지 라이브러리에 대해 정리해 보도록 하겠습니다.

당시 프로젝트에서 ChatGPT를 이용해 “제목, 본문, 해시태그”라는 3개의 구성 요소로 글을 생성하도록 프롬프트를 작성하였습니다. 그러나 때때로 ChatGPT가 이 구조와는 다른 결과를 만들어 주어 네이버 자동 포스팅 과정에서 에러가 발생하곤 했었습니다.

이 문제를 해결하기 위해 try-except문과 Json 변환 함수 등을 활용했는데, 이는 에러가 발생하지 않을 때까지 글 생성을 반복하는 방식이었습니다.

이런 경험을 통해 ChatGPT가 생성한 결과물의 구조적 일관성 확보라는 것에 대해 생각을 하게 되었는데, 이는 Langchain의 LCEL(LangChain Expression Language)에서 제공하는 “Output Parser”의 개념과 밀접하게 연관되어 있는 듯 합니다.

여기에서는 Output Parser의 여러 라이브러리 중에서 특히 “PydanticOutputParser”와 “JsonOutputParser”에 대한 내용과 이를 활용한 간단한 예(뉴스 기사 3줄 요약)를 함께 정리해 보았습니다.

  1. PydanticOutputParser

  2. JsonOutputParser

  3. JsonOutputParser 활용 사례



PydanticOutputParser


이 output parser를 사용하여 임의의 pydangtic model 형식을 지정하고, 이 스키마에 맞춰 LLM이 출력하도록 제어할 수 있습니다.


먼저, 여기서 “pydactic”에 대해 claude 3.5 sonnet에 물어 봤습니다.

네, Pydantic에 대해 설명해 드리겠습니다.

Pydantic은 Python에서 데이터 유효성 검사와 설정 관리를 위한 라이브러리입니다. 주요 특징과 용도는 다음과 같습니다:

1. 데이터 검증: Pydantic은 데이터 모델을 정의하고 입력된 데이터가 해당 모델과 일치하는지 확인합니다.

2. 타입 힌트 사용: Python의 타입 힌트를 활용하여 데이터 구조를 명확하게 정의합니다.

3. 자동 형변환: 가능한 경우 입력 데이터를 올바른 타입으로 자동 변환합니다.

4. 에러 처리: 데이터가 유효하지 않을 때 명확한 에러 메시지를 제공합니다.

5. JSON 호환: JSON으로의 직렬화와 역직렬화를 지원합니다.

6. 설정 관리: 애플리케이션의 설정을 관리하는 데 유용합니다.

추가적으로 Pydantic의 예시 코드는 다음과 같습니다.

from pydantic import BaseModel, Field, ValidationError
from typing import List

class User(BaseModel):
    username: str
    email: str
    age: int = Field(gt=0, lt=120)
    hobbies: List[str] = []

# 유효한 데이터로 User 인스턴스 생성
try:
    user1 = User(username="john_doe", email="[email protected]", age=30, hobbies=["reading", "swimming"])
    print(user1)
    
    # JSON으로 변환
    print(user1.json())

# 유효하지 않은 데이터로 User 인스턴스 생성 시도
    user2 = User(username="jane_doe", email="invalid_email", age=150)
except ValidationError as e:
    print(f"Validation error: {e}")


다음은 Langchain 공식 문서에 나온 “PydanticOutputParser”의 코드입니다.


from typing import List

from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import ChatOpenAI

model = ChatOpenAI(temperature=0)

# Define your desired data structure.
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")

# And a query intented to prompt a language model to populate the data structure.
actor_query = "Generate the filmography for a random actor."

# Set up a parser + inject instructions into the prompt template.
parser = PydanticOutputParser(pydantic_object=Actor)

prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

chain = prompt | model | parser

result = chain.invoke({"query": actor_query})

print(result)

# 요소별 분리 출력
print("요소별로 분리 출력")
print(f"[name] : {result.name}")
print(f"[file_names] : {result.film_names}")


name='Tom Hanks' film_names=['Forrest Gump', 'Cast Away', 'Saving Private Ryan', 'Toy Story', 'The Green Mile']
요소별로 분리 출력
[name] : Tom Hanks
[file_names] : ['Forrest Gump', 'Cast Away', 'Saving Private Ryan', 'Toy Story', 'The Green Mile']


기본적인 LCEL 구조에 다음의 3가지 포인트를 신경써서 보면 될 듯 합니다.

  • 출력 형식에 대한 정의

  • parser 객체 생성

  • prompt 적용





2. JsonOutputParser


이 parser를 사용하여 임의의 json 스키마를 지정하고, 이 스키마에 맞춰 LLM의 생성 결과의 출력을 제어할 수 있습니다.

Langchain 공식 문서에 나온 기본 코드는 다음과 같습니다.


from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

model = ChatOpenAI(temperature=0)

# Define your desired data structure.
class Joke(BaseModel):
    setup: str = Field(description="question to set up a joke")
    punchline: str = Field(description="answer to resolve the joke")

# And a query intented to prompt a language model to populate the data structure.
joke_query = "Tell me a joke."

# Set up a parser + inject instructions into the prompt template.
parser = JsonOutputParser(pydantic_object=Joke)

prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

chain = prompt | model | parser

chain.invoke({"query": joke_query})


{'setup': "Why don't scientists trust atoms?",
 'punchline': 'Because they make up everything!'}




3. 실제 활용 사례 : 뉴스 기사 3줄 요약


from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
from typing import List
from trafilatura import fetch_url, extract

llm = ChatOpenAI(temperature=0)

# trafilatura library 활용 뉴스 내용 크롤링
def content_trafilatura(link):
    downloaded = fetch_url(link)
    return extract(downloaded, favor_precision=True, deduplicate=True)

link = "https://n.news.naver.com/mnews/article/031/0000846788?sid=104"

context = content_trafilatura(link)

# Define your desired data structure.
class Summary(BaseModel):
    summary: List[str] = Field(description="3 bullet style list of summary in given context")

# And a query intented to prompt a language model to populate the data structure.
joke_query = "Tell me a joke."

# Set up a parser + inject instructions into the prompt template.
parser = JsonOutputParser(pydantic_object=Summary)

template = """당신은 뉴스 요약 Assistant입니다. 주어진 <context>를 3개의 bullet style list로 요약해 주세요. 
<Important>각각의 bullet list는 50자를 넘지 않아야 하며, 개조식으로 작성할 것.</Important>
\n{format_instructions}\n<context>{context}</context>\n"
"""

prompt = PromptTemplate(
    template=template,
    input_variables=["context"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

chain = prompt | llm | parser

responses = chain.invoke({"context": context})

formatted_string = '\n'.join(f'👌 {item}' for item in responses['summary'])

print(formatted_string)


👌 애플과 메타 플랫폼이 '애플 인텔리전스'에 메타의 생성형 AI 모델을 통합하는 방안 논의 중
👌 애플은 독자적인 생성형 AI 시스템 '애플 인텔리전스'를 개발, 파트너사와 협력
👌 애플 인텔리전스를 통해 AI 스타트업들이 자사 서비스에 대한 프리미엄 구독을 판매할 수 있음


#11기 랭체인 #11기랭체인

6

👉 이 게시글도 읽어보세요