에이전트형 RAG (Agentic RAG)
에이전트형 RAG (Agentic RAG)
질문에 답할 때, 그냥 답을 뱉는 모델보다 근거가 분명한 답이 훨씬 믿음직스러워요. 이 페이지는 검색(리트리벌) 능력을 가진 에이전트로 에이전트형 RAG 시스템을 만드는 방법을 소개해요.
RAG(검색 증강 생성)란 무엇인가요?
검색 증강 생성(RAG, Retrieval-Augmented Generation)은 대형 언어 모델의 힘과 외부 지식 검색을 결합해서 더 정확하고 사실에 근거한 답을 만드는 방식이에요. 핵심은 "LLM으로 사용자 질문에 답하되, 그 답의 근거를 지식 베이스에서 검색한 정보에 두는 것"이에요.
RAG를 왜 쓸까요?
RAG는 그냥 LLM이나 파인튜닝한 모델을 쓰는 것보다 여러 장점이 있어요.
- 사실 근거(Factual Grounding) — 답을 검색된 사실에 고정시켜서 환각을 줄여줘요
- 도메인 특화(Domain Specialization) — 모델을 다시 학습시키지 않고도 특정 분야 지식을 넣을 수 있어요
- 최신 지식(Knowledge Recency) — 모델의 학습 시점을 넘어선 정보에도 접근할 수 있어요
- 투명성(Transparency) — 생성된 내용에 출처를 달 수 있어요
- 통제(Control) — 모델이 어떤 정보에 접근할 수 있는지 정밀하게 조절할 수 있어요
기존 RAG의 한계
장점이 있긴 하지만, 기존 RAG 방식도 몇 가지 문제를 안고 있어요.
- 단일 검색 단계 — 처음 검색 결과가 별로면 최종 생성 결과도 망가져요
- 질문과 문서의 불일치 — 사용자 질문(보통 의문문)과 답이 담긴 문서(보통 평서문)가 잘 맞지 않을 수 있어요
- 제한된 추론 — 단순한 RAG 파이프라인은 다단계 추론이나 질문 다듬기를 지원하지 않아요
- 컨텍스트 윈도우 제약 — 검색된 문서가 모델의 컨텍스트 윈도우 안에 들어가야 해요
에이전트형 RAG: 더 강력한 접근
이런 한계를 검색 능력을 갖춘 에이전트로 극복할 수 있어요. 에이전트형 RAG는 RAG를 고정된 파이프라인이 아니라 상호작용하는 추론 중심의 과정으로 바꿔요.
에이전트형 RAG의 핵심 이점
검색 도구를 가진 에이전트는 이런 일을 할 수 있어요.
- ✅ 최적화된 검색 쿼리 생성 — 사용자 질문을 검색에 잘 맞는 쿼리로 바꿔줘요
- ✅ 여러 번 검색 수행 — 필요에 따라 반복적으로 정보를 검색해요
- ✅ 검색 내용에 대한 추론 — 여러 출처를 분석·종합해서 결론을 내려요
- ✅ 자기 비판과 개선 — 검색 결과를 평가하고 접근 방식을 조정해요
이 방식은 고급 RAG 기법을 자연스럽게 구현해요.
- 가상 문서 임베딩(HyDE) — 사용자 쿼리를 그대로 쓰지 않고 검색에 최적화된 쿼리를 만들어요 (논문 참고)
- 자가 쿼리 개선(Self-Query Refinement) — 에이전트가 초기 결과를 분석하고 다듬어진 쿼리로 후속 검색을 수행해요 (기법 참고)
에이전트형 RAG 시스템 만들기
Hugging Face Transformers 라이브러리에 관한 질문에 그 문서를 검색해서 답하는 에이전트를 단계별로 만들어 볼게요. 아래 코드를 따라 하거나, smolagents GitHub 저장소의 전체 예제(examples/rag.py)를 참고해도 돼요.
1단계: 필요한 의존성 설치
먼저 필요한 패키지를 설치해요.
pip install smolagents pandas langchain langchain-community sentence-transformers datasets python-dotenv rank_bm25 --upgrade
Hugging Face Inference API를 쓸 거라면 API 토큰을 설정해야 해요.
# 환경 변수(HF_TOKEN 포함) 로드
from dotenv import load_dotenv
load_dotenv()
2단계: 지식 베이스 준비
Hugging Face 문서 데이터셋을 불러와서 검색용으로 준비해요.
import datasets
from langchain.docstore.document import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.retrievers import BM25Retriever
# Hugging Face 문서 데이터셋 로드
knowledge_base = datasets.load_dataset("m-ric/huggingface_doc", split="train")
# Transformers 문서만 골라내기
knowledge_base = knowledge_base.filter(lambda row: row["source"].startswith("huggingface/transformers"))
# 데이터셋 항목을 Document 객체로 변환 (메타데이터 포함)
source_docs = [
Document(page_content=doc["text"], metadata={"source": doc["source"].split("/")[1]})
for doc in knowledge_base
]
# 검색 성능을 위해 문서를 작은 청크로 분할
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 청크당 문자 수
chunk_overlap=50, # 컨텍스트 유지를 위한 청크 간 겹침
add_start_index=True,
strip_whitespace=True,
separators=["\n\n", "\n", ".", " ", ""], # 분할 우선순위
)
docs_processed = text_splitter.split_documents(source_docs)
print(f"Knowledge base prepared with {len(docs_processed)} document chunks")
3단계: 검색 도구(Retriever Tool) 만들기
이제 에이전트가 지식 베이스에서 정보를 검색할 수 있는 맞춤 도구를 만들어요.
from smolagents import Tool
class RetrieverTool(Tool):
name = "retriever"
description = "Uses semantic search to retrieve the parts of transformers documentation that could be most relevant to answer your query."
inputs = {
"query": {
"type": "string",
"description": "The query to perform. This should be semantically close to your target documents. Use the affirmative form rather than a question.",
}
}
output_type = "string"
def __init__(self, docs, **kwargs):
super().__init__(**kwargs)
# 처리된 문서로 검색기 초기화
self.retriever = BM25Retriever.from_documents(
docs, k=10 # 가장 관련성 높은 문서 10개 반환
)
def forward(self, query: str) -> str:
"""제공된 쿼리를 기반으로 검색 실행"""
assert isinstance(query, str), "Your search query must be a string"
# 관련 문서 검색
docs = self.retriever.invoke(query)
# 가독성을 위해 검색된 문서 포맷팅
return "\nRetrieved documents:\n" + "".join(
[
f"\n\n===== Document {str(i)} =====\n" + doc.page_content
for i, doc in enumerate(docs)
]
)
# 처리된 문서로 검색 도구 초기화
retriever_tool = RetrieverTool(docs_processed)
[!TIP] 여기서는 BM25(어휘 기반 검색)를 단순함과 속도를 위해 사용해요. 운영 시스템에서는 임베딩을 활용한 의미 검색이 검색 품질을 높여줄 수 있어요. 고품질 임베딩 모델은 MTEB 리더보드에서 확인할 수 있어요.
4단계: 고급 검색 에이전트 만들기
이제 검색 도구를 사용해서 질문에 답할 수 있는 에이전트를 만들어요.
from smolagents import InferenceClientModel, CodeAgent
# 검색 도구를 가진 에이전트 초기화
agent = CodeAgent(
tools=[retriever_tool], # 에이전트가 사용할 도구 목록
model=InferenceClientModel(), # 기본 모델 "Qwen/Qwen3-Next-80B-A3B-Thinking"
max_steps=4, # 추론 단계 수 제한
verbosity_level=2, # 상세한 에이전트 추론 표시
)
# 특정 모델을 쓰려면 이렇게 지정할 수 있어요:
# model=InferenceClientModel(model_id="meta-llama/Llama-3.3-70B-Instruct")
[!TIP] Inference Providers는 서버리스 추론 파트너가 제공하는 수백 개의 모델에 접근을 제공해요. 지원되는 공급자 목록은 여기에서 확인할 수 있어요.
5단계: 에이전트 실행해서 질문에 답하기
Transformers에 관한 질문에 에이전트로 답해 볼게요.
# 정보 검색이 필요한 질문
question = "For a transformers model training, which is slower, the forward or the backward pass?"
# 에이전트 실행해서 답 얻기
agent_output = agent.run(question)
# 최종 답 표시
print("\nFinal answer:")
print(agent_output)
에이전트형 RAG의 실제 활용
에이전트형 RAG 시스템은 다양한 곳에 적용할 수 있어요.
- 기술 문서 도우미 — 복잡한 기술 문서를 사용자가 쉽게 탐색하도록 도와줘요
- 연구 논문 분석 — 과학 논문에서 정보를 뽑아 종합해요
- 법률 문서 검토 — 법적 문서에서 관련 판례와 조항을 찾아줘요
- 고객 지원 — 제품 문서와 지식 베이스를 바탕으로 질문에 답해요
- 교육 튜터링 — 교과서와 학습 자료를 바탕으로 설명을 제공해요
정리하며
에이전트형 RAG는 기존 RAG 파이프라인보다 한 단계 더 나아간 방식이에요. LLM 에이전트의 추론 능력과 검색 시스템의 사실 근거를 결합하면, 더 강력하고 유연하며 정확한 정보 시스템을 만들 수 있어요.
이 페이지에서 보여준 접근은:
- 단일 단계 검색의 한계를 넘어서고
- 지식 베이스와 더 자연스럽게 상호작용하며
- 자기 비판과 쿼리 개선을 통한 지속적 개선의 틀을 제공해요
여러분만의 에이전트형 RAG를 만들 때는 다양한 검색 방법과 에이전트 구조, 지식 소스를 실험해 보면서 자신의 사용 사례에 맞는 최적 구성을 찾아보세요.