LangChain 기초 (3) : PromptTemplate, LCEL, Chain, OutputParser

furince·2025년 2월 27일

LangChain

목록 보기
4/6
post-thumbnail

Intro

지난 포스트까지 LangChain에서 OpenAI LLM 모델을 활용하는 방법을 알아보고, 이를 확장하여 Multi-Modal 모델 활용법까지 알아보았습니다.

Model을 사용하는 방법을 알아보았으니, 이제 Model의 앞 뒤에 여러 처리 단계를 이어 붙여서 일련의 파이프라인을 구성하는 방법을 알아보아야겠습니다.

이번 포스트에서는 Chain의 구성 요소인 PromptTemplate과 OutputParser에 대해 간략하게 살펴보고 LCEL을 활용해 Chain을 구성하는 방법까지 정리해보겠습니다.


1. PromptTemplate

가장 먼저 PromptTemplate에 대해서 알아보겠습니다.

Prompt란 LLM에게 요청하는 텍스트를 의미합니다. 우리가 ChatGPT에게 "~~에 대해 설명해줘" 라고 물어보는 이 텍스트가 바로 Prompt 입니다.

그렇다면 왜 "question", "query" 라는 용어를 사용하지 않고 "prompt"라는 용어를 사용할까요? prompt는 단순히 모델에게 던지는 질문뿐만 아니라, '지시사항'을 포함하기 때문입니다. 답변의 형식부터 답변을 도출하는 추론의 과정까지 지시사항으로 포함하는 것이 가능하고, 이 때문에 "prompt" 라는 용어를 사용합니다.

이제 PromptTemplate도 쉽게 이해할 수 있을 것입니다. 이름 그대로 Prompt의 일정한 템플릿을 미리 지정해두고, 내부에 특정 변수값을 채워넣어 완전한 Prompt 문자열을 그때 그때 완성하고 싶을 때 사용합니다.

다음과 같이 PromptTemplate을 생성할 수 있습니다.

from lanchain_core.prompts import PromptTemplate

template = "{country}의 수도를 알려주세요."

prompt = PromptTemplate.from_template(template)

이렇게 PromptTemplate 클래스의 from_template() 메서드를 활용해서 특정 템플릿 문자열을 기반으로 PromptTemplate 객체를 생성하고, 이 객체에 변수값을 전달하면 완성된 prompt 문자열을 반환받을 수 있습니다.

위에서 생성한 PromptTemplate 객체를 출력해보겠습니다.

PromptTemplate(input_variables=['country'], input_types={}, partial_variables={}, template='{country}의 수도를 알려주세요.')

input_variables에 해당 템플릿에서 받을 수 있는 변수의 key 값을 알려주고 있고, template에서 전체 템플릿 문자열을 포함하고 있습니다.

이제 이 템플릿에 변수 값을 전달해서 완성된 Prompt 문자열을 만들어보겠습니다.

prompt.format(country="대한민국")

위와 같이 작성하면 아래와 같은 완성된 prompt를 얻을 수 있습니다.

'대한민국의 수도를 알려주세요.'

이렇게 전달하는 변수값에 따라 Prompt를 완성시켜주는 PromptTemplate의 사용법을 알아보았습니다.


2. LCEL과 Chain

PromptTemplate을 생성하는 방법을 알아보았으니, 이제 어떤 값을 입력하면 자동으로 Prompt를 완성해서 LLM에게 전달하고 답변까지 받아올 수 있는 파이프라인을 만들어보겠습니다.

LangChain에서는 이렇게 일련의 처리과정을 지정한 파이프라인을 Chain(체인) 이라는 용어로 사용합니다. 각각의 처리과정을 담당하는 요소들을 이어붙여서 하나의 실행가능한 객체로 만들고, 이를 Chain이라고 부르는 것이죠.

Chain을 구성하기 위해서는 LCEL이라는 연산자를 사용합니다.

LCELLangChain Expression Language의 줄임말로, LangChain에서 제공하는 다양한 구성요소들을 연결해 하나의 실행가능한 객체(Chain)로 만들기 위한 연산자입니다. | 가 바로 LCEL 입니다. |는 unix의 파이프 연산자와 유사하며, 서로 다른 구성 요소를 연결하고 한 구성 요소의 결과물을 다음 구성 요소의 입력값으로 전달하도록 설정합니다.


2-1. LCEL을 활용한 Chain 생성

다음과 같이 LCEL을 활용한 Chain을 생성할 수 있습니다.

from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

template = "{topic}에 대해 자세히 설명해주세요."
prompt = PromptTemplate.from_template(template)

llm = ChatOpenAI(
	model = "gpt-4o-mini",
    temperature = 0.1
)

chain = prompt | llm

| 연산자로 PromptTemplate 객체와 LLM 클라이언트를 연결하였습니다. 앞에서부터 지정한 순서대로 값이 전달됩니다.

현재 chain에서는 chain에 전달한 변수값에 따라 완성된 Prompt가 LLM 클라이언트의 입력값으로 자동 전달되고, LLM은 자동으로 전달받은 프롬프트를 바탕으로 답변을 생성하여 사용자에게 제공합니다.


2-2. Chain 실행하기

이제 chain을 실행하는 방법을 알아보겠습니다.

LCEL로 생성한 Chain도 ChatOpenAI 객체와 마찬가지로 .invoke() 메서드를 활용해 실행시킬 수 있습니다. 다만, Chain에서 제일 처음 실행되는 컴포넌트가 PromptTemplate이기 때문에 해당 변수에 들어갈 변수값을 python dictionary 형태로 넣어주어야 합니다.

response = chain.invoke({"topic" : "LangChain의 LECL"})

이렇게 chain을 실행해서 나온 결과물은 chain의 마지막 컴포넌트의 실행결과입니다. 여기서는 ChatOpenAI 객체의 실행결과가 마지막 실행결과이므로, response에는 AIMessage 객체가 들어가게 됩니다. 그리고 실제 답변은 content key에 들어있겠죠? 한번 확인해보겠습니다.

LangChain의 LECL(LLM-Enhanced Chain Learning)은 자연어 처리(NLP)와 머신러닝의 통합을 통해 언어 모델(LLM)을 활용하여 다양한 작업을 수행하는 방법론입니다. LECL은 주로 다음과 같은 요소로 구성됩니다:

1. **언어 모델의 활용**: LECL은 대규모 언어 모델을 사용하여 텍스트 데이터를 이해하고 생성하는 데 중점을 둡니다. 이러한 모델은 자연어 이해(NLU)와 자연어 생성(NLG) 작업에서 뛰어난 성능을 발휘합니다.

2. **체인 구조**: LECL은 여러 단계의 처리를 체인 형태로 구성하여 복잡한 작업을 수행합니다. 각 단계는 특정 작업을 수행하며, 이전 단계의 출력을 다음 단계의 입력으로 사용합니다. 이를 통해 복잡한 프로세스를 간소화하고 효율적으로 처리할 수 있습니다.

3. **강화 학습**: LECL은 강화 학습 기법을 적용하여 모델의 성능을 지속적으로 개선합니다. 모델은 환경과 상호작용하며, 보상을 통해 최적의 행동을 학습합니다. 이를 통해 모델은 점점 더 나은 결과를 생성할 수 있습니다.

4. **응용 분야**: LECL은 다양한 NLP 작업에 적용될 수 있습니다. 예를 들어, 질문 응답 시스템, 대화형 AI, 텍스트 요약, 감정 분석 등 여러 분야에서 활용될 수 있습니다.

5. **모듈화**: LECL은 모듈화된 구조를 가지고 있어, 필요에 따라 특정 모듈을 추가하거나 수정하여 다양한 요구사항에 맞출 수 있습니다. 이는 개발자와 연구자들이 자신만의 솔루션을 쉽게 구축할 수 있도록 돕습니다.

LECL은 이러한 요소들을 통해 언어 모델의 강력한 기능을 활용하여 복잡한 문제를 해결하고, 다양한 응용 프로그램에서 효과적으로 사용할 수 있는 방법론입니다. LangChain의 LECL은 특히 대화형 AI와 같은 분야에서 그 가능성을 보여주고 있습니다.

질문한 LangChain의 LCEL에 대해 자세히 설명해준 모습입니다.


2-3. 다변수 Chain 생성/실행하기

자, 조금 더 응용해서 이번엔 PromptTemplate가 2개의 변수를 요구할 때 실행하는 방법을 알아보겠습니다.

먼저 2개의 변수를 요구하는 chain을 LCEL로 생성해보죠.

template = "{topic}에 대해 {how} 설명하주세요."

prompt = PromptTemplate.from_template(template)

llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.1
)


chain = prompt | llm

이제 2개의 변수를 전달하면서 실행하는 방법을 알아보겠습니다. 어렵게 생각할 필요없습니다. 앞서 변수에 대한 값을 python dictionary로 전달했던 것처럼, 각 변수에 대한 값을 key-value 쌍으로 만들어 하나의 dictionary로 전달하면 됩니다.

response = chain.invoke({"topic" : "LangChain의 LECL, "how" : "영어로"})

이렇게 전달하면 PromptTemplate에서 key에 맞는 값을 자동으로 대입해 프롬프트를 완성합니다.

실행 결과를 확인해볼까요?

LECL, which stands for "LangChain's Enhanced Chain Language," is a framework designed to facilitate the development of applications that utilize language models. It provides a structured way to create complex workflows by chaining together various components, such as prompts, tools, and data sources, to achieve specific tasks.

Key features of LECL include:

1. **Modularity**: LECL allows developers to build applications in a modular fashion, enabling them to easily swap out components or add new ones without significant rework.

2. **Flexibility**: The framework supports a wide range of use cases, from simple question-answering systems to more complex applications that require multiple steps and interactions with external APIs or databases.

3. **Integration**: LECL is designed to work seamlessly with various language models and tools, making it easier for developers to integrate different technologies into their applications.

4. **Ease of Use**: With a focus on user-friendly design, LECL provides clear abstractions and documentation, allowing developers to quickly understand and implement the framework in their projects.

Overall, LECL aims to streamline the process of building language model-driven applications, making it accessible for both experienced developers and those new to the field.

이렇게 LangChain의 LCEL을 활용해 Chain을 생성하고 실행하는 방법을 알아보았습니다.


3. OutputParser

Chain의 결과물을 조금 더 보기 편하게 바꾸어보겠습니다.

지금까지 Chain의 결과물은 AIMessage 객체였습니다. Chain의 마지막 컴포넌트가 ChatOpenAI 객체였기 때문이죠. AIMessage 내부에 포함된 response_metadata를 매번 활용할 예정이라면 그대로 활용하면 되겠지만, 답변만 필요한 경우라면 이렇게 반환되는 형태는 다소 보기 불편할 것입니다.

따라서 ChatOpenAI의 응답 결과에서 답변 텍스트만 추출해서 반환해주도록 Chain을 발전시켜보겠습니다. 이를 위해서는 langchain_core.output_parsers에서 제공하는 StrOutputParser 클래스를 활용합니다.

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

template = "{topic}에 대해 {how} 설명하주세요."

prompt_template = PromptTemplate.from_template(template)

llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.1
)

output_parser = StrOutputParser()


chain = prompt_template | llm | output_parser

StrOutputParser 객체는 ChatOpenAI의 반환값인 AIMessage 객체에서 content에 포함된 답변 텍스트만을 추출해서 반환하는 역할을 수행합니다. 따라서 위와 같이 chain을 구성하면 실행결과는 더이상 AIMessage 객체가 아닌 답변 텍스트가 됩니다. 한번 확인해보죠!

input = {
    "topic" : "인공지능 모델의 학습 원리",
    "how" : "자세하게"
}

print(chain.invoke(input))
인공지능(AI) 모델의 학습 원리는 주로 머신러닝(Machine Learning)과 딥러닝(Deep Learning)이라는 두 가지 주요 분야로 나눌 수 있습니다. 이 두 분야는 데이터에서 패턴을 학습하고 예측을 수행하는 데 중점을 두고 있습니다. 아래에서 이들 원리를 자세히 설명하겠습니다.

### 1. 머신러닝의 기본 원리

머신러닝은 데이터에서 학습하여 특정 작업을 수행하는 알고리즘을 개발하는 분야입니다. 머신러닝의 학습 과정은 일반적으로 다음과 같은 단계로 이루어집니다.

#### a. 데이터 수집
모델을 학습시키기 위해서는 먼저 데이터를 수집해야 합니다. 이 데이터는 모델이 학습할 수 있는 입력(features)과 출력(target)으로 구성됩니다.

#### b. 데이터 전처리
수집된 데이터는 종종 노이즈가 있거나 불완전합니다. 따라서 데이터 전처리 과정이 필요합니다. 이 과정에는 결측값 처리, 정규화, 범주형 변수 인코딩 등이 포함됩니다.

#### c. 모델 선택
문제에 적합한 머신러닝 알고리즘을 선택합니다. 예를 들어, 회귀 문제에는 선형 회귀, 분류 문제에는 로지스틱 회귀, 결정 트리, 서포트 벡터 머신(SVM) 등을 사용할 수 있습니다.

#### d. 학습
선택한 모델을 사용하여 데이터를 학습합니다. 이 과정에서 모델은 입력 데이터와 해당하는 출력 데이터를 기반으로 패턴을 찾아내고, 이를 통해 예측을 수행할 수 있는 능력을 갖추게 됩니다.

#### e. 평가
학습된 모델의 성능을 평가하기 위해 테스트 데이터를 사용합니다. 일반적으로 정확도, 정밀도, 재현율, F1-score 등의 지표를 사용하여 모델의 성능을 측정합니다.

#### f. 하이퍼파라미터 튜닝
모델의 성능을 개선하기 위해 하이퍼파라미터를 조정합니다. 하이퍼파라미터는 모델의 구조나 학습 과정에 영향을 미치는 매개변수로, 예를 들어 학습률, 트리의 깊이 등이 있습니다.

### 2. 딥러닝의 기본 원리

딥러닝은 머신러닝의 한 분야로, 인공신경망(Artificial Neural Networks)을 기반으로 합니다. 딥러닝의 학습 원리는 다음과 같습니다.

#### a. 인공신경망 구조
딥러닝 모델은 여러 층(layer)으로 구성된 인공신경망으로 이루어져 있습니다. 각 층은 노드(node)로 구성되며, 노드 간의 연결은 가중치(weight)로 표현됩니다. 입력층, 은닉층(hidden layer), 출력층으로 구성됩니다.

#### b. 순전파(Forward Propagation)
입력 데이터가 모델에 들어오면, 각 층을 거치면서 가중치와 활성화 함수(activation function)를 통해 변환됩니다. 이 과정을 통해 최종 출력이 생성됩니다.

#### c. 손실 함수(Loss Function)
모델의 예측 결과와 실제 값 간의 차이를 측정하기 위해 손실 함수를 사용합니다. 손실 함수는 모델의 성능을 평가하는 기준이 됩니다.

#### d. 역전파(Backpropagation)
모델의 예측이 실제 값과 얼마나 다른지를 기반으로 가중치를 조정하는 과정입니다. 손실 함수의 기울기를 계산하여 가중치를 업데이트합니다. 이 과정은 경량화된 경로를 통해 이루어지며, 일반적으로 경사 하강법(Gradient Descent) 알고리즘을 사용합니다.

#### e. 반복 학습
순전파와 역전파 과정을 여러 번 반복하여 모델이 점점 더 정확한 예측을 할 수 있도록 학습합니다. 이 과정을 에포크(epoch)라고 하며, 각 에포크마다 모델의 가중치가 업데이트됩니다.

### 3. 결론

인공지능 모델의 학습 원리는 데이터에서 패턴을 찾아내고 이를 기반으로 예측을 수행하는 과정입니다. 머신러닝과 딥러닝은 각각의 방법론을 통해 이러한 학습을 수행하며, 데이터의 특성과 문제의 유형에 따라 적절한 방법을 선택하여 적용합니다. 이러한 과정은 AI의 다양한 응용 분야에서 중요한 역할을 하고 있습니다.

이렇게 chain의 결과물을 필요한 값만 반환하도록 설정해보았습니다.


Outro

기본적인 Chain의 생성 방법과 PromptTemplate, StrOutputParser 를 활용해 Chain으로 연결하는 과정까지 살펴보았습니다.

앞으로 더 많은 컴포넌트를 살펴보고, 더 복잡한 Chain을 구성하기 위해서는 이렇게 기본적인 Chain 구성 과정에 익숙해져 있어야겠습니다.

출처

0개의 댓글