언어 AI (NLP)/LLM & RAG
RAGAS 를 이용한 RAG 평가
bellmake
2025. 2. 27. 19:22
*아래는 전체코드가 아닌 핵심 코드만을 다루고 있습니다.
참고) RAGAS: https://docs.ragas.io/en/latest/getstarted/evaluation.html
from ragas.testset.generator import TestsetGenerator
from ragas.testset.evolutions import simple, reasoning, multi_context, conditional
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
from ragas.testset.extractor import KeyphraseExtractor
from ragas.testset.docstore import InMemoryDocumentStore
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 데이터셋 생성기
generator_llm = ChatOpenAI(model="gpt-4o-mini")
# 데이터셋 비평기
critic_llm = ChatOpenAI(model="gpt-4o-mini")
📌 설명
ChatOpenAI(model="gpt-4o-mini"): OpenAI의 GPT-4o-mini 모델을 LangChain의 LLM 인터페이스로 사용합니다.
데이터셋 생성기 (generator_llm): 평가를 위한 테스트 데이터를 생성하는 역할을 합니다.
데이터셋 비평기 (critic_llm): 생성된 테스트 데이터의 품질을 평가합니다.
🧐 왜 필요한가요?
RAG(Retrieval-Augmented Generation) 시스템을 평가하려면, 다양한 질문과 문서를 포함하는 테스트 데이터셋이 필요합니다.
생성된 데이터가 올바른지 검토하는 **비평기(critic)**가 있어야 데이터의 품질을 보장할 수 있습니다.
# 문서 임베딩
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
📌 설명
OpenAIEmbeddings(model="text-embedding-3-small"): OpenAI의 문서 임베딩 모델을 사용하여 텍스트를 벡터 형식으로 변환합니다.
🧐 왜 필요한가요?
문서를 검색할 때, 단순한 키워드 매칭이 아니라 의미적으로 유사한 문장을 찾기 위해 임베딩이 필요합니다.
# 텍스트 분할기를 설정합니다.
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
# LangChain의 ChatOpenAI 모델을 LangchainLLMWrapper로 감싸 Ragas와 호환되게 만듭니다.
langchain_llm = LangchainLLMWrapper(ChatOpenAI(model="gpt-4o-mini"))
📌 설명
LangchainLLMWrapper: LangChain의 LLM을 **Ragas에서 사용할 수 있도록 변환하는 래퍼(wrapper)**입니다.
LangChain과 Ragas의 LLM 인터페이스가 다르기 때문에, 변환 과정이 필요합니다.
🧐 왜 필요한가요?
LangChain과 Ragas는 서로 다른 방식으로 LLM을 처리하므로, LangChain LLM을 Ragas에서 사용하려면 변환이 필요합니다.
# 주요 구문 추출기를 초기화합니다. 위에서 정의한 LLM을 사용합니다.
keyphrase_extractor = KeyphraseExtractor(llm=langchain_llm)
📌 설명
KeyphraseExtractor: 문서에서 **핵심 키워드(주요 구문)**를 추출하는 도구입니다.
llm=langchain_llm: 위에서 설정한 GPT-4o-mini 모델을 사용하여 중요한 구문을 자동으로 추출합니다.
🧐 왜 필요한가요?
문서를 더 잘 이해하고 요약할 수 있도록 키워드(핵심 구문)를 추출하는 과정이 필요합니다.
검색 시스템에서 가장 관련성이 높은 문서를 찾는 데 도움을 줍니다.
# ragas_embeddings 생성
ragas_embeddings = LangchainEmbeddingsWrapper(embeddings)
📌 설명
LangchainEmbeddingsWrapper: LangChain의 embeddings 객체를 Ragas에서 사용할 수 있도록 감싸는 래퍼입니다.
embeddings: 문서와 질문을 벡터로 변환하는 모델입니다.
🧐 왜 필요한가요?
문서 검색 시 단순한 키워드 매칭이 아니라, 의미적으로 유사한 문장을 찾기 위해 임베딩을 활용해야 합니다.
# InMemoryDocumentStore를 초기화합니다.
# 이는 문서를 메모리에 저장하고 관리하는 저장소입니다.
docstore = InMemoryDocumentStore(
splitter=splitter,
embeddings=ragas_embeddings,
extractor=keyphrase_extractor,
)
📌 설명
InMemoryDocumentStore: 문서를 메모리에 저장하고 관리하는 저장소입니다.
문서를 저장할 때, 다음과 같은 기능을 수행합니다.
splitter=splitter: 텍스트 분할기를 사용하여 문서를 자동으로 적절한 크기로 나눔.
embeddings=ragas_embeddings: 문서 임베딩을 저장하여 유사 문서 검색 가능.
extractor=keyphrase_extractor: 문서를 저장할 때 핵심 구문을 자동으로 추출.
🧐 왜 필요한가요?
문서를 검색하거나 요약할 때 빠르게 접근할 수 있도록 메모리 기반 저장소를 활용합니다.
문서를 검색할 때 **키워드 기반 검색 + 의미적 검색(임베딩 활용)**을 동시에 할 수 있도록 설계되었습니다.
generator = TestsetGenerator.from_langchain(
generator_llm,
critic_llm,
ragas_embeddings,
docstore=docstore,
)
📌 설명
TestsetGenerator.from_langchain(...)를 사용하여 테스트 데이터셋을 자동으로 생성합니다.
매개변수
generator_llm: 테스트 데이터(질문과 문서)를 생성하는 LLM.
critic_llm: 생성된 데이터를 평가하는 LLM.
ragas_embeddings: 의미적 검색을 위한 문서 임베딩.
docstore: 문서가 저장된 메모리 저장소.
🧐 왜 필요한가요?
RAG 시스템을 평가하려면 다양한 문서와 질문 쌍을 포함하는 테스트셋이 필요합니다.
이 과정이 자동화되면 효율적으로 테스트셋을 구축할 수 있습니다.
# 질문 유형별 분포 결정
# simple: 간단한 질문, reasoning: 추론이 필요한 질문, multi_context: 여러 맥락을 고려해야 하는 질문, conditional: 조건부 질문
distributions = {simple: 0.4, reasoning: 0.2, multi_context: 0.2, conditional: 0.2}
# 테스트셋 생성
# docs: 문서 데이터, 10: 생성할 질문의 수, distributions: 질문 유형별 분포, with_debugging_logs: 디버깅 로그 출력 여부
testset = generator.generate_with_langchain_docs(
documents=docs,
test_size=10,
distributions=distributions,
with_debugging_logs=True,
raise_exceptions=False,
)
# 생성된 테스트셋을 pandas DataFrame으로 변환
test_df = testset.to_pandas()
test_df
# DataFrame을 CSV 파일로 저장
test_df.to_csv("data/ragas_synthetic_dataset.csv", index=False)