Corrective RAG (CRAG) 개념 정리

Corrective RAG란

Corrective RAG(CRAG)는 기존 RAG 파이프라인에 “검색 결과 검증 및 보정 단계”를 추가한 방식입니다.

일반적인 RAG는 아래 흐름으로 동작합니다.

  1. 사용자 질문 입력
  2. 벡터 DB 또는 검색 시스템에서 관련 문서 검색
  3. 검색된 문서를 컨텍스트로 LLM이 답변 생성

문제는 검색 결과가 부정확하거나, 애매하거나, 일부만 맞는 경우에도 그대로 답변 생성에 사용된다는 점입니다.
즉, RAG는 hallucination을 줄여주지만, retrieval 자체가 잘못되면 오히려 잘못된 문서를 근거로 그럴듯한 답을 만들 수 있습니다.

CRAG는 이 문제를 해결하기 위해, 검색 결과의 품질을 먼저 평가한 뒤 결과를 그대로 쓸지, 보정할지, 외부 검색으로 보강할지 결정합니다.

왜 필요한가

기본 RAG가 자주 겪는 문제는 아래와 같습니다.

  • 질문과 겉보기로만 비슷한 문서를 가져옴
  • 문서 일부는 맞지만 핵심 답변에는 부족함
  • 내부 지식베이스에 최신 정보가 없음
  • irrelevant chunk가 섞여 답변 품질이 흔들림

CRAG는 이런 상황에서 “검색했으니 믿고 생성한다”가 아니라,
**”검색 결과가 믿을 만한지 먼저 판단하고, 부족하면 보정한 뒤 생성한다”**는 관점으로 접근합니다.

핵심 아이디어

CRAG의 핵심은 아래 4가지입니다.

  1. Retrieval Evaluator

    • 검색된 문서가 질문에 얼마나 적절한지 평가
  2. Corrective Action

    • 평가 결과에 따라 다음 행동을 분기
  3. Web Search Augmentation

    • 내부 검색이 부족하면 웹 검색으로 보강
  4. Knowledge Refinement

    • 가져온 문서 전체를 그대로 쓰지 않고, 핵심 정보만 추려서 재구성

즉, CRAG는 검색 -> 평가 -> 보정 -> 생성 구조를 갖습니다.

동작 흐름

CRAG를 단순화하면 아래처럼 볼 수 있습니다.

사용자 질문
->
내부 지식베이스 검색
->
검색 결과 품질 평가
->
[좋음] 검색 결과 정제 후 사용
[애매함] 내부 검색 + 웹 검색 결합
[나쁨] 웹 검색으로 대체/보강
->
정제된 컨텍스트로 답변 생성

기본 RAG와의 가장 큰 차이는 검색 결과를 바로 생성 단계로 넘기지 않는다는 점입니다.

Retrieval Evaluator

CRAG 논문에서 가장 중요한 구성 요소는 retrieval evaluator입니다.

이 모듈은 질문과 검색된 문서들을 보고, 현재 검색 결과가 답변 생성에 쓸 만한지 신뢰도(confidence)를 판단합니다.

평가 결과는 대략 아래 3가지 상황으로 볼 수 있습니다.

  • Correct: 검색 결과가 충분히 좋음
  • Incorrect: 검색 결과가 부정확하거나 거의 쓸 수 없음
  • Ambiguous: 일부는 맞지만 확신하기 어려움

이 평가 결과에 따라 다음 액션이 달라집니다.

Correct / Incorrect / Ambiguous

1. Correct

검색 결과가 충분히 관련성이 높다면, 바로 버리지 않고 정제(refinement) 해서 사용합니다.

  • 불필요한 문장 제거
  • 핵심 정보만 추출
  • 중복되거나 잡음이 많은 부분 제거

즉, 좋은 검색 결과라도 그대로 넣는 것이 아니라, LLM이 쓰기 좋은 형태로 다듬어 전달합니다.

2. Incorrect

검색 결과가 전반적으로 틀렸거나 품질이 낮다면, 내부 검색 결과를 신뢰하지 않고 외부 웹 검색으로 보강합니다.

이 단계는 특히 아래 같은 상황에서 의미가 있습니다.

  • 내부 문서 저장소에 최신 정보가 없는 경우
  • 질문이 롱테일 주제인 경우
  • 벡터 검색이 엉뚱한 chunk를 가져온 경우

즉, CRAG는 “검색 실패”를 그냥 받아들이지 않고, 다른 검색 채널로 재시도합니다.

3. Ambiguous

검색 결과가 완전히 틀린 것은 아니지만 애매한 경우에는, 내부 검색 결과와 외부 웹 검색 결과를 함께 활용합니다.

이 경우가 실무적으로 가장 중요합니다.

  • 내부 문서에는 일부 정보가 있음
  • 하지만 질문 전체를 커버하기에는 부족함
  • 최신성이나 보충 설명이 더 필요함

즉, CRAG는 애매한 상황을 실패로 보지 않고, 혼합형 보정 전략으로 처리합니다.

Knowledge Refinement

CRAG는 검색된 문서를 통째로 넣는 대신, 논문에서 말하는 decompose-then-recompose 방식을 사용합니다.

이 개념은 아래처럼 이해하면 됩니다.

  1. 검색 문서를 더 작은 정보 단위로 분해
  2. 그중 질문과 관련 있는 부분만 선택
  3. 선택된 정보만 다시 묶어서 컨텍스트 구성

기존 RAG에서는 관련 없는 문장이 섞인 채로 프롬프트에 들어가서 LLM이 노이즈를 함께 읽는 경우가 많습니다.
CRAG는 이 문제를 줄이기 위해 “문서를 가져오는 것”보다 “쓸 만한 정보만 재조합하는 것” 에 더 집중합니다.

기존 RAG와 비교

구분 기본 RAG Corrective RAG
검색 결과 사용 방식 바로 생성에 사용 먼저 평가 후 사용
검색 실패 대응 별도 대응 없음 웹 검색/보정 수행
노이즈 처리 chunk 품질에 의존 refinement 단계로 정제
강점 구조 단순, 구현 쉬움 검색 오류에 더 강함
단점 잘못된 retrieval에 취약 파이프라인이 더 복잡함

정리하면, CRAG는 기본 RAG의 약점인 retrieval quality 불안정성을 보완하는 방향입니다.

실무 관점에서의 장점

1. 검색 실패를 그대로 두지 않는다

기본 RAG는 검색이 잘못되면 답변 품질이 급격히 무너질 수 있습니다.
CRAG는 검색 자체를 검토하고, 필요하면 대체 경로를 사용합니다.

2. 최신 정보 대응력이 좋아진다

내부 지식베이스만으로 부족한 경우 웹 검색을 이용해 보강할 수 있으므로,
정적 문서 저장소만 쓰는 RAG보다 최신성 대응이 좋습니다.

3. 노이즈를 줄일 수 있다

관련 없는 chunk가 일부 섞여도 refinement 단계에서 걸러낼 수 있어,
LLM이 덜 혼란스러운 컨텍스트를 받게 됩니다.

4. 기존 RAG 위에 붙이기 쉽다

논문에서도 강조하는 부분인데, CRAG는 plug-and-play 성격이 강합니다.
즉, 기존 RAG를 완전히 버리는 것이 아니라 중간에 평가/보정 레이어를 추가하는 형태로 설계할 수 있습니다.

한계와 비용

CRAG가 항상 공짜로 좋은 것은 아닙니다.

  • retrieval evaluator가 추가되므로 구조가 복잡해짐
  • 웹 검색을 붙이면 latency가 증가함
  • 외부 검색 사용 시 비용 및 보안 이슈가 생길 수 있음
  • refinement 단계가 잘못 설계되면 오히려 중요한 정보를 버릴 수 있음

즉, CRAG는 정확도와 견고성을 위해 복잡도와 응답 시간을 더 지불하는 방식이라고 볼 수 있습니다.

언제 쓰면 좋은가

CRAG는 아래 같은 상황에서 특히 유용합니다.

  • 검색 품질 편차가 큰 RAG 시스템
  • 최신 정보가 중요해 웹 검색 보강이 필요한 서비스
  • 단순 Q&A보다 정확도와 근거 품질이 더 중요한 시스템
  • 내부 문서 + 외부 공개 정보 둘 다 활용해야 하는 경우

반대로 아래 경우에는 기본 RAG가 더 단순하고 실용적일 수 있습니다.

  • 문서 품질이 매우 좋고 검색 정확도도 높은 경우
  • 초저지연 응답이 중요한 경우
  • 외부 검색을 붙일 수 없는 보안 환경

예제 코드

아래 코드는 CRAG의 핵심 흐름을 단순화해서 보여주는 Python 예제입니다.

  • 내부 문서 검색
  • 검색 결과 품질 평가
  • 결과에 따라 correct / ambiguous / incorrect 분기
  • 필요하면 웹 검색 보강
  • 최종 컨텍스트로 답변 생성

실제 서비스에서는 벡터 DB, reranker, evaluator 모델, 웹 검색 API를 각각 교체해서 사용하면 됩니다.

1. 가장 단순한 CRAG 파이프라인

from dataclasses import dataclass
from typing import List, Literal


ConfidenceLabel = Literal["correct", "ambiguous", "incorrect"]


@dataclass
class Document:
content: str
source: str
score: float = 0.0


def retrieve_from_kb(query: str) -> List[Document]:
# 예시: vector db에서 top-k 검색
return [
Document("CRAG adds a retrieval evaluator before generation.", "kb://rag-paper", 0.88),
Document("CRAG may use web search when retrieval quality is low.", "kb://rag-notes", 0.81),
]


def retrieval_evaluator(query: str, docs: List[Document]) -> ConfidenceLabel:
# 실제로는 cross-encoder, T5 계열 evaluator, 또는 별도 classifier를 사용
if not docs:
return "incorrect"

avg_score = sum(doc.score for doc in docs) / len(docs)

if avg_score >= 0.8:
return "correct"
if avg_score >= 0.5:
return "ambiguous"
return "incorrect"


def refine_documents(query: str, docs: List[Document]) -> List[Document]:
# 실제로는 chunk filtering, sentence extraction, reranking 등을 수행
refined = []
for doc in docs:
if query.lower().split()[0] in doc.content.lower() or doc.score >= 0.75:
refined.append(doc)
return refined


def web_search(query: str) -> List[Document]:
# 실제로는 Tavily, SerpAPI, Bing Search API 등을 붙일 수 있음
return [
Document("Web result: CRAG evaluates retrieved documents before generation.", "web://result-1", 0.77),
Document("Web result: CRAG uses decompose-then-recompose to refine knowledge.", "web://result-2", 0.74),
]


def generate_answer(query: str, docs: List[Document]) -> str:
context = "\n".join(f"- {doc.content} ({doc.source})" for doc in docs)
return f"""질문: {query}

참고 컨텍스트:
{context}

답변:
CRAG는 검색 결과의 품질을 먼저 평가하고, 필요 시 웹 검색과 정제 과정을 거쳐 더 신뢰도 높은 컨텍스트로 답변을 생성하는 방식입니다.
"""


def run_crag(query: str) -> str:
retrieved_docs = retrieve_from_kb(query)
label = retrieval_evaluator(query, retrieved_docs)

if label == "correct":
final_docs = refine_documents(query, retrieved_docs)
elif label == "ambiguous":
refined_docs = refine_documents(query, retrieved_docs)
external_docs = web_search(query)
final_docs = refined_docs + external_docs
else:
final_docs = web_search(query)

return generate_answer(query, final_docs)


if __name__ == "__main__":
answer = run_crag("What is Corrective RAG?")
print(answer)

이 예제에서 중요한 부분은 retrieval_evaluator()가 전체 흐름을 분기한다는 점입니다.

  • correct: 내부 검색 결과를 정제해서 사용
  • ambiguous: 내부 검색 + 웹 검색을 결합
  • incorrect: 내부 검색을 버리고 외부 검색으로 대체

2. LangChain 스타일로 보면 이런 구조다

실무에서는 보통 아래처럼 각 컴포넌트를 나눠서 생각합니다.

def crag_pipeline(query, retriever, evaluator, web_search_tool, llm):
docs = retriever.invoke(query)
confidence = evaluator.evaluate(query, docs)

if confidence == "correct":
final_docs = evaluator.refine(query, docs)
elif confidence == "ambiguous":
final_docs = evaluator.refine(query, docs) + web_search_tool.search(query)
else:
final_docs = web_search_tool.search(query)

prompt = build_prompt(query, final_docs)
return llm.invoke(prompt)

이 패턴으로 구현하면 아래를 각각 독립적으로 교체할 수 있습니다.

  • retriever: BM25 / vector / hybrid retrieval
  • evaluator: classifier / cross-encoder / small LLM
  • web_search_tool: 외부 검색 API
  • llm: 최종 답변 생성 모델

3. 실무 구현 시 체크 포인트

예제 코드를 실제 서비스로 확장할 때는 아래를 같이 고려해야 합니다.

  • evaluator score threshold를 고정값으로 둘지, 도메인별로 다르게 둘지
  • ambiguous일 때 내부 검색과 웹 검색의 가중치를 어떻게 줄지
  • 웹 검색 결과를 그대로 쓰지 않고, 추가 filtering / reranking을 할지
  • 최종 프롬프트에 source citation을 어떻게 넣을지
  • latency 증가를 줄이기 위해 evaluator와 web search를 어떻게 최적화할지

한 줄 요약

Corrective RAG(CRAG)는 검색 결과를 그대로 믿지 않고, 먼저 평가하고 필요하면 보정한 뒤 생성하는 RAG입니다.

즉, RAG를 더 “똑똑하게 검색”하게 만드는 것이 아니라,
“검색 결과가 틀릴 수도 있다는 전제를 두고 더 견고하게 답변하게 만드는 방법” 이라고 볼 수 있습니다.

참고

Share