Home / LangGraph / LangGraph RAG 기초
Note

LangGraph RAG 기초

그래프 기반 RAG — StateGraph 구조와 노드 설계

⏱ 20분 48 / 189

LangGraph RAG 기초

그래프 기반 RAG — StateGraph 구조와 노드 설계

그래프로 에이전트와 RAG를 제어한다

LCEL의 직선 한계를 넘어 — 조건 분기·루프·상태 관리·멀티에이전트

LangGraph 핵심 구조

LCEL 한계 · StateGraph · 3요소

PART 1 · 핵심 구조

핵심 구조

직선을 넘는 3요소

3요소로 모든 워크플로우

LCEL의 직선 한계를 순환 그래프로 넘어섭니다 — State·Node·Edge 세 가지로 분기와 루프까지 표현합니다.

  • State — TypedDict 공유 데이터
  • Node — State를 처리하는 Python 함수
  • Edge — add_edge · add_conditional_edges
  • compile() — invoke/stream 가능한 Runnable
takeaway

조건 분기·루프·상태 관리 — LCEL이 못 하는 세 가지가 StateGraph의 존재 이유입니다.

PART 1 · 핵심 구조

LCEL vs LangGraph

직선 파이프라인의 한계를 순환 그래프로 해결

LCEL

직선 파이프라인

  • A | B | C — 항상 같은 순서
  • 조건 분기·루프 불가
  • 상태 관리 없음
  • 빠른 프로토타입에 적합

LangGraph

순환 방향 그래프

  • 조건 엣지로 분기
  • 순환 엣지로 루프 (재시도)
  • TypedDict State + Checkpointer
  • 프로덕션 에이전트에 적합
takeaway

빠른 프로토타입은 LCEL로, 분기·루프·상태가 필요한 프로덕션 에이전트는 LangGraph로 갑니다.

PART 1 · 핵심 구조

StateGraph 3요소

State + Node + Edge — 이 3개로 모든 워크플로우를 표현

State

TypedDict — 그래프 전체 공유 데이터. Annotated[list, add]로 자동 누적

Node

Python 함수 — State를 받아 처리하고 업데이트 반환

Edge

노드 간 연결 — 직접(add_edge) 또는 조건부(conditional)

takeaway

State를 Node가 갱신하고 Edge가 다음 경로를 고릅니다 — 셋의 조합만으로 분기·루프·병렬을 모두 표현합니다.

PART 1 · 핵심 구조

문의 하나의 그래프 여정

"배송이 3일째 안 와요" — 분류 노드의 유형 판정과 조건 엣지가 고르는 갈림길





"배송이 3일째 안 와요"
고객 문의 — State.question



STATEGRAPH — 문의가 지나는 길

classify 노드

조건 엣지 — category?

"배송" → check_delivery 노드

respond 노드


↳ 환불 문의였다면
"환불" → process_refund 노드
→ 같은 respond로 합류




"내일 도착 예정입니다"
응답 — State.answer



조건 엣지가 State의 category 값을 보고 다음 노드를 고릅니다 — A | B | C 직선(LCEL)으로는 그릴 수 없는 갈림길


takeaway

이 그림의 부품이 곧 3요소입니다 — 상자는 Node, 화살표는 Edge, 그 사이를 흐르는 데이터가 State입니다.

PART 1 · 핵심 구조

State 스냅샷 — 노드를 지날 때마다

"배송이 3일째 안 와요" 문의의 여정을 데이터로 — 노드가 반환한 갱신분이 State에 병합되는 과정




이어지는 예시
"배송이 3일째 안 와요" — 앞 장의 여정을 State로 다시 봅니다
(예시 수치)



START — 입력 직후
{
  question:
    "배송이 3일째 안 와요"
}



classify 통과
{
  question: "배송이…",
  category: "배송"
}

반환값 {"category": "배송"}만 병합


check_delivery 통과
{
  question: "배송이…",
  category: "배송",
  tracking_info:
    "간선 상차 · D-1"

}



respond 통과 → END
{
  question: "배송이…",
  category: "배송",
  tracking_info: "…D-1",
  answer:
    "내일 도착 예정…"

}




노드는 State 전체를 고쳐 쓰지 않고 갱신분 dict만 반환 — 병합은 그래프가 맡습니다 (다음 장의 불변 갱신 원칙)


takeaway

조건 엣지가 본 것도, 답변 노드가 쓴 것도 전부 이 State 안의 값입니다 — State 설계가 곧 그래프 설계인 이유입니다.

PART 1 · 핵심 구조

State 설계 원칙

그래프의 데이터 계약 — 필드 최소 · 불변 갱신 · 자동 누적

핵심 포인트

TypedDict
상태 스키마를 타입으로 선언 — 필드 구조가 곧 그래프의 계약
최소 필드
필요한 값만 State에 — 필드가 늘수록 메모리와 디버깅 부담 증가
불변 갱신
노드는 State를 직접 수정하지 않고 갱신분을 반환 — 흐름 추적 용이
Annotated[list, add]
리스트 필드 자동 누적 — messages처럼 쌓이는 값에 사용

코드

python
from typing import TypedDict

# 그래프 전체가 공유하는 상태 스키마
class RAGState(TypedDict):
    question: str
    documents: list
    relevance_score: float
    answer: str
    retry_count: int

# 노드는 State를 받아 갱신분만 반환하는 순수 함수
def retrieve(state: RAGState):
    docs = retriever.invoke(state["question"])
    return {"documents": docs}
PART 1 · 핵심 구조

StateGraph 코드 패턴

add_node → add_edge → compile() — 3단계로 그래프 조립

python
from langgraph.graph import StateGraph, MessagesState, START, END

# 노드 = State를 받아 업데이트를 반환하는 순수 함수
def retrieve(state): ...
def generate(state): ...

# State 스키마로 그래프 선언 — MessagesState는 messages를 자동 누적
graph = StateGraph(MessagesState)
graph.add_node("retrieve", retrieve)
graph.add_node("generate", generate)
graph.add_edge(START, "retrieve")  # START/END 특수 노드로 진입·종료 지정
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", END)
app = graph.compile()  # 컴파일하면 invoke/stream 가능한 Runnable이 됨
그래프로 에이전트의 두뇌 회로를 설계합니다.

StateGraph 위에 RAG 루프 · ReAct · Supervisor · HITL 패턴 구현