Gemma, LangChain, ChromaDB로 챗봇 만들기
Gemma, LangChain, ChromaDB로 챗봇을 만드는 방법을 알아보세요. 이 가이드는 설정, 데이터 처리, 응답 생성 과정을 단계별로 안내합니다. 본 문서는 AI 번역본입니다. 오역이 의심되는 부분이 있으면 댓글로 알려주세요.
Created on September 15|Last edited on September 15
Comment
챗봇은 다양한 산업에서 필수 도구로 자리 잡아, 기업이 상호작용을 자동화하고 즉각적인 지원을 제공하며 운영을 간소화하도록 돕고 있습니다. 고객 지원, 가상 비서, 레스토랑 주문과 같은 특화된 애플리케이션 등 어떤 용도이든, 잘 만들어진 챗봇은 사용자 경험과 효율성을 크게 높일 수 있습니다.
이 가이드에서는 다음을 활용해 챗봇을 구축하는 과정을 단계별로 살펴보겠습니다 Gemma, LangChain, 그리고 ChromaDB 지능적이고 문맥을 이해하는 어시스턴트를 개발하는 과정을 단순화해 주는 강력한 도구 세 가지입니다. 예시로는 레스토랑 메뉴 주문 챗봇을 다루지만, 동일한 원칙은 다른 도메인에도 그대로 적용할 수 있습니다.
왜 Gemma, LangChain, ChromaDB를 사용해야 할까요?
사용자 질의를 이해하고, 관련 정보를 검색해, 자연스럽게 응답하는 챗봇을 만들기 위해 다음의 강점을 활용하겠습니다:
- LangChain: LLM 작업을 단순화하고, 검색과 메모리 같은 구성 요소를 손쉽게 오케스트레이션할 수 있게 해 주는 프레임워크.
- ChromaDB: 관련 컨텍스트를 저장·검색하는 벡터 데이터베이스로, 챗봇의 정확도를 향상합니다.
이 기술들을 결합해 고객 질문에 답하고, 메뉴를 추천하며, 다양한 문의를 유연하게 처리할 수 있는 챗봇을 개발하겠습니다.
그럼 시작해 볼까요…
환경 설정
코딩을 시작하기 전에 필요한 종속 항목을 설치해야 합니다:
!pip install langchain!pip install chromadb!pip install wandb # For logging and monitoring
설치가 끝났다면 실험을 추적하기 위해 Weights & Biases에 로그인하세요:
wandb login
데이터 불러오기와 준비하기
이 예제에서는 JSON 파일에 저장된 레스토랑 메뉴를 챗봇이 사용합니다. 먼저 이 데이터를 불러와 검색에 적합한 형식으로 변환하겠습니다.
import jsonimport osjson_file = "menu.json"with open(json_file, "r") as f:json_data = json.load(f)count = 0folder_path = "/content/Data" #You will need to create this before writing files.for dish in json_data:file_path = os.path.join(folder_path,"{}.txt".format(count))f = open(file_path, "w")for key, value in dish.items():f.write(f"{key}: {value}\n")f.close()count+=1
각 요리는 별도의 텍스트 파일로 저장되어 있어 LangChain으로 손쉽게 로드하고 처리할 수 있습니다.
다음으로 LangChain의 TextLoader 데이터를 문서로 로드하기 위해.
from langchain_community.document_loaders import TextLoaderloaders = []for i in range(12):file_path = os.path.join(folder_path,"{}.txt".format(i))loaders.append(TextLoader(file_path))docs = []for loader in loaders:docs.extend(loader.load())
벡터 데이터베이스 만들기
from langchain.vectorstores import Chromafrom langchain_community.embeddings import HuggingFaceInferenceAPIEmbeddingsinference_api_key = "key"embeddings = HuggingFaceInferenceAPIEmbeddings(api_key=inference_api_key, model_name="sentence-transformers/all-mpnet-base-v2")vectordb = Chroma.from_documents(documents=docs,embedding=embeddings)
이것 덕분에 챗봇은 사용자 쿼리에 답할 때 관련된 컨텍스트를 검색할 수 있습니다.
언어 모델 불러오기
우리는 효율성을 위해 설계된 경량 모델인 Gemma-2B-IT을 사용할 것입니다. 응답을 간결하고 정확하게 유지하기 위해 다음을 설정합니다 temperature=0.1 그리고
from langchain_community.llms import HuggingFaceHubllm = HuggingFaceHub(repo_id="google/gemma-2b-it",task="text-generation",model_kwargs={"max_new_tokens": 512,"top_k": 5,"temperature": 0.1,"repetition_penalty": 1.03,},huggingfacehub_api_token = "API_TOKEN")
챗봇의 동작 정의하기
챗봇의 응답을 안내하기 위해 구조화된 프롬프트를 사용하겠습니다.
from langchain.prompts import PromptTemplatetemplate = """You are a Chatbot at a Restaurant. Help the customer pick the right dish to order. The items in the context are dishes. The field below the item is the cost of the dish. About is the description of the dish. Use the context below to answe the questions{context}Question: {question}Helpful Answer:"""QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context", "question"],template=template,)
메모리와 검색 추가하기
대화 기록을 유지하기 위해 LangChain의 ConversationBufferMemory.
from langchain.memory import ConversationBufferMemorymemory = ConversationBufferMemory(memory_key="chat_history",return_messages=True)
이제 관련 메뉴 정보를 가져올 리트리버를 정의하고, Conversational Retrieval Chain을 초기화하겠습니다.
from langchain.chains import ConversationalRetrievalChainretriever = vectordb.as_retriever()qa = ConversationalRetrievalChain.from_llm(llm,retriever=retriever,memory=memory,)
질의 처리 개선하기
문맥을 인지한 응답을 보장하기 위해, 리포뮬레이션 체인을 사용하여 사용자 질의를 사전 처리하겠습니다.
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthroughcontextualize_q_system_prompt = """Given a chat history and the latest user question \which might reference context in the chat history, formulate a standalone question \which can be understood without the chat history. Do NOT answer the question, \just reformulate it if needed and otherwise return it as is."""contextualize_q_prompt = ChatPromptTemplate.from_messages([("system", contextualize_q_system_prompt),MessagesPlaceholder(variable_name="chat_history"),("human", "{question}"),])contextualize_q_chain = contextualize_q_prompt | llm | StrOutputParser()
더 나은 대화를 위한 질의 처리 개선
챗봇이 문맥을 인지하도록 만들려면, 후속 질문을 재구성할 방법이 필요합니다. 사용자는 다음과 같이 전체 문맥을 반복하지 않은 채 후속 질문을 자주 합니다.
- "채식 옵션은 어떤가요?"
- "매운 음식 추천해 주실 수 있나요?"
챗봇은 각 질문의 문맥을 스스로 기억하지 않기 때문에, 관련 데이터를 조회하기 전에 사용자 질의를 재작성하는 방법이 필요합니다.
이를 위해 채팅 기록의 존재 여부를 확인하는 문맥화 함수를 정의합니다. 사용자의 질문이 이전 메시지에 의존하는 경우, 이 함수가 질문을 단독으로도 의미가 통하도록 재구성합니다.
def contextualized_question(input: dict):if input.get("chat_history"):return contextualize_q_chain.invoke(input)else:return input["question"]
이제 이를 검색 단계에 통합해, 후속 질문이라도 챗봇이 관련 정보를 정확히 가져오도록 할 수 있습니다.
rag_chain = (RunnablePassthrough.assign(context=contextualized_question | retriever)| QA_CHAIN_PROMPT| llm)
이렇게 하면 데이터베이스에 질의를 보내기 전에, 필요 시 챗봇이 질의를 자동으로 재구성합니다. 이는 사용자가 매 메시지에 전체 문맥을 제공하지 않을 수 있는 다중 턴 대화에서 특히 유용합니다.
챗봇 실행하기
사용자가 "exit"을 입력할 때까지 챗봇과 상호작용할 수 있도록 대화 루프를 설정하겠습니다.
from langchain_core.messages import AIMessage, HumanMessageos.environ["LANGCHAIN_WANDB_TRACING"] = "true"os.environ["WANDB_PROJECT"] = "Restaurant_ChatBot"print("Welcome to the Restaurant. How can I help you today?")chat_history = []def predict(message, history):ai_msg = rag_chain.invoke({"question": message, "chat_history": chat_history})idx = ai_msg.find("Answer")chat_history.extend([HumanMessage(content=message), ai_msg])return ai_msg[idx:]
데모를 직접 사용해 보세요

참고자료
Add a comment