LLM 개발 입문 (7) - 1

루나·2026년 2월 14일

LLM STUDY

목록 보기
14/31
post-thumbnail

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


Chapter 7

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

본 포스팅에서는 랭체인으로 챗봇을 만들어보도록 하겠습니다

1. 랭체인이란?

랭체인(LangChain)언어 모델에 기반한 AI 애플리케이션을 쉽게 개발할 수 있도록 도와주는 프레임워크이다
기존에는 오픈AI와 같은 언어 모델의 API를 사용해 원하는 기능을 구현하려면 모든 코드를 직접 작성해야했다
랭체인은 이 작업을 간소화할 수 있는 다양한 도구와 모듈을 제공한다
랭체인을 사용하면 요약, 검색, 문서 생성, 질의응답 등 여러 기능을 손쉽게 구현할 수 있으며
복잡한 애플리케이션을 개발할 대도 미리 구축된 모듈을 활용해 개발속도를 높일 수 있다

또한 랭체인을 이용하면 다른 언어 모델을 쉽게 교체할 수 있다
구글의 제미나이, 메타의 라마, 앤트로픽의 클로드와 같은 모델은 오픈AI의 GPT와 API 사용방식이 달라서
기존 프로그램을 다른 언어 모델로 바꾸려면 코드 전반을 수정해야했다

하지만 랭체인을 이용하면 언어 모델 선언 부분만 수정해서 다른 언어모델로 쉽게 변경할 수 있다
이 기능 덕에 특정 모델에 종속되지 않고 다양한 모델의 장점을 활용한 애플리케이션을 개발할 수 있다

따라서 지금까지의 오픈AI의 GPT API를 활용해서 만든 챗봇을 랭체인을 활용해 다시 개발하면서
랭체인의 사용 방법을 알아보고 편의성이 무엇인지 알아보자!!

2. 랭체인과 오픈AI의 GPT API의 비교

우선 랭체인을 사용하기 위해 터미널 창을 키고 랭체인을 설치하자
당연히 생성한 가상환경을 활성화한 다음에 설치해야 하는 것을 잊지말자

pip install langchain

오픈AI의 GPT API를 사용하기 위해 랭체인에서 오픈AI 모델을 사용할 수 있게 해주는 langchain-openai 또한 설치한다
랭체인에서는 GPT뿐만 아니라 제미나이, 라마, 클로드 등 다양한 언어 모델을 사용할 수 있다

pip install langchain-openai

이제 langchain_chatbot.ipynb 파일을 생성하고 openai 라이브러리 대신 ChatOpenAI를 임포트한다
그리고 gpt-4o-mini 모델을 사용한다고 선언해보자

from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")

이어서 랭체인에서 사용자의 메세지를 의미하는 HumanMessage를 불러온다
그리고 model.invoke에 메세지를 리스트 형태로 전달한다

오픈AI의 API : 메세지를 딕셔너리 형태로 작성해서 리스트로 쌓아서 전송
랭체인 : 메세지를 리스트 형태로 전송체인 : 메세지를 리스트 형태로 전송

from langchain_core.messages import HumanMessage
model.invoke([HumanMessage(content = "안녕? 나는 이성용이야")])

이 코드를 실행하면 기존의 챗봇에서 메세지를 보낸것 처럼 아래와 같은 결과를 확인할 수 있다

AIMessage(content='안녕하세요, 이성용님! 어떻게 도와드릴까요?', additional_kwargs={'refusal': None},
response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 16, 'total_tokens': 31, 
'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-D93kwl7YyNlB9fscXZ9I8cBlsluhj', 'service_tier': 'default', 'finish_reason': 'stop', 
'logprobs': None}, id='lc_run--019c5ae9-96d6-7783-ad91-119f27a52b5b-0', tool_calls=[], invalid_tool_calls=[], 
usage_metadata={'input_tokens': 16, 'output_tokens': 15, 'total_tokens': 31, '
input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})

지금까지 봐왔던 터미널의 결과와는 다르지만 그래도 content 부분에 적절하게 AI의 답변이 들어간 것은 알 수 있다

랭체인에서는 AIMessage, HumanMessage, SystemMessage와 같은 다양한 메세지 타입을 제공한다
이 메세지들은 오픈AI의 API에서 각각 role을 assistant, user, system으로 설정했을 때와 같은 역할을 수행한다

지금까지의 상태에서 랭체인을 통해 GPT와 대화를 이어나가도 GPT가 이전 내용을 기억하지 못한다
이 상황은 멀티턴 대화를 구현하기 전까지의 상황과 동일하다
예전 포스팅에서 했던것 처럼 멀티턴 대화를 구현해보자

3. 랭체인으로 멀티턴 대화하기

에전 포스팅에서 멀티턴 대화를 구현하기 위해서 대화 내용을 담은 리스트에 사용자의 질문과 GPT의 답변을 계속 추가해서 대화를 이어나갔다
이러한 방식으로 랭체인에서도 멀티턴 대화를 구현해보자
이전에 사용하였던 multi_turn.py를 복사해와서 코드를 수정해보자

# langchain_multiturn.py
# from openai import OpenAI
from dotenv import load_dotenv
# import os

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

load_dotenv()
# api_key = os.getenv('OPENAI_API_KEY')

# client = OpenAI(api_key = api_key)
llm = ChatOpenAI(model = "gpt-4o")

# def get_ai_response(messages):
#   response = client.chat.completions.create(
#     model = "gpt-4o",
#     temperature = 0.9,
#     messages = messages,
#   )

#   return response.choices[0].message.content

# 초기 시스템 메세지 설정
messages = [
  # {"role" : "system", "content" : "너는 사용자를 도와주는 상담사야"},
  SystemMessage(content = "너는 사용자를 도와주는 상담사야")
]

while True:
  user_input = input("사용자 : ")

  if user_input == "exit":
    break

  # messages.append({"role" : "user", "content" : user_input})
  messages.append(HumanMessage(user_input))
  
  # ai_response = get_ai_response(messages)
  ai_response = llm.invoke(messages)

  # messages.append({"role" : "assistant", "content" : ai_response})
  messages.append(ai_response)

  print("AI : " + ai_response.content)
  • llm = ChatOpenAI(model = "gpt-=4o")
    랭체인에서 사용할 모델을 정의한다
    랭체인에서 제공하는 ChatOpenAI를 사용하므로 이전에 사용한 get_ai_response는 필요하지 않다
  • ai_response = llm.invoke(messages)
    messages.append(ai_response)
    llm.invoke()로 받은 ai_response를 그대로 메세지 리스트에 가할 수 있다
    이전에 오픈AI의 API를 사용할 때는 반환된 값에서 content만 추출한 후
    {"role" : "assistant", "content" : "GPT 답변"} 의 형태로 리스트에 추가했지만
    랭체인에서는 이미 반환값이 AIMessage 형태여서 별도로 content를 추출할 필요 없이 그대로 추가할 수 있다

이제 코드를 실행하면 다음과 같이 대화를 이어나갈 수 있다

4. 랭체인의 메세지 히스토리

앞에서 멀티턴 대화를 위해 매번 GPT와 사용자의 대화 내용을 리스트나 딕셔너리에 추가하는 코드를 작성해야 했다
하지만 랭체인에서는 메세지 히스토리 기능을 사용하면 멀티턴 대화를 더 쉽게 구현할 수 있다

메세지 히스토리 기능을 이해하기 위해 langchain_message_history.ipynb 파일을 만들고 코드를 작성해보자

# langchain_message_history.ipynb

from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

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

store = {}

# 세션 ID에 따라 대화 기록을 가져오는 함수
def get_session_history(session_id : str) -> BaseChatMessageHistory:
  # 만약 해당 세션 ID가 store에 없으면 새로 생성해서 추가
  if session_id not in store:
    store[session_id] = InMemoryChatMessageHistory()      # 메모리에 대화 기록을 저장하는 객체 생성
  return store[session_id]      # 해당 세션의 대화 기록을 반환

# 모델 생성시 대화기록을 함ㄲ메 전달하는 래퍼 객체 생성
with_message_history = RunnableWithMessageHistory(model, get_session_history)
  • InMemoryChatMessageHistory
    메모리 내에서 메세지를 리스트 형태로 보관한다
    애플리케이션을 종료하면 대화 내용이 사라지기 때문에 계속 사용하고 싶다면 파일이나 DB에 저장해야 한다
  • RunnableWithMessageHistory
    모델을 생성할 때 대화 기록을 함께 전달할 수 있도록 하는 클래스
  • def get_session_history(session_id : str) -> BaseChatMessageHistory:
    RunnableWithMessageHistory 클래스에 대화기록을 관리하기 위해 호출된다
    session_id에 해당하는 대화 기록을 반환하는 역할을 한다
    store에서 특정 사용자의 대화 기록을 찾고, 만약 해당 session_id가 없으면 새로운 대화 기록 객체를 만들어서 저장한다
    그리고 그에 해당하는 대화 기록 객체를 반환한다
  • with_message_history = RunnableWithMessageHistory(model, get_session_history)
    RunnableWithMessageHistory를 이용하면 대화 내용을 저장하면서 대화를 이어 나갈 수 있다!
    with_message_history를 사용해 RunnableWithMessageHistory 인스턴스를 만든다

이제 ChatOpenAI를 바로 사용하는 model.invoke() 대신에 with_message_history()를 실행한다
그리고 session_id를 랭체인에서 정한 양식에 맞게 설정한다
현재 config객체에 session_id는 abc2로 설정했다

config = {"configurable" : {"session_id" : "abc2"}}   # 세션 ID를 설정하는 config 객체 생성

response = with_message_history.invoke(
  [HumanMessage(content = "안녕? 난 이성용이야.")],
  config = config,
)

print(response.content)

이 코드를 실행한 이후 아래 코드도 연속해서 실행해보자

response = with_message_history.invoke(
  [HumanMessage(content = "내 이름이 뭐지?")],
  config = config,
)

print(response.content)


이전 대화내용을 잘 기억하고 있다
지금 대화가 이뤄지고 있는 session의 ID 값은 abc2라서 대화 내용이 해당 session_id 를 가진 store[]에 저장되기 때문이다

만약 새로운 config를 생성하고 session_id 값을 변경시킨다면 대화 내용이 따로 저장되기 때문에 GPT는 이전 대화를 기억하지 못한다

config = {"configurable" : {"session_id" : "abc3"}}   # 세션 ID를 설정하는 config 객체 생성

response = with_message_history.invoke(
  [HumanMessage(content = "나에 대해서 뭘 알고 있어?")],
  config = config,
)

print(response.content)


session_id를 abc2에서 abc3으로 변경해서 다시 질문을 하면 이름을 기억하지 못하는 것을 확인할 수 있다

5. 스트림 방식으로 출력하기

마지막으로 랭체인으로 만든 챗봇의 답변을 스트림 방식으로 출력해보자!
랭체인을 이용하면 스트림 방식으로 답변을 출력하는것도 간단하다
.invoke 라고 되어있던 부분을 .stream으로 바꿔주기만 하면 된다

config = {"configurable" : {"session_id" : "abc2"}}   # 세션 ID를 설정하는 config 객체 생성

for r in with_message_history.stream(
  [HumanMessage(content = "나에 대해서 뭘 알고 있어?")],
  config = config,
):

  print(r.content, end="|")

실제로 코드를 실행하면 | 단위로 답변이 스트림 방식으로 출력되는 것을 확인할 수 있다

6. 마무리

이번 포스팅에서는 간단하게 랭체인을 이용하는 방법에 대해서 알아보았다
공부하기 전에는 되게 어려운 고급기술일것이라 생각했는데 막상 공부해보니 언어모델을 사용하는 것을 더 쉽게 만들어 주는 도구라는 것을 알았다
캡스톤 디자인을 할 때에는 단순히 오픈AI의 API만 사용했는데 그때도 랭체인을 알고 활용할 수 있었다면 랭체인을 이용해서 더 많은 언어모델을 활용할 수 있지 않았을까 라는 생각이 들었다

다음 포스팅에서는 "LCEL로 체인 만들기" 라는 실습을 진행해보려 한다
일단 LCEL이 뭔지 1도 모르지만.... 그래도 책을 열심히 따라가보면 알게되지 않을까

그리고 구글링을 하다가 꽤나 괜찮은 문서를 발견해서 링크를 남겨놓으려 한다
랭체인 위키 독스

profile
Per ardua ad astra

0개의 댓글