LangChain과 GPT-4o-mini로 구현하는 간단한 페르소나 챗봇

woody.ahn·2024년 7월 29일
0
post-thumbnail

시작하며

OpenAI의 gpt-4o-mini API 가격이 엄청 낮습니다. 1M 입력 로큰이 US$0.15 이고, 1M 출력 토큰이 US$0.6 입니다.

OpenAI가 제공하는 tokenizer 로 확인해 보니 gpt-3.5 & gpt-4 기준으로 한 글자당 한 토큰이네요.

gpt-4o-mini도 한 글자당 한 토큰이라면 1,048,576 글자를 출력하는데 US$0.6 이라는거죠.
참고로 '해리 포터와 마법사의 돌'의 글자 수가 약 20만 자임을 감안하면, 이 가격이 얼마나 저렴한지 실감할 수 있습니다.

그래서 써보기로 하고 US$10 지르고 API Key 발급 받았습니다.

LangChain이란?

OpenAI에서 제공하는 API로 구현 할 수도 있지만 LangChain으로 해볼까 합니다.

claude에게 LangChain에 대해서 설명해 달라고 하니 아래처럼 알려주네요. ^^

LangChain은 대규모 언어 모델(LLM)을 활용한 애플리케이션 개발을 위한 프레임워크입니다. 이 프레임워크는 다양한 AI 모델과 도구들을 쉽게 통합할 수 있게 해주며, 복잡한 작업 흐름을 구현하는 데 필요한 여러 컴포넌트를 제공합니다.

LangChain의 주요 특징:

  • 다양한 LLM 지원
  • 프롬프트 관리 및 최적화
  • 메모리 기능을 통한 대화 컨텍스트 유지
  • 외부 데이터 소스와의 연동
  • 복잡한 작업을 위한 체인과 에이전트 구현

주요 코드 분석

필요한 패키지를 설치하고 임포트

LangChain을 사용하기 위해서 필요한 패키지를 설치합니다.

pip3 install langchain langchain_openai

그리고 필요한 모듈을 import 합니다.

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain.schema.runnable import RunnableLambda, RunnablePassthrough

언어 모델 설정

GPT-4o-mini를 사용하기 위해 API 키를 설정하고 ChatOpenAI 컴포넌트를 초기화합니다:

# OpenAI API Key
os.environ["OPENAI_API_KEY"] = (
	"your-openai-api-key"
)

# initialize language model
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)

대화 히스토리용 메모리 설정

LLM은 본질적으로 stateless이기 때문에, 대화의 맥락을 유지하기 위해서는 대화 히스토리를 저장하고 매번 LLM에 전송해야 합니다:

# memory key
HISTORY_MEMORY_KEY = "chat_history"

# initialize memory for chat history
memory = ConversationBufferMemory(
    memory_key=HISTORY_MEMORY_KEY,
    return_messages=True,
)
runnable = RunnablePassthrough.assign(
    chat_history=RunnableLambda(memory.load_memory_variables)
    | itemgetter(HISTORY_MEMORY_KEY)
)

여기서는 ConversationBufferMemory를 사용했지만, LangChain은 다양한 메모리 유형을 제공합니다.

LangChain에서 제공하는 메모리

  • ConversationBufferMemory: 모든 대화 유지
  • ConversationBufferWindowMemory: 최근 K개의 대화만을 유지.
  • ConversationTokenBufferMemory: 토큰 길이로 대화 내용 유지.
  • ConversationEntityMemory: 대화에서 특정 entity에 대한 주어진 사실을 기억. 대화에서 entity에 대한 정보를 추출하고 해당 entity에 대한 지식을 축적함.
  • ConversationKGMemory: 지식 그래프를 사용한다고 함.
  • ConversationSummaryMemory: 대화가 진행되는 동안 대화를 요약하여 저장.
  • ConversationSummaryBufferMemory: 이전 대화는 요약해서 저장하고, 최근 대화 내용도 유지
  • VectorStoreRetrieverMemory: 메모리로 벡터 DB를 사용. 상위 K개 쿼리.

프롬프트 템플릿 정의

ChatPromptTemplate을 사용하여 LLM에 전달할 프롬프트 템플릿을 정의합니다.

# create prompt for persona chat
system_prompts = "".join(
    [
        "너의 이름은 민지.",
		"취미: 영화보기, 요리하기, 쇼핑하기",
        "너는 대화할 때 이모티콘을 자주 써.",
    ]
)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system_prompts),
        MessagesPlaceholder(variable_name=HISTORY_MEMORY_KEY),
        ("human", "{question}"),
    ]
)

여기서는 챗봇에 간단한 페르소나를 부여하고, 대화 히스토리와 사용자 입력을 포함하도록 템플릿을 구성했습니다.

체인 구성

LCEL(LangChain Expression Language)을 사용해서 memory, prompt, llm을 하나의 체인으로 묶어줍니다.

# create chain
chain = runnable | prompt | llm | StrOutputParser()

이렇게 구성된 체인은 다음과 같은 흐름으로 동작합니다.

  1. 대화 히스토리 로드
  2. 프롬프트 템플릿에 히스토리와 사용자 입력 적용
  3. LLM을 통한 응답 생성
  4. 응답을 문자열로 파싱

대화

마지막으로 CLI로 사용자 입력을 받아서 chain을 통해 LLM에게 질문하고. 응답을 출력, 저장합니다.

while True:
    user_input = input("You: ")
        
    response = chain.invoke({"question": user_input})
    memory.save_context({"input": user_input}, {"output": response})
    
    print("Assistant:", response)

마치며

이렇게 LangChain을 사용하면 페르소나를 가진 챗봇을 비교적 적은 코드로 구현할 수 있습니다. 이 예제에서는 간단하게 페르소나를 정의하고 대화 기록 유지 기능만을 구현했지만, LangChain의 다양한 기능을 활용하면 더욱 복잡하고 흥미로운 챗봇을 만들 수 있을 것입니다.
전체 코드는 GitHub 레포지토리에서 확인할 수 있습니다. 링크

profile
developer

2개의 댓글

comment-user-thumbnail
2025년 7월 7일

우디님 응원합니다!!

답글 달기
comment-user-thumbnail
2025년 7월 31일

우디님 많이 배우고 갑니다.

답글 달기