LangChain

[LangChain] LLM 에서 특정 형식의 답변 지정

sian han 2026. 4. 27. 09:22

애플리케이션에서 LLM 이 작업한 결과를 구조화된 형식으로 전달하면 다른 코드가 처리할 수 있어 매우 유용하다. 

 

▶ JSON 출력

JSON 출력은 프런트엔드 코드에 전달하거나 DB 에 저장할 수 있다. 

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field

# 1. 응답 구조 정의 (Pydantic 모델)
# 모델이 내보낼 JSON의 '스키마' 역할을 합니다.
class AnswerWithJustification(BaseModel):
    answer: str = Field(description="사용자의 질문에 대한 핵심 답변")
    justification: str = Field(description="답변에 대한 기술적 근거 또는 이유")

# 2. 모델 설정
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 3. 모델에 구조적 출력 기능 입히기 (핵심!)
# 이 단계를 거치면 llm은 무조건 AnswerWithJustification 형식을 따릅니다.
structured_llm = llm.with_structured_output(AnswerWithJustification)

# 4. 프롬프트 정의
# 여기서는 지구 복잡한 format_instructions를 넣을 필요가 없습니다.
prompt = ChatPromptTemplate.from_messages([
    ("system", "너는 백엔드 개발 전문가야. 질문에 대해 정확한 답변과 근거를 제공해줘."),
    ("user", "{question}")
])

# 5. 체인 구성 (파서가 필요 없음!)
chain = prompt | structured_llm

# 6. 실행
question_text = "레이어드 아키텍처(Layered Architecture)의 장점은 뭐야?"
response = chain.invoke({"question": question_text})

# 7. 결과 확인
print("--- 결과 타입 ---")
print(type(response))

 

with_structured_output 메서드는 선언된 스키마 AnswerWithJustification 를 두가지 용도로 활용한다. 

스키마를 JSONSchema 객체로 변환하해 LLM 에 전송한다. 해당 객체는 JSON 데이터의 구조를 기술한다. 

스키마는 LLM이 변환한 출력물을 반환하기 전에 그 유효성을 검증한다. 

 

JSON 외에도 CSV나 XML 같은 출력물을 생성하도록 지시할 수 있는데, 이 경우 출력파서가 유용한다.

 

▶ 출력파서

LLM 응답을 구조화 하는 클래스다. 출력 파서는 아래 두 가지 기능을 수행한다. 

 

  • 출력 형식 지정 : 프롬프트에 추가 지시 사항을 삽입해, LLM이 파싱하기 좋은 형식으로 텍스트를 출력하도록 유도한다.
  • 출력 검증 및 파싱 : LLM 또는 채팅 모델의 텍스트 출력 겨로가물을 받아 리스트, XML 등의 구조화된 형식으로 가공한다. 불필요한 정보를 제거하고, 불완전한 출력을 수정하며, 파싱한 값을 검증한다. 

 

▷ CSV 출력 파서 예제

CommaSeparatedListOutputParser 파서는 모델이 내놓은 문자열을 파이썬의 list 객체로 변환해준다. 

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

# 1. 모델 설정
model = ChatOpenAI(model="gpt-4o", temperature=0)

# 2. CSV 출력 파서 설정
output_parser = CommaSeparatedListOutputParser()

# 3. 프롬프트 템플릿 정의
# get_format_instructions()는 "쉼표로 구분된 리스트로 답해라"라는 지시문을 생성합니다.
prompt = ChatPromptTemplate.from_messages([
    ("system", "너는 IT 기술 트렌드 전문가야. 사용자가 제시한 주제와 관련된 기술 키워드 5개를 뽑아줘.\n{format_instructions}"),
    ("user", "{subject}")
])

# 4. LCEL 체인 구성
chain = prompt | model | output_parser

# 5. 실행
# format_instructions를 반드시 입력값으로 넣어줘야 모델이 형식을 지킵니다.
response = chain.invoke({
    "subject": "MSA(Microservices Architecture)",
    "format_instructions": output_parser.get_format_instructions()
})

# 6. 결과 확인
print("--- 결과 타입 ---")
print(type(response)) # <class 'list'>

print("\n--- 파싱된 리스트 ---")
print(response)

 

이때 parser 가 내부적으로 어떻게 작동하냐면

 

1. 모델의 원본 응답 (AIMessage)

먼저 모델은 내부적으로 아래와 같은 문자열을 생성한다. 

"Docker, Kubernetes, Kafka, API Gateway, gRPC"

 

2. 파서의 처리 (OutputParser)

CommaSeparatedListOutputParser 가 쉼표를 기준으로 문자열을 쪼개고, 각 단어의 앞뒤 불필요한 공백을 제거한다. 

 

3. 최종결과

파이썬 코드에서 즉시 사용가능한 리스트 객체가 된다. 

['Docker', 'Kubernetes', 'API Gateway', 'gRPC']

 

 

랭체인은 CSV, XML 등 다양한 경우에 대응하는 출력 파서를 제공한다.