LangChain을 활용한 RAG 구성
LangChain을 활용한 Retrieval Augmented Generation(RAG) 구성하는 과정과 코드를 정리한 포스트입니다.
LangChain을 활용한 RAG 구성
🔎 RAG 파이프라인
- 문서의 내용을 읽는다.
- 문서를 쪼갠다.
- 토큰 수 초과로 문서(input) 이 길면 답변 생성이 올래걸리기 때문에 chunk 단위로 저장 필요
- Embedding 모델을 이용해 문서를 vector로 변환해 데이터베이스에 저장
- 질문이 있을 때, Vector Database에 유사도를 검색
- 유사도 검색으로 가져온 문서와 질문을 LLM에 전달
Pinecone 를 활용한 RAG 구성
Pinecone은 대규모 데이터를 다루기에 최적화된 클라우드 기반(SaaS) 완전 관리형 벡터 데이터베이스이며 인프라 설정이나 확장에 신경 쓸 필요 없이 API 연결만으로 수억 개의 벡터 데이터에서 초고속 유사도 검색을 수행할 수 있다.
Knowledge Base 구성을 위한 데이터 생성
RecursiveCharacterTextSplitter를 활용한 데이터 chunking- split 된 데이터 chunk를 Large Language Model(LLM)에게 전달하면 토큰 절약 가능
- 비용 감소와 답변 생성시간 감소의 효과
- LangChain에서 다양한 TextSplitter들을 제공
chunk_size는 split 된 chunk의 최대 크기chunk_overlap은 앞 뒤로 나뉘어진 chunk들이 얼마나 겹쳐도 되는지 지정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import os
from langchain_community.document_loaders import Docx2txtLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
from pinecone import Pinecone
from langchain_pinecone import PineconeVectorStore
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1500,
chunk_overlap=200,
)
loader = Docx2txtLoader('./tax.docx')
document_list = loader.load_and_split(text_splitter=text_splitter)
# 환경변수 불러옴(.env)
load_dotenv()
# OpenAI에서 제공하는 Embedding Model을 활용해서 `chunk`를 vector화
embedding = OpenAIEmbeddings(model='text-embedding-3-large')
index_name = 'tax-index'
pinecone_api_key = os.environ.get("PINECONE_API_KEY")
pc = Pinecone(api_key=pinecone_api_key)
# 데이터를 추가할 때 사용
database = PineconeVectorStore.from_documents(document_list, embedding, index_name=index_name)
# 데이터를 추가한 이후에 사용
database = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embedding)
답변 생성을 위한 Retrieval
- RetrievalQA에 전달하기 위해 retriever 생성
search_kwargs의k값을 변경해서 가져올 문서의 갯수를 지정할 수 있음.invoke()를 호출해서 어떤 문서를 가져오는지 확인 가능
1
2
3
4
5
query = '연봉 5천만원인 직장인의 소득세는 얼마인가요?'
# 'k' 값을 조절해서 얼마나 많은 데이터를 불러올지 결정
retriever = database.as_retriever(search_kwargs={'k': 4})
retriever.invoke(query)
Augmentation을 위한 Prompt 활용
- Retrieval된 데이터는 LangChain에서 제공하는 프롬프트(
"rlm/rag-prompt") 사용
1
2
3
4
5
from langchain_openai import ChatOpenAI
from langchain import hub
llm = ChatOpenAI(model='gpt-4o')
prompt = hub.pull("rlm/rag-prompt")
답변 생성
RetrievalQA를 통해 LLM에 전달
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm,
retriever=database.as_retriever(),
chain_type_kwargs={"prompt": prompt}
)
ai_message = qa_chain.invoke({"query": query})
print(ai_message)
"""
{'query': '연봉 5천만원인 직장인의 소득세는 얼마인가요?',
'result': '소득세는 여러 요소에 따라 달라질 수 있어서 정확한 계산이 필요합니다. 대략적으로 5천만원 연봉의 소득세는 약 5백만원 정도입니다. 하지만 정확한 금액은 국세청의 소득세 계산기를 통해 확인하는 것이 좋습니다.'}
"""
RetrievalQA는create_retrieval_chain으로 대체- 실제 ChatBot 구현 시
create_retrieval_chain으로 변경하는 과정을 볼 수 있음
- 실제 ChatBot 구현 시
1
2
3
4
5
6
7
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
combine_docs_chain = create_stuff_documents_chain(
llm, retrieval_qa_chat_prompt
)
retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain)
Retrieval 효율 개선을 위한 키워드 사전 활용
- Knowledge Base에서 사용되는 keyword를 활용하여 사용자 질문 수정
- LangChain Expression Language (LCEL)을 활용한 Chain 연계
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
dictionary = ["사람을 나타내는 표현 -> 거주자"]
prompt = ChatPromptTemplate.from_template(f"""
사용자의 질문을 보고, 우리의 사전을 참고해서 사용자의 질문을 변경해주세요.
만약 변경할 필요가 없다고 판단된다면, 사용자의 질문을 변경하지 않아도 됩니다.
그런 경우에는 질문만 리턴해주세요
사전: {dictionary}
질문:
""")
dictionary_chain = prompt | llm | StrOutputParser()
tax_chain = {"query": dictionary_chain} | qa_chain
new_question = dictionary_chain.invoke({"question": query})
ai_response = tax_chain.invoke({"question": query})
그 외의 Vector Database
Chroma- 오픈소스 기반의 AI 전용 벡터 데이터베이스로, 임베딩된 벡터 데이터와 메타데이터를 간편하게 저장하고 검색할 수 있게 도와줌
- 별도의 복잡한 설정 없이 Python 코드 몇 줄로 로컬 환경에서 즉시 실행 가능
FAISS- 벡터 검색 라이브러리로 DB라기보다는 엔진에 가까워 속도가 압도적으로 빠르고 무료임
- 서버 구축 없이 내 컴퓨터 메모리(RAM) 상에서 수만~수십만 개의 벡터를 초고속으로 검색할 때 씀
Milvus / Qdrant- 오픈소스 기반의 고성능 전용 벡터 DB 로 직접 서버에 설치(Self-hosting)할 수 있어 데이터 보안이 중요한 기업용 프로젝트에 많이 쓰임
pgvector- 기존에 널리 쓰이는 PostgreSQL 데이터베이스에 벡터 저장 기능을 추가한 것으로 새로운 DB를 배포할 필요 없이 기존 DB 하나로 일반 데이터와 벡터 데이터를 동시에 관리할 수 있어 운영이 매우 단순함
This post is licensed under CC BY 4.0 by the author.