본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 17. 14:49

RAG 시리즈 (17): Agentic RAG — 검색 과정에 에이전트의 제어권 부여

요약

기존의 Pipeline RAG는 질문 → 벡터 검색 → 문서 전달 → LLM 생성이라는 선형적이고 고정된 시퀀스를 따르며, 검색 결과가 불충분할 때 시스템이 실패를 인지하지 못하고 부정확한 답변을 내놓거나 회피하는 '조용한 실패'에 빠지는 한계가 있습니다. Agentic RAG는 여기에 에이전시(Agency) 개념을 도입하여, 질문 유형 분석, 도구 선택, 검색 결과 평가(Reflection), 그리고 실패 시 전략 전환 및 재시도 메커니즘을 포함한 의사결정 기반의 피드백 루프를 구축합니다. 이로써 시스템은 스스로의 검색 과정을 모니터링하고 품질이 낮으면 다른 전략으로 수정하여 더 정확하게 답변할 수 있습니다.

핵심 포인트

  • Agentic RAG는 고정된 파이프라인을 의사결정이 포함된 피드백 루프로 전환합니다.
  • 질문 유형에 따라 벡터 검색, 그래프 탐색, 웹 검색 등 적절한 도구를 선택하는 '분류(classify)' 단계가 추가됩니다.
  • 검색 후 에이전트가 컨텍스트의 품질을 평가(Reflection)하고 임계값 미만일 경우 실패로 간주합니다.
  • 실패 시에는 다른 전략으로 재라우팅하거나 재시도하여 답변의 정확도를 높입니다.
  • LangGraph와 같은 아키텍처를 사용하여 실행 추적(Execution Trace) 및 디버깅이 용이해집니다.

Pipeline RAG의 조용한 실패

이 시리즈의 모든 글은 동일한 질문에 답하기 위해 노력해 왔습니다: 어떻게 하면 검색 (Retrieval)을 더 개선할 수 있을까? 더 나은 청킹 (Chunking), 재순위화 (Reranking), 쿼리 재작성 (Query Rewriting), CRAG의 웹 폴백 (Web fallback), Graph RAG의 관계 탐색 (Relationship traversal) 등이 그 방법들입니다. 하지만 과정 내내 변하지 않은 한 가지가 있습니다: 검색 결과가 무엇이든 간에, 그것은 LLM으로 전달된다는 점입니다. Pipeline RAG는 선형적이고 고정된 시퀀스입니다: 질문 → 벡터 검색 (Vector search) → 상위 4개 문서 (Top-4 docs) → LLM 생성. 시스템은 어느 시점에서도 "방금 검색한 내용이 이 질문에 답하기에 실제로 충분한가?"라고 묻지 않습니다. 그 결과: 지식 베이스 (Knowledge base)에 관련 내용이 없을 때, LLM은 관련 없는 네 개의 문서를 전달받고 조용히 환각 (Hallucination) 답변을 내놓거나 "제공된 컨텍스트를 바탕으로는 답변할 수 없습니다"라며 회피합니다. 시스템은 자신이 실패했다는 것을 알지 못합니다. 그저 조용히 틀린 답을 전달할 뿐입니다. 이것이 Pipeline RAG의 조용한 실패 모드입니다.

Agentic RAG가 바꾸는 것

Agentic RAG는 한 가지를 추가합니다: 에이전시 (Agency, 주체성). 세 가지 구체적인 변화는 다음과 같습니다:

  1. 검색은 고정된 단계가 아닌 도구입니다
    벡터 검색 (Vector search), 그래프 탐색 (Graph traversal), 웹 검색 (Web search) — 이들은 모두 한꺼번에 사용되거나 항상 동일한 방식이 아닙니다. 에이전트는 어떤 종류의 질문이 던져졌느냐에 따라 적절한 도구를 선택합니다. 사실 관계를 묻는 질문은 벡터 검색으로 갑니다. 관계를 묻는 질문은 그래프 탐색으로 갑니다. 시간에 민감한 질문은 웹 검색으로 갑니다. 일반적인 지식 질문은 검색 과정을 완전히 건너뜁니다. 이것이 실제 인간 연구자가 정보를 찾는 방식을 결정하는 방식입니다.

  2. 검색 뒤에는 성찰 (Reflection)이 따릅니다
    검색을 실행한 후, 에이전트는 즉시 답변을 생성하지 않습니다. 에이전트는 평가합니다: "이 검색된 컨텍스트가 질문을 얼마나 잘 커버하고 있는가?" 만약 점수가 임계값 (Threshold) 미만이라면, 검색이 실패한 것으로 간주합니다.

  3. 실패를 수정할 수 있습니다
    품질이 낮으면 → 전략을 전환하여 재시도합니다. 벡터 검색이 유용한 것을 찾지 못했나요? 그래프 탐색을 시도하세요. 그래프 탐색이 실패했나요? 웹 검색을 시도하세요. 무한 루프를 방지하기 위해 재시도 루프에는 엄격한 제한 (이 구현에서는 2회 시도)이 설정되어 있습니다.

이 세 가지 변화가 결합되어 "고정된 파이프라인 (fixed pipeline)"은 "의사결정이 포함된 피드백 루프 (feedback loop with decisions)"로 변모합니다. LangGraph 아키텍처 질문 ↓ [classify] → 질문 유형 분석, 초기 전략 선택
factual (사실적) → vector (벡터)
relational (관계적) → graph (그래프)
time-sensitive (시간 민감적) → web (웹)
general knowledge (일반 지식) → direct (직접)
↓ [retrieve] → 선택된 전략 실행 (세 개의 별도 노드)
↓ [evaluate] → 컨텍스트 품질 점수 산출 (0.0–1.0), 임계값 (threshold) = 0.6
↓ ≥0.6 ──yes──→ [generate] → 최종 답변
│ no (시도 횟수 < 2) ↓ [re_route] → 다음 미시도 전략 선택: vector → graph → web
↓ [retrieve] 다시 실행...
↓ (2회 시도 후에는 결과에 상관없이 생성)
direct_generate 경로는 모든 검색 (retrieval) 노드를 우회하여 즉시 END로 이동합니다.

주요 노드 구현
상태 (State): 실행 추적 (Execution Trace)은 당신의 디버거입니다
class AgenticRAGState ( TypedDict ):
question : str
strategy : str # "vector" | "graph" | "web" | "direct"
tried_strategies : list [ str ] # 이미 시도된 전략들, 중복 방지
retrieved_docs : list [ Document ]
quality_score : float # evaluate 노드로부터의 점수, 0.0–1.0
answer : str
path : list [ str ] # 실행 추적: ["classify→graph", "graph_retrieve", ...]

path 필드는 전체 시스템에서 가장 유용한 디버깅 도구입니다. 실행이 끝난 후, 각 질문이 정확히 어떤 경로를 거쳤는지, 어디서 재라우팅 (re-routing)이 트리거되었는지, 그리고 품질 점수가 어떠했는지를 정확히 확인할 수 있습니다. 이는 최종적인 RAGAS 메트릭 (metrics)만 확인하는 것보다 훨씬 더 많은 정보를 제공합니다.

classify 노드
CLASSIFY_PROMPT = ChatPromptTemplate.from_messages([
("system", "질문에 가장 적합한 검색 전략을 결정하세요.")
])

오직 전략 이름만 출력하세요:

" " vector - 지식 베이스(정의, 파라미터, 비교)를 필요로 하는 사실적 질문
" " graph - 엔티티 간의 연결 관계에 대한 관계적 질문 (누가 무엇을 만들었는지, X가 Y와 어떻게 연관되는지)
" " web - 최신 정보(최신 버전, 최근 논문, 시사 이슈)가 필요한 경우
" " direct - 검색이 필요 없는 경우 (상식, 수학, 번역, 코드 구문) " ), ( " human " , " 질문: {question} " ), ])

def classify_node ( state ):
raw = classify_chain . invoke ({ " question " : state [ " question " ]}). strip (). lower ()
strategy = " vector " # 안전한 기본값
for s in [ " vector " , " graph " , " web " , " direct " ]:
if s in raw :
strategy = s
break
return { ** state , " strategy " : strategy , " tried_strategies " : [ strategy ], " path " : [ f " classify→ { strategy } " ], }

평가 노드 (The evaluate Node): 아키텍처의 핵심

QUALITY_PROMPT = ChatPromptTemplate . from_messages ([ ( " system " , " 검색된 컨텍스트 (context)가 질문을 얼마나 잘 커버하는지 평가하세요.
" " 0.0에서 1.0 사이의 숫자만 출력하고, 설명은 생략하세요:
" " 1.0 = 완벽하게 커버함, 직접 답변 가능
" " 0.5 = 부분적임, 사용 가능하지만 불완전함
" " 0.0 = 완전히 관련 없음, 답변 불가능 " ), ( " human " , " 질문: {question}

컨텍스트 (context): {context} " ), ])

def evaluate_node ( state ):
context = "

" . join ( d . page_content [: 300 ] for d in state [ " retrieved_docs " ])
raw = quality_chain . invoke ({ " question " : state [ " question " ], " context " : context , })
try :
score = max ( 0.0 , min ( 1.0 , float ( raw .

strip ()))) except ValueError : score = 0.5 # 파싱 실패 → 중립 return { ** state , " quality_score " : score }

라우팅 로직 (The Routing Logic)

QUALITY_THRESHOLD = 0.6
MAX_ATTEMPTS = 2

def route_after_evaluate ( state ) -> str :
score = state [ " quality_score " ]
attempts = len ( state [ " tried_strategies " ])
# 충분히 좋거나, 재시도 횟수를 모두 소진함 — 생성 단계로 진행
if score >= QUALITY_THRESHOLD or attempts >= MAX_ATTEMPTS :
return " generate "
return " re_route "

def re_route_node ( state ):
tried = set ( state [ " tried_strategies " ])
# 우선순위에 따라 전략을 시도하며, 이미 시도한 전략은 건너뜀
for s in [ " vector " , " graph " , " web " ]:
if s not in tried :
return { ** state , " strategy " : s , " tried_strategies " : list ( tried ) + [ s ], " path " : state [ " path " ] + [ f " re_route→ { s } " ], }
return { ** state , " strategy " : " vector " } # 최후의 수단

실험 결과 (Experimental Results)

라우팅 동작 (Routing Behavior)

4가지 질문 유형을 모두 테스트하도록 설계된 8개의 테스트 질문:

초기 전략 분포:

  • vector: 4개 질문 (사실적: RAGAS 지표, vector DB 사용 사례)
  • graph: 2개 질문 (관계적: BAAI의 두 모델, Self-RAG/CRAG/Graph RAG 비교)
  • direct: 2개 질문 (일반적: 영어로 번역, Python 리스트 평균)
  • web: 0개 질문 ← 논의할 가치가 있음

재라우팅(Re-routing) 트리거 발생: 6개의 검색 질문 중 4개 (67%)

에이전트가 올바르게 수행한 것:
두 개의 관계적 질문 ("bge-large-zh-v1.5와 bge-reranker-v2-m3는 모두 어느 조직 출신이며, 각각 어떤 RAG 단계에서 사용되는가?" 및 "Self-RAG, CRAG, Graph RAG는 각각 어떤 문제를 해결하는가?")은 graph로 올바르게 라우팅되었습니다. 문서 코퍼스(corpus)로부터 구축된 지식 그래프(knowledge graph)를 탐색하여 엔티티(entity) 간의 연결을 직접 찾아냈습니다. 두 개의 일반 지식 질문 ("'retrieval-augmented generation'을 영어로 번역하라" 및 "Python에서 리스트 평균을 어떻게 계산하는가?")은 direct로 올바르게 라우팅되었습니다. 불필요한 검색을 방지했고, 지식 베이스에 대한 불필요한 LLM 호출을 하지 않았습니다.

정직한 라우팅 오류: "2025년에 발표된 최신 RAG 논문은 무엇인가요?"라는 질문이 GLM-4-flash에 의해 web이 아닌 vector로 분류되었습니다. 이는 프레임워크 설계의 결함이 아니라, classify 노드에서의 프롬프트 엔지니어링 (prompt engineering) 공백입니다. "papers"라는 단어는 지식 베이스 (knowledge-base) 콘텐츠와 강력하게 연관되어 있으며, LLM은 이 질문을 "최신 정보를 찾아달라"는 의미가 아니라 "논문에 관한 정보를 찾아달라"는 의미로 읽었습니다. "'latest', 'recent', 또는 'this year'와 같은 시간적 표식 (temporal markers)을 포함하는 질문은 web을 우선한다"라는 규칙을 추가하면 이러한 유형의 오분류를 수정할 수 있습니다.

4번의 재라우팅 (re-routes): 평가 (evaluate) 노드가 실제로 작동하고 있습니다. 6개의 검색 질문 중 4개(67%)가 재라우팅을 트리거했습니다. 이는 품질 평가기 (quality evaluator)가 단순히 형식적으로 작동하는 것이 아니라, 불충분하다고 판단되는 컨텍스트 (context)를 진정으로 거부하고 에이전트가 다른 접근 방식을 시도하도록 유도하고 있음을 의미합니다. 시스템이 실행 중에 스스로를 수정하고 있는 것입니다.

RAGAS Metrics ======================================================================
RAGAS Metrics Comparison (Always-Vector vs Agentic RAG) ======================================================================
Metric Always-Vector Agentic RAG Delta
──────────────────────────────────────────────────────────────
context_recall 0.611 0.611 →+0.000
context_precision 0.639 0.681 ↑+0.042
◀ faithfulness 0.625 0.625 →+0.000
answer_relevancy 0.431 0.433 →+0.002

context_precision +0.042; 그 외 나머지는 본질적으로 변화가 없습니다.

RAGAS 개선 수치가 작은 이유 (그리고 그것이 핵심이 아닌 이유)
이 부분은 주의 깊은 설명이 필요합니다. 수치가 작으면 "Agentic RAG는 효과가 없다"라고 오해하기 쉽기 때문입니다. RAGAS는 프로세스의 견고함 (robustness)이 아니라 최종 답변의 품질을 측정합니다. 우리의 테스트 지식 베이스는 이 6개의 검색 질문을 상당히 잘 커버하고 있습니다. evaluate 노드가 검색 결과가 불충분하다고 점수를 매기고 에이전트가 전략을 전환하더라도, 최종 답변의 품질이 극적으로 뛰어오르지는 않습니다. 왜냐하면 정보가 이미 그곳에 있었기 때문입니다.

지식 베이스 (Knowledge Base, KB)가 포괄적일 때, Agentic RAG와 Pipeline RAG는 유사한 품질의 답변을 생성합니다. 진정한 가치는 지식 베이스가 부족할 때 나타납니다.

| 시나리오 | Pipeline RAG | Agentic RAG |
| :--- | :--- | :|
| KB에 정답이 있음 | ✅ 올바르게 답변함 | ✅ 올바르게 답변함 |
| KB에 관련 내용이 없음 | ❌ 관련 없는 문서에서 생성 (환각 (Hallucination) 위험) | ✅ 웹 검색으로 전환하거나 정보의 공백을 인정함 |
| 질문에 관계적 추론 (Relational reasoning)이 필요함 | ⚠️ 유사도에 따라 검색하여 연결 고리를 놓칠 수 있음 | ✅ 그래프 탐색 (Graph traversal)으로 라우팅함 |
| 질문에 검색이 필요 없음 | ⚠️ 검색 호출을 낭비함 | ✅ 생성 단계로 직접 건너뜀 |

이 실험에서 RAGAS는 첫 번째 행만 테스트합니다. 두 번째, 세 번째, 네 번째 행의 가치는 지표 (Metrics)에 나타나지 않지만, 이것이 실제 배포 환경에서 Agentic RAG를 선택해야 하는 이유입니다. 이는 이 시리즈 전체를 관통하는 반복되는 주제입니다. 모든 최적화에는 타겟 시나리오가 있습니다. 시나리오 맥락이 없는 숫자는 불완전합니다.

| 비교 차원 | Pipeline RAG | Agentic RAG |
| :--- | :--- | :|
| 흐름 (Flow) | 고정된 선형 시퀀스 (Fixed linear sequence) | 동적 피드백 루프 (Dynamic feedback loop) |
| 검색 전략 (Retrieval strategy) | 고정됨 (대개 벡터 방식) | 동적, 질문 유형별로 선택됨 |
| 결과 평가 (Result evaluation) | 없음 | 품질 점수 산정 (Quality scoring) |
| 실패 처리 (Failure handling) | 어떻게든 생성함 | 전략을 전환하고 재시도함 |
| 직접 생성 (Direct generation) | 지원되지 않음 | 일반 지식에 대해 지원됨 |
| 추가 LLM 호출 (Extra LLM calls) | 0 | 분류 (Classify) + 평가 (Evaluate) (+ 재라우팅) |
| 최적의 용도 (Best fit) | 포괄적인 KB, 균일한 질문 유형 | 혼합된 의도, 정보 커버리지의 공백 |

비용은 실재합니다. 모든 질문마다 최소 2회의 추가 LLM 호출(분류 + 평가)이 발생하며, 재라우팅 (Re-routing)이 트리거되면 더 늘어납니다. 만약 질문 유형이 균일하고 지식 베이스가 포괄적이라면, Pipeline RAG의 비용 효율성은 확실한 강점입니다.

전체 코드 Complete code는 다음에서 오픈 소스로 제공됩니다: https://github.com/chendongqi/llm-in-action/tree/main/17-agentic-rag

핵심 파일: agentic_rag.py — 전체 구현: 그래프 구축 (graph build), LangGraph 에이전트 (agent), RAGAS 평가 (evaluation)

실행 방법:
git clone https://github.com/chendongqi/llm-in-action
cd 17-agentic-rag
cp .env.example .env
pip install -r requirements.txt
python agentic_rag.py

요약
이 글에서는 Agentic RAG를 구현했습니다.

주요 결과:

  • 검색을 고정된 단계가 아닌 도구 (tool)로 취급 — 이것이 Pipeline RAG와의 본질적인 차이점입니다. 도구는 선택, 평가 및 교체가 가능하지만, 단계 (steps)는 불가능합니다.
  • 라우팅 (Routing) 정확도가 견고했습니다 — 관계형 질문은 그래프 탐색 (graph traversal)으로 올바르게 이동했고, 일반 지식 질문은 검색을 올바르게 건너뛰었습니다. 분류 (classification)가 진정으로 유용합니다.
  • 6개의 검색 질문 중 4개가 재라우팅 (re-routing)을 트리거했습니다 — 평가 노드 (evaluate node)가 단순히 검색 결과에 승인 도장을 찍으며 지연 시간 (latency)만 추가하는 것이 아니라, 실질적인 품질 관리 (quality control)를 수행하고 있습니다.
  • RAGAS 점수는 +0.042 상승했으나, 지표 자체가 핵심은 아닙니다 — 개선 폭이 작은 이유는 우리의 지식 베이스 (knowledge base)가 이미 테스트 질문들을 포괄하고 있기 때문입니다. 진짜 가치는 견고성 (robustness)에 있습니다: 커버리지가 실패할 때 어떤 일이 발생하는지를 보면 알 수 있습니다. Pipeline RAG는 잘못된 답변을 조용히 생성하지만, Agentic RAG는 전략을 전환하여 최소한 더 나은 것을 찾으려고 시도합니다.

이 시리즈의 흐름을 되돌아보면:
Self-RAG는 "우리가 검색을 해야 하는가?"에 답했고, CRAG는 "우리가 검색한 것이 충분히 좋은가?"에 답했으며, Graph RAG는 "관계형 질문을 어떻게 처리할 것인가?"에 답했습니다. Agentic RAG는 이 세 가지를 하나의 통합된 결정 루프 (decision loop)로 결합합니다. 시스템은 단순히 고정된 계획을 실행하는 것이 아니라, 자신의 검색 동작을 능동적으로 라우팅, 평가 및 수정합니다. 이것이 파이프라인 사고 (pipeline thinking)에서 에이전트 사고 (agent thinking)로의 전환입니다.

참고 문헌
Agentic RAG Survey (Hu et al., 2024)
LangGraph Documentation
Self-RAG Paper (Asai et al., 2023)
CRAG Paper (Yan et al., 2024)

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0