LLM 개발 입문 (7) - 2

루나·2026년 2월 14일

LLM STUDY

목록 보기
15/31
post-thumbnail

본 포스팅은 "Do it! LLM을 활용한 AI 에이전트 개발 입문"을 독학하며 쓴 글입니다.
내돈내산 포스팅임을 참고해주시면 감사하겠습니다.
2026년 2월 14일 기준으로 작성되었습니다.


Chapter 7

랭체인을 활용한 에이전트 개발

본 포스팅에서는 LCEL로 체인 만들기를 해보겠습니닷!!

1. LCEL로 체인 만들기

LCEL(LangChain Expression Language)은 랭체인에서 복잡한 작업 흐름을 간단하게 만들고 관리할 수 있도록 돕는 도구이다
랭체임에서는 이런 작업 흐름을 연결하는 것은 체인이라고 한다
LCEL을 사용하면 여러 줄로 표현해야 하는 작업 단계를 읽기 쉽게 축약할 수 있고 스트림 출력 등 여러 작업을 병렬적으로 처리할 수 있다

직접 코드를 작성하면서 실습해보자!!

일단 lcel.ipynb 파일을 생성하고 랭체인 기본 문법만 사용해서 챗봇 코드를 작성해보자
GPT-40-mini 모델을 사용해서 챗봇을 만들어보자

# lcel.ipynb
from langchain_openai import ChatOpenAI

model = ChatOpenAI(model = "gpt-4o-mini")

from langchain_core.messages import HumanMessage, SystemMessage

messages = [
  SystemMessage(content = "너는 미녀와 야수에 나오는 미녀야. 그 캐릭터에 맞게 사용자와 대화해"),
  HumanMessage(content = "안녕? 저는 개스톤입니다. 오늘 시간 괜찮으시면 저녁 같이 먹을까요?")
]

model.invoke(messages)

이 코드를 실행해 보면 챗봇은 우리가 설정한 시스템 프롬프트에 맞게 데이트를 거절한다

AIMessage(content='안녕하세요, 개스톤! 저녁 제안은 정말 고맙지만, 저는 다른 생각이 있습니다. 야수와 함께하는 시간이 소중하거든요. 
당신의 진심은 알지만, 저는 사랑이란 것이 다른 모습에서도 아름다울 수 있다고 믿어요. 혹시 다른 이야기를 나눌까요?',
additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 71, 'prompt_tokens': 60, 'total_tokens': 131, 
'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 
'model_provider': 'openai', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_f4ae844694', 'id': 'chatcmpl-D96s7DHw9I2pbnjCRzMCSVcUj2UGk', 'service_tier': 'default', 'finish_reason': 'stop', '
logprobs': None}, id='lc_run--019c5ba0-575a-70c3-a39c-6a7ed5df879e-0', tool_calls=[], invalid_tool_calls=[], 
usage_metadata={'input_tokens': 60, 'output_tokens': 71, 'total_tokens': 131, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})

출력된 결과를 보면 AIMessage 객체 안에 여러 정보가 포함되어 있다
만약 텍스트 결과만 필요하다면 StrOutputParser를 사용하면 된다
StrOutputParser는 랭체인에서 제공하는 다양한 출력 파서 중 하나로 텍스트만 추출하여 반환하며
다른 종류의 파서들은 JSON, 숫자 등 특정 형식을 처리할 수 있다

StrOutputParser를 이용해서 텍스트만 반환하도록 코드를 수정해보자

from langchain_core.output_parsers import StrOutputParser

parser = StrOutputParser()

result = model.invoke(messages)
parser.invoke(result)

이 코드를 실행하면 아래와 같이 깔끔하게 텍스트 답변만 나온다!!

'안녕, 개스톤. 당신의 제안은 정말 매력적이지만, 나는 당신의 관심을 받는 것만큼 내 마음에 드는 이가 있는 것 같아요. 
저녁은 몽환적인 숲 속에서 나만의 시간을 가지는 것이 더 좋을 것 같아요. 
당신은 언제나 친절하니까, 그런 점은 감사하게 생각해요. 하지만 저는 제 길을 가고 싶어요.'

앞에서 작성한 코드는 GPT 모델에서 결과를 얻고 그 결과를 StrOutputParser를 사용해 텍스트만 추출하는 2단계가 순차로 이어진다
이 2단계를 랭체인에서는 LCEL의 체인을 이요해 연산자 | 로 한줄로 간단하게 처리할 수 있다!!
(솔직히 매우매우매우매우 놀라움 그저 간편함 GOAT...)

chain = model | parser
chain.invoke(messages)

이렇게 코드를 실행해도 parser.invoke(result)과 동일한 결과가 나온다

2. 프롬프트 템플릿 이용하기

만약 다른 캐릭터가 벨에게 제안한다면 어떻게 될까?
이런 경우에는 매번 messages의 내용을 바꿔서 테스트해야 하는데 솔직히 이건 너무 귀찮다!!
그래서 랭체인의 프롬프트 템플릿을 이용하면 필요한 부분만 수정하여 실행할 수 있다!!
이전 파일에 이어서 실습을 해보자

ChatPromptTemplate을 사용해 대화 중 변수로 만들고 싶은 부분들 {대괄호}로 감싸고 변수명을 지정한다
아래 코드에서는 system_template과 human_template을 문자열로 작성하고 story, character_a, character_b, activity를 변수로 사용한다
그리고 이를 ChatPromptTemplate 객체인 prompt_tamplate로 생성하고 .invoke를 사용해 딕셔너리 형태로 항목을 채워넣는다!

from langchain_core.prompts import ChatPromptTemplate

system_template = "너는 {story}에 나오는 {character_a}역할이다. 그 캐릭터에 맞게 사용자와 대화하라"
human_template = "안녕? 저는 {character_b}입니다. 오늘 시간 괜찮으시면 {activity} 같이 할까요?"

prompt_template = ChatPromptTemplate([
  ("system", system_template),
  ("user", human_template)
])

result = prompt_template.invoke({
  "story" : "미녀와 야수",
  "character_a" : "미녀",
  "character_b" : "야수",
  "activity" : "저녁" 
})

print(result)

이 코드를 실행하면 변수로 설정한 부분들이 모두 원하는 값으로 채워진 프롬프트가 만들어진다

messages=[SystemMessage(content='너는 미녀와 야수에 나오는 미녀역할이다. 그 캐릭터에 맞게 사용자와 대화하라', 
additional_kwargs={}, response_metadata={}), 
HumanMessage(content='안녕? 저는 야수입니다. 오늘 시간 괜찮으시면 저녁 같이 할까요?', additional_kwargs={}, response_metadata={})]

이제 랭체인의 | 연산자를 이용해 체인을 구성한다
딕셔너리 형태로 채운 내용을 prompt_template을 거쳐 GPT-4o-mini 모델과 StrOutputParser를 통해 최종 결과로 출력된다

chain = prompt_template | model | parser

chain.invoke({
  "story" : "미녀와 야수",
  "character_a" : "미녀",
  "character_b" : "야수",
  "activity" : "저녁" 
})
'안녕하세요, 야수님! 정말 즐거운 제안이네요. 저녁 함께 하는 것은 너무 좋을 것 같아요. 
당신과 함께 시간을 보낼 수 있어서 기쁩니다. 어떤 음식을 좋아하시나요?'

이렇게 변수를 이용해서 프롬프트의 내용을 간단하게 바꾸고 반복 작업을 할 수 있다!!

3. 마무리

LCEL도 랭체인과 생각보다 내용 자체는 간단한것 같다!!
개인적으로 | 연산자를 이용해서 좀 더 간편하게 표현할 수 있는게 신기했던 부분.....
지금은 간단해 보이는데 더 깊숙히 들어가면 어렵..지 않을까??

다음 실습은 펑션 콜링을 랭체인에서 사용해보는 실습을 해보려고 한다!!

profile
Per ardua ad astra

0개의 댓글