Langchain으로 RAG를 아주 쉽게 구현하게 도와주는 RetrievalQA 가 이제 가신다고 한다...
아주 친절하게도 Langchain 은 그 대안을 제공하는데 그에 맞춰서 구현해 봤다.
뭔가 한방에 체이닝 하는 느낌이 아니라 아쉽긴 한데 아예 create_retrieval_chain 을 따로 만들어놔서 직관적이긴 하다.
# 수정 전
qa_chain = RetrievalQA.from_chain_type(
llm, # llama3.2:1b
retriever=chroma_database.as_retriever(),
chain_type_kwargs={"prompt": prompt}
)
# 수정 후
# llm 과 프롬프트 엮기
llm_prompt_chain = create_stuff_documents_chain(
llm,
retrieval_qa_chat_prompt
)
# llm + 프롬프트 + retriever 엮기
retrieval_chain = create_retrieval_chain(retriever, llm_prompt_chain)
나는 배달앱을 싫어한다. 그냥 아무 뉴스나 긁어온것이다.
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.schema import Document
def getRetriever():
# 1. 일반 텍스트 리스트
texts = [
"""
김동연 경기도지사가 “도민 여러분들께서는 장바구니 물가 걱정 좀 덜었으면 한다”고 말했다.
김 지사는 지난 21일 오산 오색시장에서 열린 ‘2025 경기 살리기 통큰세일’ 개막식에서 “경기도가 통 크게 준비했다. 작년 40억 예산을 올해 100억으로 2.5배 늘렸다”고 설명하면서 이같이 밝혔다.
경기 살리기 통큰세일은 경기지역 400여 개 전통시장과 골목상권이 참여하는 소비촉진행사로, 이날부터 9일간 대장정에 돌입했다.
통큰세일은 경기지역 소비를 촉진하고 지역 내 경제 선순환을 유도하기 위해 경기도와 경기도시장상권진흥원이 지난해부터 추진해 온 프로젝트다.
특히 올해는 상권별 행사기간을 통일하고, 상반기와 하반기에 두 번 추진하는 것으로 정례화해 도민 혼선 없이 집중 효과를 낼 수 있도록 기획됐다.
김 지사는 “(참여)시장도 2배 이상 늘려서 경기도의 전통시장과 골목상권 다 합쳐서 400곳 넘는 곳 전부 혜택 볼 수 있도록 했다”며 “통큰세일을 계기로 지금 가장 어려움을 겪고 있는 소상공인, 자영업자 또 골목상권 계시는 많은 분들 힘내시기 바란다. 시장이 활기차게 돌아가고 장사가 잘 돼서 상권이 다시 살아나기를 바란다”고 말했다. 이어 “최대 20%까지 할인(환급) 행사까지 하니까 (도민들은) 마음껏 이용해 주시기 바란다”고 덧붙였다.
김 지사는 개막식 후 시장을 돌며 온누리상품권과 현금으로 과일과 채소 등 15만원가량 장을 봤다. 이 과정에서 통큰세일 혜택으로 온누리상품권 2만원을 환급받아 인근 가게에서 수박 한 통을 구매하고 현장을 떠났다.
올해 통큰세일은 사업비를 전년 대비 2.5배(40억→100억원) 확대해 도내 400여 개 전통시장과 골목상권에서 29일까지 동시에 진행된다. 경기지역화폐와 온누리상품권 등을 활용한 최대 20% 페이백(1일 1인 최대 3만원)을 중심으로 구성됐다. ‘배달특급’ ‘먹깨비’ ‘땡겨요’ 등 공공배달앱도 할인쿠폰을 발행하는 등 참여해 음식업 소상공인의 매출 증대도 도모한다.
"""
]
# 2. 텍스트를 Document로 래핑
docs = [Document(page_content=t) for t in texts]
# 3. 임베딩 모델 선택 (HuggingFace 등)
embedding_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
# 4. 벡터 스토어 생성 (로컬 저장 없이 메모리에서만)
vectorstore = FAISS.from_documents(docs, embedding_model)
# 5. retriever 로 변환
retriever = vectorstore.as_retriever()
return retriever
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
from langchain import hub
from basic_retriever import getRetriever
# 프롬프트 갖다쓰기.
retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat")
# ChatOllama
llm = ChatOllama(
model = "exaone3.5:32b",
temperature = 0.8,
num_predict = 256
)
# 데이터
retriever = getRetriever()
# llm 과 프롬프트 엮기
llm_prompt_chain = create_stuff_documents_chain(
llm,
retrieval_qa_chat_prompt
)
# llm + 프롬프트 + retriever 엮기
retrieval_chain = create_retrieval_chain(retriever, llm_prompt_chain)
# 질의
result = retrieval_chain.invoke({"input": "공공배달앱으로는 무엇이 있는가?"})
# 결과
print(result["answer"])
langchain-ai/retrieval-qa-chat 이 모든 llm 에게 허용되진 않는다. (by ChatGpt)
허깅페이스의 tokenizer.apply_chat_template 와 비슷하게 프롬프트를 만들어 보자. 얼핏 보면 Langchain 이 훨씬 깔끔하고 정갈한 느낌이 있다.
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
from langchain import hub
from basic_retriever import getRetriever
# 프롬프트 갖다쓰기.
# retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat")
template = """<|begin_of_text|><|start_header_id|>system<|end_header_id|>
당신은 친절하고 유능한 AI 비서입니다.<|eot_id|><|start_header_id|>user<|end_header_id|>
다음 정보를 참고하여 질문에 답해주세요. \n
{context} \n
질문: {input}<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
retrieval_qa_chat_prompt = ChatPromptTemplate.from_template(template)
# ChatOllama
llm = ChatOllama(
model = "exaone3.5:32b",
temperature = 0.8,
num_predict = 256
)
# 데이터
retriever = getRetriever()
# llm 과 프롬프트 엮기
llm_prompt_chain = create_stuff_documents_chain(
llm,
retrieval_qa_chat_prompt,
document_variable_name="context" # 프롬프트에 context로 전달
)
# llm + 프롬프트 + retriever 엮기
retrieval_chain = create_retrieval_chain(retriever, llm_prompt_chain)
# 질의
result = retrieval_chain.invoke({"input": "공공배달앱으로는 무엇이 있는가?"})
# 결과
print(result["answer"])