3.4. JSON 출력 파서(JsonOutputParser)

LangChain의 JsonOutputParser는 LLM의 출력을 특정 JSON 스키마에 맞게 파싱하고 구조화하는 데 도움을 주는 강력한 도구입니다. 이 챕터에서는 Ollama를 기반으로 한 LangChain 환경에서 JsonOutputParser를 사용하는 방법과 EXAONE 3.5 모델을 활용한 실전 예제를 다룹니다.

JsonOutputParser는 사용자가 정의한 JSON 스키마에 따라 LLM의 출력을 구조화된 JSON 객체로 변환합니다. 이를 통해 비정형 텍스트 출력을 프로그램에서 쉽게 사용할 수 있는 데이터로 변환할 수 있습니다.

JsonOutputParser의 역할과 필요성

LLM은 일반적으로 자유 형식의 텍스트를 생성합니다. 하지만 애플리케이션에서는 JSON과 같은 구조화된 데이터 형식이 요구되는 경우가 많습니다. 예를 들어, 사용자 정보를 JSON 객체로 추출하거나, 특정 데이터 필드를 API로 전송해야 할 때 JsonOutputParser는 다음과 같은 이점을 제공합니다:

  • 구조화된 출력 보장: 사용자가 정의한 스키마에 맞는 JSON 객체를 생성.
  • 오류 감소: 비정형 텍스트를 수동으로 파싱할 때 발생할 수 있는 오류를 줄임.
  • 효율적인 통합: JSON 출력을 통해 다른 시스템과의 통합을 간소화.
  • 유연성: 복잡한 중첩 스키마나 선택적 필드도 처리 가능.

JsonOutputParser 설정 및 사용법

LangChain에서 JsonOutputParser를 사용하려면 다음 단계를 따릅니다:

  1. 필요한 모듈 가져오기: JsonOutputParser와 함께 PromptTemplate, 그리고 Ollama 기반의 LLM을 초기화합니다.
  2. JSON 스키마 정의: 출력하고자 하는 JSON 구조를 Pydantic 모델 또는 딕셔너리 형태로 정의합니다.
  3. 프롬프트 템플릿 설정: LLM이 JSON 형식으로 응답하도록 유도하는 프롬프트를 작성합니다.
  4. 파이프라인 구성: 프롬프트, LLM, 그리고 JsonOutputParser를 연결하여 체인을 구성합니다.

다음은 EXAONE 3.5 모델을 사용하여 사람 정보를 JSON 형식으로 추출하는 예제입니다. 이 예제는 사용자가 입력한 텍스트에서 이름, 나이, 직업 정보를 추출합니다.

from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel, Field

# 출력 스키마 정의
class Person(BaseModel):
    name: str = Field(description="사람의 이름")
    age: int = Field(description="사람의 나이")
    occupation: str = Field(description="사람의 직업")

# 프롬프트 템플릿 설정
prompt = PromptTemplate.from_template(
    """다음 텍스트에서 사람의 이름, 나이, 직업을 JSON 형식으로 추출하세요:
    {input}

    출력은 다음 JSON 스키마를 따라야 합니다:
    {format_instructions}
    """
)

# LLM 초기화 (EXAONE 3.5)
llm = OllamaLLM(model="exaone3.5", format="json", temperature=0.1)

# JSON 출력 파서 설정
parser = JsonOutputParser(pydantic_object=Person)

# 체인 구성
chain = prompt | llm | parser

# 입력 텍스트
input_text = "김민수는 35세이고, 소프트웨어 엔지니어로 일하고 있습니다."

# 체인 실행
response = chain.invoke({
    "input": input_text,
    "format_instructions": parser.get_format_instructions()
})

print(response)
{‘name’: ‘김민수’, ‘age’: 35, ‘occupation’: ‘소프트웨어 엔지니어’}
  • Pydantic 모델 (Person): 출력 JSON의 구조를 정의합니다. 각 필드에는 설명을 추가하여 LLM이 올바른 데이터를 추출하도록 돕습니다.
  • 프롬프트 템플릿: 입력 텍스트와 JSON 스키마 형식을 LLM에 전달합니다. parser.get_format_instructions()는 스키마를 문자열로 변환하여 프롬프트에 삽입합니다.
  • OllamaLLM: EXAONE 3.5 모델을 JSON 출력 모드로 설정(format=”json”)하여 구조화된 출력을 보장합니다.
  • 체인: 프롬프트, LLM, 파서를 연결하여 입력부터 JSON 출력까지의 파이프라인을 구성합니다.

고급 사용 사례

복잡한 데이터 구조를 다룰 때는 중첩된 JSON 스키마가 필요할 수 있습니다. 예를 들어, 여러 사람의 정보를 포함한 리스트를 추출하는 경우를 살펴보겠습니다.

from typing import List

# 중첩 스키마 정의
class PeopleData(BaseModel):
    people: List[Person] = Field(description="사람들의 정보 리스트")

# 프롬프트 템플릿
prompt = PromptTemplate.from_template(
    """다음 텍스트에서 사람들의 정보를 JSON 형식으로 추출하세요:
    {input}

    출력은 다음 JSON 스키마를 따라야 합니다:
    {format_instructions}
    """
)

# 파서 및 체인 설정
parser = JsonOutputParser(pydantic_object=PeopleData)
chain = prompt | llm | parser

# 입력 텍스트
input_text = """
김민수는 35세이고, 소프트웨어 엔지니어입니다.
이영희는 28세이고, 디자이너로 일하고 있습니다.
"""

# 체인 실행
response = chain.invoke({
    "input": input_text,
    "format_instructions": parser.get_format_instructions()
})

print(response)
{‘people’: [{‘name’: ‘김민수’, ‘age’: 35, ‘occupation’: ‘소프트웨어 엔지니어’}, {‘name’: ‘이영희’, ‘age’: 28, ‘occupation’: ‘디자이너’}]}

오류 처리 및 디버깅

JsonOutputParser는 유효한 JSON을 생성하지 못할 경우 예외를 발생시킵니다. 이를 처리하기 위해 try-except 블록을 사용하는 것이 좋습니다:

try:
    response = chain.invoke({
        "input": input_text,
        "format_instructions": parser.get_format_instructions()
    })
    print(response)
except Exception as e:
    print(f"JSON 파싱 오류: {e}")
{‘people’: [{‘name’: ‘김민수’, ‘age’: 35, ‘occupation’: ‘소프트웨어 엔지니어’}, {‘name’: ‘이영희’, ‘age’: 28, ‘occupation’: ‘디자이너’}]}

또한, LangSmith와 같은 도구를 사용하면 체인 실행 과정을 추적하여 디버깅을 용이하게 할 수 있습니다.

EXAONE 3.5와의 최적화 팁

EXAONE 3.5는 한국어 처리에 특화된 모델로, JSON 출력에서도 높은 성능을 발휘합니다. 다음은 최적화 팁입니다:

  • 명확한 프롬프트: 한국어로 작성된 명확하고 구체적인 프롬프트를 사용하세요. 예: “JSON 형식으로만 응답하세요”를 명시.
  • 온도(Temperature) 조정: temperature=0.1과 같이 낮은 값을 설정하여 출력의 일관성을 높임.
  • 스키마 설명 강화: Pydantic 필드의 description을 상세히 작성하여 모델이 올바른 데이터를 추출하도록 유도.
  • JSON 모드 활성화: OllamaFunctions에서 format=”json”을 반드시 설정.

주의사항

  • 모델 한계: EXAONE 3.5는 뛰어난 성능을 제공하지만, 매우 복잡한 스키마나 모호한 입력에서는 추가 프롬프트 엔지니어링이 필요할 수 있습니다.
  • Ollama JSON 모드 성능: Ollama의 JSON 모드는 때때로 느리거나 공백을 과도하게 생성할 수 있습니다. 최신 Ollama 버전을 사용하는 것이 좋습니다.
  • 스키마 검증: Pydantic 모델을 사용할 때는 필수 필드와 선택 필드를 명확히 정의하여 유연성을 확보하세요.

참고) Pydantic 없이 사용하기

Pydantic 없이도 이 기능을 사용할 수 있습니다. 이 경우 JSON을 반환하도록 요청하지만, 스키마가 어떻게 되어야 하는지에 대한 구체적인 정보는 제공하지 않습니다.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_ollama import ChatOllama

llm_model = ChatOllama(model="exaone3.5", temperature=0)

# 질의 작성
question = "화성 이주 최신 계획을 알려주세요. `description`에, 관련 키워드는 `hashtags`에 담아주세요."

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

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

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

# 프롬프트, 모델, 파서를 연결하는 체인 생성
chain = prompt | llm_model | parser

# 체인을 호출하여 쿼리 실행
response = chain.invoke({"question": question})

# 출력을 확인합니다.
print(response)
{‘description’: “현재 공개된 가장 구체적인 화성 이주 계획 중 하나는 NASA의 ‘아르테미스 프로그램’과 민간 기업들의 노력을 포함합니다. 아르테미스는 장기적으로 인간의 지속 가능한 화성 정착을 목표로 하며, 초기 단계로는 달 주변에서의 경험을 쌓고 있습니다. 구체적인 이주 일정은 아직 확정되지 않았으나, 2030년대 후반이나 2040년대 초반에 인간의 화성 표면 탐사 및 임시 정착지 구축을 목표로 하고 있습니다. 이 과정에서 생명 유지 시스템, 자원 재활용 기술, 그리고 건강 관리 시스템 개발이 핵심 과제로 부각되고 있습니다.”, ‘hashtags’: [‘#화성이주’, ‘#아르테미스프로젝트’, ‘#우주탐사’, ‘#지속가능한정착’, ‘#우주기술혁신’]}
이 글은 카테고리: 랭체인 (LangChain)에 포함되어 있습니다. 고유주소를 북마크하세요.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다