AI/LLM

QWQ vs Llama3.3 Ollama 기반 Agentic RAG 해보기

bigc 2025. 4. 9. 14:08
반응형

Agentic RAG를 두 가지의 llm으로 테스트해 봤습니다.

 

추론 오픈 모델 중 성능 높다고 알려진 QWQ-32B와 비추론 모델인 Llama3.3-70B입니다. 

 

이미 Llama4도 나오긴 했으나, H100 한대로는 아직 어려워서 해보진 않았습니다. Ollama에 양자화 모델(말로는 60GB 정도 사용) 뜨면 해보겠습니다.

 

원래 QWQ전에 여러 모델들 테스트해 봤는데, 지금까진 Llama3.3-70B가 한국어에 무난해서 사용하고 있었습니다.

 

QWQ-32B를 로컬에서 실행하려면 최소 24GB VRAM이 필요합니다. 저는 H100 80GB 사용했습니다.

 

 

우선 Ollama를 사용했기에 아래 명령으로 Ollama, QWQ 설치해 줍니다.

curl -fsSL https://ollama.com/install.sh | sh   # Ollama 리눅스 설치
ollama serve # Ollama 실행
ollama pull qwq:32b # QWQ 32b 다운로드

 

다음 아래 코드는 벡터 DB에 데이터를 저장하기 위해 폴더 안의 모든 pdf파일을 읽는 코드입니다. 

국토교통부 공통설계기준 pdf 데이터를 사용했습니다.

import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"

import weaviate
from langchain_weaviate.vectorstores import WeaviateVectorStore
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_huggingface import HuggingFaceEmbeddings

# Kicon_data 폴더 내 모든 PDF 파일 경로 가져오기
pdf_folder_path = "Kicon_data"
pdf_files = [os.path.join(pdf_folder_path, file) for file in os.listdir(pdf_folder_path) if file.lower().endswith(".pdf")]

# 1. PDF 전체 로드
all_pages = []

# PDF 로드 + 마크다운으로 표 추출
for pdf_path in pdf_files:
    loader = PyMuPDFLoader(
        pdf_path,
        mode="page",
        extract_tables="markdown",  # ✅ 테이블을 마크다운 형식으로 추출
    )
    pages = loader.load()
    all_pages.extend(pages) # 여러 파일의 페이지를 전부 모음

# 2. 텍스트 분할기 설정
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
)

# 3. 문서 분할
documents = text_splitter.split_documents(all_pages)

# Iterate over each Document object in the texts list
for page in documents:
    # Replace '\x01' with an empty string in the page_content
    page.page_content = page.page_content.replace('\x01', '').replace('\n', '')

 

허깅페이스에 로그인해 주고 embedding 모델을 입력합니다.

차원 대비 용량 효율 좋은 모델이 e5-large라 거의 e5-large multilingual을 사용합니다.

!huggingface-cli login --token 자기토큰입력
from langchain_huggingface import HuggingFaceEmbeddings
model_name = "embaas/sentence-transformers-multilingual-e5-large"
emb = HuggingFaceEmbeddings(model_name=model_name)

 

아래는 벡터 DB(weaviate)에 DB 저장 코드입니다. 

import weaviate
from weaviate.auth import AuthApiKey

auth_config = weaviate.AuthApiKey(api_key="Weaviate 개인 키 넣기")
client = weaviate.connect_to_custom(
    http_host="localhost",
    http_port="9090",
    http_secure=False,
    auth_credentials=auth_config,
    grpc_host="localhost",
    grpc_port="50051",
    grpc_secure=False,
)

from langchain_weaviate.vectorstores import WeaviateVectorStore

# Add to vectorDB
vector_store = WeaviateVectorStore.from_documents(
    client=client,
    documents=documents,
    text_key="page_content",  # Assuming you have stored documents with the text field named "text"
    embedding=emb,
)

 

아래 코드로 QWQ 모델을 불러옵니다.

retriever = vector_store.as_retriever(search_type="similarity", k=4) # weaviate 벡터 데이터베이스에서 유사한 문서를 검색하는 검색기(retriever) 생성 과정

from langchain_ollama import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

#llm = ChatOllama(model="llama3.3", temperature=0.2, top_p=0.2, eos_token_id=128009)
llm = ChatOllama(model="qwq", temperature=0.2, top_p=0.2, eos_token_id=128009)
#llm = ChatOllama(model="qwen2.5:72b", temperature=0.2, top_p=0.2, eos_token_id=128009)

template = "{topic} 에 대하여 간략히 설명해 줘."
prompt = ChatPromptTemplate.from_template(template)
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"topic": "단계별 상세 지반조사에서 예비조사 목적"})

 

아래는 Retrieval QA를 구성하고 Agentic RAG 챗봇을 구성하는 코드입니다.

from langchain.chains import RetrievalQA

# Create the Retrieval QA chain
retrieval_qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=retriever,  # weaviate 벡터 데이터베이스에서 유사한 문서를 검색하는 검색기(retriever)
    return_source_documents=True # 검색된 문서(출처)를 함께 반환
)

from langchain.agents import initialize_agent, Tool, AgentType

# Combine tools and retrieval chain
tools = [
    Tool(
        name="Document Retrieval", # 문서 검색을 수행하는 도구
        func=lambda q: retrieval_qa_chain({"query": q})["result"], # 사용자가 질문을 입력하면 retrieval_qa_chain 호출하여 답변 생성
        description="Retrieve knowledge from the document database." # 문서 데이터베이스에서 정보를 검색합니다
    ), # Document database에서 정보를 검색하는 역할
]
# Initialize the agent
agent = initialize_agent(
    tools=tools,
    llm=llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)

def chatbot_agentic_rag():
    print("Agentic RAG Chatbot is running! Type 'exit' to quit.")
    while True:
        user_query = input("You: ")
        if user_query.lower() == "exit":
            print("Chatbot session ended.")
            break
        try:
            response = agent.run(user_query)
            print(f"Bot: {response}")
        except Exception as e:
            print(f"Error: {e}")

 

아래 코드로 실행

re = chatbot_agentic_rag()

 

 

- 결과

 

문서에서 일반 텍스트로 되어있는 부분에 대한 질문은 대부분 llm이 비슷하게 답합니다.

 

 

위와 같은 이중표가 섞여있는 표에서 차이가 발생합니다.

 

위 결과를 보시면 Llama3.3은 표 부분을 아얘 찾지 못함.

 

QWQ 모델은 추론 모델이라 혼잣말이 많은데 찾아냄.

 

 

제가 테스트해 봤을 때는 QWQ가 더 쓸만한 것 같습니다.

 

Qwen2.5-72b 모델도 사용해 봤는데, 중국어 답을 너무 많이 하더라고요.

 

요새 Qwen의 모델들 성능이 좋은 것 같아 조만간 출시될 Qwen3가 매우 기대됩니다.

  

 

 

반응형