# Agentic RAG (에이전틱 RAG)
## 정의
LLM이 검색 도구의 사용을 **자율적으로 결정**하는 RAG 기법. 전통 RAG의 모든 쿼리에 자동 검색하는 방식을 벗어나, 에이전트 패러다임으로 LLM이 "검색이 필요한가?"를 판단한 후 선택적으로 검색을 수행한다.
## 핵심 원리
### 파이프라인 비교
```
전통 RAG:
쿼리 → [항상 검색] → 문서 취득 → LLM 생성 → 답변
Agentic RAG:
쿼리 → LLM 판단 → {검색 필요?}
├─ YES: 검색 → 문서 취득
└─ NO: (스킵)
↓
LLM 생성 → 답변
```
### 수식
**의사결정 함수:**
$\text{should_search}(q) = \text{LLM}("검색이 필요한가? 쿼리: " + q) \in \{\text{YES}, \text{NO}\}$
**조건부 검색:**
$d = \begin{cases}
\text{Retrieve}(q) & \text{if } \text{should_search}(q) = \text{YES} \\
\emptyset & \text{otherwise}
\end{cases}$
**최종 생성:**
$a = \text{LLM}(q, d)$
### 에이전트 루프
```
상태: (질문, 컨텍스트, 단계)
↓
LLM 호출 (현재 상태 분석)
↓
출력: {Think, Act(search), Answer}
├─ Think: 현재 상황 분석
├─ Act(search): 검색 도구 호출
│ ↓
│ 도구 실행
│ ↓
│ 결과 추가
│
└─ Answer: 최종 답변
↓
종료 또는 다음 루프
```
## 사용 사례
### 1. 간단한 질문 (검색 불필요)
```
질문: "파이썬의 len() 함수는 뭐예요?"
LLM 판단: "기본 내장 함수, 검색 불필요"
→ 검색 스킵
→ LLM이 직접 답변
효과: 응답 시간 단축, 토큰 절감
```
### 2. 복잡한 질문 (검색 필수)
```
질문: "2024년 최신 LLM 벤치마크 결과는?"
LLM 판단: "최신 정보 필요, 검색 필수"
→ 검색 실행
→ 검색 결과 통합
→ 최신 정보 포함 답변
효과: 최신성 보장, 할루시네이션 방지
```
### 3. 다단계 추론 (반복 검색)
```
질문: "RAG와 Fine-tuning의 성능을 비교하면?"
Loop 1:
LLM: "RAG 정보 필요"
→ 검색: "RAG 성능 벤치마크"
Loop 2:
LLM: "Fine-tuning 정보 필요"
→ 검색: "Fine-tuning 성능 벤치마크"
Loop 3:
LLM: "충분한 정보 확보, 비교 가능"
→ 답변 생성
효과: 복잡한 비교 분석 가능
```
## 구현 예시
### 기본 구조 (LangGraph)
```python
from langgraph.graph import StateGraph, END
from typing import TypedDict, List
class State(TypedDict):
question: str
context: str
history: List[str]
def agent_node(state: State) -> State:
"""LLM 에이전트 판단"""
prompt = f"""
현재 정보: {state['context']}
질문: {state['question']}
다음 중 선택하세요:
1. search: 검색이 필요함
2. answer: 답변 가능
"""
decision = llm.predict(prompt)
return {"history": state["history"] + [decision]}
def should_search(state: State) -> str:
"""검색 필요 여부 판단"""
last_decision = state["history"][-1]
if "search" in last_decision:
return "search"
else:
return "answer"
def retriever_node(state: State) -> State:
"""검색 실행"""
docs = retriever.get_relevant_documents(state["question"])
context = "\n".join([d.page_content for d in docs])
return {"context": context}
# 워크플로우 구성
workflow = StateGraph(State)
workflow.add_node("agent", agent_node)
workflow.add_node("retriever", retriever_node)
workflow.add_conditional_edges(
"agent",
should_search,
{"search": "retriever", "answer": END}
)
workflow.add_edge("retriever", "agent")
workflow.set_entry_point("agent")
```
## 장점 vs 단점
### 장점
| 항목 | 설명 |
|------|------|
| **성능** | 불필요한 검색 제거 → 응답 시간 ↓ |
| **비용** | 토큰 사용 감소 → API 비용 ↓ |
| **유연성** | 동적 의사결정으로 다양한 시나리오 대응 |
| **정확도** | 필요한 경우만 검색 → 할루시네이션 방지 |
### 단점
| 항목 | 설명 |
|------|------|
| **복잡성** | 에이전트 루프 + 상태 관리 필요 |
| **예측 불가** | LLM 판단이 항상 정확하지 않음 |
| **오버헤드** | 판단 LLM 호출 추가 비용 |
| **디버깅** | 다단계 루프로 인한 디버깅 어려움 |
## 최적화 전략
### 1. 명시적 지시
```python
system_prompt = """
검색 도구를 사용해야 할 경우:
1. 최신 정보 필요 (시간 민감)
2. 특정 도메인 전문 지식
3. 통계 데이터나 벤치마크
4. 사실 검증이 중요한 경우
검색 스킵 가능한 경우:
1. 일반적인 개념 설명
2. 논리적 추론만 필요
3. 학습자의 이해도 확인
"""
```
### 2. 최대 반복 제한
```python
MAX_ITERATIONS = 5
def should_continue(state: State) -> bool:
return len(state["history"]) < MAX_ITERATIONS
```
### 3. 검색 결과 평가
```python
def evaluate_search_results(docs: List) -> float:
"""검색 결과의 관련성 점수"""
if not docs:
return 0.0
scores = [d.metadata.get("score", 0) for d in docs]
return sum(scores) / len(scores)
```
## 성능 비교
```
시나리오별 성능 (시간, 토큰):
질문 유형 | 전통 RAG | Agentic RAG | 개선
---------|---------|-----------|-----
간단한 질문 | 2.0s, 500토큰 | 0.5s, 50토큰 | 75% 단축
복잡한 질문 | 2.5s, 700토큰 | 4.0s, 800토큰 | -60% (여러 루프)
평균 | 2.2s, 600토큰 | 1.8s, 300토큰 | 35% 개선
```
## 열린 질문
1. **판단 정확성**: LLM이 검색 필요성을 정확히 판단하려면?
2. **루프 제어**: 무한 루프를 방지하면서 필요한 반복은 보장할까?
3. **문맥 축적**: 여러 루프에서 컨텍스트를 효율적으로 관리할 방법은?
4. **사용자 제어**: 사용자가 검색 정책(aggressive vs. conservative)을 제어할 수 있을까?
5. **평가 지표**: Agentic RAG의 성능을 어떻게 정량화할까?