한국어
Week 2: 추론
05. 고급 (Self-RAG)

05. 고급: LangGraph를 활용한 Self-RAG

기존 RAG의 문제점

표준 RAG는 직선 파이프라인입니다: 검색 → 생성. 하지만 다음과 같은 상황에서는 어떻게 될까요?

  • 검색된 문서가 질문과 관련이 없는 경우?
  • 생성된 답변이 문서를 넘어서 환각하는 경우?
  • 답변이 기술적으로는 맞지만 유용하지 않은 경우?

기존 RAG는 눈이 멀었습니다—이러한 실패를 감지하거나 수정할 수 없습니다.

Self-RAG란?

**Self-RAG (자기 성찰적 RAG)**는 시스템이 다음을 수행할 수 있는 피드백 루프를 추가합니다:

  1. 문서 평가: 검색된 내용이 실제로 관련이 있는가?
  2. 생성 평가: 답변이 문서에 근거하는가?
  3. 수정: 문제가 있으면 재검색하거나 재생성

핵심 인사이트: Self-RAG는 RAG를 파이프라인에서 자체 실패를 감지하고 수정할 수 있는 제어 루프로 변환합니다.

LangGraph 아키텍처

Self-RAG는 조건부 엣지가 있는 상태 머신으로 자연스럽게 표현됩니다:

구현

Step 1: 상태 정의

평가와 루프에 필요한 모든 정보 추적:

from typing import TypedDict, List
 
class GraphState(TypedDict):
    question: str
    documents: List[str]
    generation: str
    relevance: str      # "yes" 또는 "no"
    hallucination: str  # "yes" 또는 "no"
    useful: str         # "yes" 또는 "no"

Step 2: 노드 구축

def retrieve(state: GraphState):
    """벡터 스토어에서 문서 검색"""
    print("---검색---")
    question = state["question"]
 
    # 프로덕션: 실제 벡터 스토어 사용
    docs = vector_store.similarity_search(question, k=3)
 
    return {"documents": [doc.page_content for doc in docs]}

Step 3: 조건부 엣지

흐름을 제어하는 결정 로직:

def decide_to_generate(state: GraphState) -> str:
    """문서 관련성에 따라 라우팅"""
    if state["relevance"] == "yes":
        print("---문서 관련 있음: 생성---")
        return "generate"
    else:
        print("---문서 관련 없음: 웹 검색---")
        return "web_search"
 
def decide_to_finish(state: GraphState) -> str:
    """환각 확인에 따라 라우팅"""
    if state["hallucination"] == "no":
        print("---답변 근거 있음: 완료---")
        return "finish"
    else:
        print("---환각: 재생성---")
        return "regenerate"

Step 4: 그래프 구축

from langgraph.graph import StateGraph, END
 
workflow = StateGraph(GraphState)
 
# 노드 추가
workflow.add_node("retrieve", retrieve)
workflow.add_node("grade_documents", grade_documents)
workflow.add_node("generate", generate)
workflow.add_node("grade_generation", grade_generation)
workflow.add_node("web_search", web_search)
 
# 진입점 설정
workflow.set_entry_point("retrieve")
 
# 엣지 추가
workflow.add_edge("retrieve", "grade_documents")
workflow.add_edge("web_search", "grade_documents")
workflow.add_edge("generate", "grade_generation")
 
# 조건부 엣지
workflow.add_conditional_edges(
    "grade_documents",
    decide_to_generate,
    {
        "generate": "generate",
        "web_search": "web_search"
    }
)
 
workflow.add_conditional_edges(
    "grade_generation",
    decide_to_finish,
    {
        "finish": END,
        "regenerate": "generate"
    }
)
 
# 컴파일
app = workflow.compile()

Step 5: 에이전트 실행

inputs = {"question": "AI 에이전트란 무엇인가요?"}
 
for output in app.stream(inputs):
    for key, value in output.items():
        print(f"완료: {key}")
 
# 출력:
# ---검색---
# 완료: retrieve
# ---문서 평가---
# ---문서 관련 있음: 생성---
# 완료: grade_documents
# ---생성---
# 완료: generate
# ---생성 평가---
# ---답변 근거 있음: 완료---
# 완료: grade_generation

Self-RAG 평가 기준

확인 항목질문실패 시
관련성문서가 주제에 관한 것인가?재검색 또는 웹 검색
근거답변이 문서에 의해 뒷받침되는가?재생성
유용성답변이 질문을 다루는가?다른 프롬프트로 재생성

비교: 기존 RAG vs Self-RAG

측면기존 RAGSelf-RAG
아키텍처파이프라인제어 루프
오류 처리없음자동 수정
환각감지 안 됨감지 및 수정
지연 시간낮음 (단일 패스)높음 (여러 패스)
신뢰성변동적더 일관적
비용낮음높음 (더 많은 LLM 호출)

고급: CRAG (Corrective RAG)

CRAG는 더 정교한 수정 전략으로 Self-RAG를 확장합니다:

실습 내용

노트북에서 다음을 수행합니다:

상태 스키마 정의

모든 평가 정보를 추적하기 위한 TypedDict 생성

평가 노드 구축

구조화된 출력으로 문서 및 생성 평가기 구현

조건부 로직 추가

라우팅을 위한 결정 함수 생성

그래프 시각화

LangGraph의 내장 시각화 사용

엣지 케이스 테스트

다른 경로를 트리거하는 쿼리 시도

핵심 요점

  1. 피드백 루프가 자기 수정 가능하게 - 기존 RAG는 조용히 실패; Self-RAG는 감지하고 수정
  2. 평가를 위한 구조화된 출력 - 신뢰할 수 있는 yes/no 결정을 위해 Pydantic 모델 사용
  3. LangGraph가 간단하게 - 조건부 엣지가 라우팅 로직을 자연스럽게 표현
  4. 트레이드오프: 지연 시간 vs 신뢰성 - 더 많은 검사는 더 많은 LLM 호출을 의미하지만 오류는 적어짐

참고 자료 & 추가 학습

학술 논문

관련 튜토리얼

다음 단계

Week 2를 완료한 것을 축하합니다! 주말 프로젝트로 이동하여 이러한 리플렉션 패턴을 코드 생성에 적용하는 자가 수정 코더를 구축하세요.