RAG 이용한 Agent [2] 랭그래프 (LangGraph) 기본기 다지기

Leejaegun·2025년 4월 4일

rag

목록 보기
2/4

1. ✅ LangGraph 개요

LangGraph는 LangChain 기반의 프레임워크로, 복잡한 LLM 워크플로우(Agent + Tool + 흐름 제어)를 상태 머신(State Machine) 방식으로 설계할 수 있게 해주는 도구입니다.

✅ LangChain vs LangGraph

항목LangChainLangGraph
처리 흐름직선적 순차 처리 (Chain 중심)분기, 반복, 병렬, 조건 흐름 등 유연한 그래프 기반 흐름
복잡한 워크플로우어렵거나 코드 분기 복잡함복잡한 시나리오를 자연스럽게 구성 가능
에이전트 구조한 번 호출 후 도구 판단, 실행노드 단위의 에이전트 정의 가능, 다단계 설계 가능
상태 관리히스토리 수동 전달 필요상태(state)를 명시적으로 관리 가능

🧠 LangGraph 핵심 개념

(1). State Machine (상태 머신) 기반

  • 각 노드(node)는 하나의 작업 단위 (에이전트 or 처리 함수)
  • 상태(state)는 입력/출력 값을 저장하는 구조체 (예: dict)
  • 워크플로우는 상태의 흐름과 조건에 따라 분기 / 반복 / 종료

(2). Graph Node (노드)

  • 각 노드는 하나의 함수 또는 에이전트 (예: 검색, 요약, 분석 등)
  • 노드 간 연결은 의사 결정 로직에 따라 동적 분기 가능

(3). Agent (에이전트)

  • LangChain의 에이전트처럼 Tool을 선택하고 실행
  • 각 노드는 LLM 기반 에이전트를 바인딩할 수 있음
  • 예: SearchAgent, SummarizeAgent 등 각 역할 분리 가능

🔄 LangGraph 흐름 예시

[User Input] 
     ↓
[Agent #1: 이해 및 판단]
     ├── 도구 사용 필요 O → 도구 실행 → 결과 전달
     ↓
[Agent #2: 요약 or 분석]
     ├── 작업 완료 여부 판단
     └── 반복 수행 or 종료

🧱 LangGraph 주요 특징

기능설명
🔁 반복 처리특정 조건에서 반복 실행 가능 (while 같은 흐름 설계)
🔀 분기 처리조건에 따라 다른 경로 선택 (if-else처럼 작동)
🧠 에이전트 연계도구가 바인딩된 LLM 기반 Agent를 노드로 구성 가능
📦 상태 공유이전 노드의 결과를 상태(state)에 저장해서 다음 노드에서 활용 가능
🔚 종료 제어명시적으로 작업 종료 지점(end) 지정 가능

📌 실제 LangGraph에서 설계할 수 있는 시나리오 예시

  1. 문서 분석 파이프라인

    • 입력 → 문서 요약 → 질문 추출 → 검색 도구 호출 → 최종 답변
  2. 다단계 에이전트 시나리오

    • 1단계: 사용자 의도 파악
    • 2단계: 필요한 도구 판단 및 실행
    • 3단계: 응답 생성 후 요약
  3. 모듈형 업무 흐름

    • 업무 A → 업무 B → 결과 판단 → B 반복 or 종료

✅ 결론

LangChain이 선형적 단일 에이전트/체인 중심이라면,
LangGraph는 LLM 기반 복잡한 플로우를 명확하게 설계할 수 있도록 지원합니다.

  • 복잡한 Agent 시스템, 다단계 Tool 사용, 상태 기반 제어가 필요할 경우 LangGraph가 강력한 선택지입니다.

📚 다음 추천 학습 흐름

  1. LangGraph 기본 설치 및 Hello World 예제
  2. 단일 Agent 노드 구성 → 실행 흐름 파악
  3. 여러 노드를 연결한 그래프 설계 (분기, 반복 포함)
  4. LangChain 도구 연동
  5. LangGraph + Gradio 또는 Streamlit 챗봇 인터페이스 연결

2. 🧠 LangGraph의 핵심 개념: State Graph 요약 정리

✅ (1). State Graph란?

  • 상태(State)를 기반으로 작동하는 워크플로우 그래프 구조
  • LangChain보다 더 유연하고 조건 분기 가능한 복잡한 흐름을 설계할 수 있음

✅ (2). 구성 요소

구성 요소설명
상태 (State)전체 워크플로우의 공유 데이터. 모든 노드에서 공유하며, 클래스(Pydantic 모델)로 정의
노드 (Node)작업을 수행하는 함수. 상태를 입력으로 받아 상태를 출력으로 반환
엣지 (Edge)노드 간의 연결 경로. 조건 분기 등 흐름 제어 역할 수행

✅ (3). 동작 원리

  1. 초기 상태(Initial State)가 그래프에 전달됨
  2. Start → Node → End 순서로 상태가 전달되고 업데이트됨
  3. Node는:
    • 입력: 현재 상태
    • 작업: API 호출, 계산 등
    • 출력: 수정된 상태 반환

⚙️ 각 노드는 상태의 필드를 업데이트하거나 새로운 정보를 추가함.

✅ (4). 코드 구성 흐름

📌 1) 상태 정의 (Pydantic 사용)

class OverallState(BaseModel):
    text: str

📌 2) 노드 함수 정의

def node_fn(state: OverallState) -> OverallState:
    state.text = "반갑습니다"
    return state

📌 3) 그래프 정의 및 구성

builder = StateGraph(OverallState)
builder.add_node("node", node_fn)
builder.set_entry_point("start")
builder.add_edge("start", "node")
builder.add_edge("node", "end")
graph = builder.compile()

📌 4) 실행

initial_state = OverallState(text="안녕하세요")
result = graph.invoke(initial_state)
print(result.text)  # 출력: 반갑습니다

✅ (5). 중요한 개념 요약

개념설명
상태 공유모든 노드는 동일한 상태 구조를 공유함
노드 함수입력 상태 → 처리 → 출력 상태 (상태 업데이트)
엣지다음 노드를 결정하는 흐름 제어 장치
컴파일정의한 그래프를 실행 가능한 워크플로우로 변환
invoke()그래프 실행 메서드, 초기 상태를 전달함

✅ (6). 확장 기능: 조건 분기 (브랜칭)

  • 조건에 따라 실행할 노드를 분기할 수 있음
builder.add_conditional_edges("check_node", 
  condition_fn=lambda state: state.text == "A",
  true_node="A_node", false_node="B_node"
)

🎯 최종 요약

State Graph = 상태 기반 + 노드 기반 + 조건 흐름 제어가 가능한 유연한 워크플로우 그래프

  • 복잡한 LLM 기반 시스템 (예: 검색 → 요약 → 평가 → 재검색 등)을 설계하는 데 적합
  • 각 단계에서 상태를 업데이트하면서 흐름을 제어할 수 있음
  • 기존 LangChain의 단순한 체인보다 더 유연하고 강력한 흐름 설계 가능

3. 🧠 LangGraph로 상태 기반(State-based) 에이전트 그래프 만들기 요약

✅ 목적

  • 사용자 선호도에 따라 메뉴를 추천하고, 해당 메뉴 정보를 제공하는 간단한 레스토랑 추천 그래프를 구현

📦 전체 흐름 요약

1. 상태(State) 정의
2. 상태를 처리하는 노드(Node) 정의
3. 노드들을 연결하는 엣지(Edge) 구성
4. 그래프 컴파일 및 시각화
5. 그래프 실행 및 결과 출력

1️⃣ 상태 정의 (State Schema)

MenuState = TypedDict("MenuState", {
    "user_preference": Optional[str],
    "recommended_menu": Optional[str],
    "menu_info": Optional[str],
})
  • 사용자 선호도 (user_preference)
  • 추천된 메뉴 (recommended_menu)
  • 추천 메뉴의 정보 (menu_info)

2️⃣ 노드(Node) 정의

각 노드는 MenuState를 입력 받고, 해당 필드를 업데이트

🔹 get_user_preference()

  • 무작위로 사용자 선호도를 선택 (육류, 해산물, 채식, 아무거나)
  • user_preference 필드를 업데이트

🔹 recommend_menu()

  • user_preference에 따라 추천 메뉴 선택
  • 예: 육류 → 스테이크, 아무거나 → 오늘의 특선
  • recommended_menu 필드 업데이트

🔹 provide_menu_info()

  • 추천 메뉴에 따른 상세 정보 제공
  • 예: 스테이크 → "프리미엄 한우 스테이크, 38,000원"
  • menu_info 필드 업데이트

3️⃣ 그래프 구성 (StateGraph)

graph = StateGraph(MenuState)
graph.add_node("선호도", get_user_preference)
graph.add_node("추천", recommend_menu)
graph.add_node("정보제공", provide_menu_info)

graph.set_entry_point("선호도")
graph.add_edge("선호도", "추천")
graph.add_edge("추천", "정보제공")
graph.set_finish_point("정보제공")

app = graph.compile()
  • 각 노드를 순차적으로 연결 (선형 시퀀스 형태)
  • Start → 선호도 → 추천 → 정보제공 → End

4️⃣ 그래프 실행

initial_state = {"user_preference": None, "recommended_menu": None, "menu_info": None}
result = app.invoke(initial_state)
  • 매 실행마다 랜덤한 선호도가 설정되므로 결과는 실행할 때마다 달라짐
  • 결과는 MenuState 형태의 딕셔너리

5️⃣ 결과 예시

User Preference: 해산물
Recommended Menu: 랍스터 파스타
Menu Info: 신선한 랍스터와 크림소스를 곁들인 파스타

🎨 시각화 (선택)

from langgraph.graph import MermaidDrawer
MermaidDrawer(graph).draw_svg()
  • 머메이드(Mermaid.js)를 이용한 상태 그래프 시각화 가능

✅ 전체 정리

구성 요소내용
상태 (State)공유되는 데이터 구조 (유저 선호, 추천 메뉴, 메뉴 정보)
노드 (Node)상태를 입력받아 처리하고 다시 상태를 반환하는 함수
엣지 (Edge)노드 간의 실행 흐름 연결
실행 (Invoke)초기 상태를 입력 → 자동으로 순차적 노드 실행
출력최종 MenuState 결과 반환

4. ✅ LangGraph 조건부 엣지를 활용한 분기 흐름 정리

🎯 목표

사용자의 입력에 따라 두 가지 다른 경로로 처리되는 분기형 그래프 구현:

  • 음식/메뉴 관련 질문 → 벡터DB 검색 → 메뉴 관련 답변 생성
  • 그 외 일반 질문 → 일반 LLM 응답 생성

(1). 🧱 상태(State) 정의

class MenuState(BaseModel):
    user_query: str
    is_menu_related: bool
    search_result: list[str]
    final_answer: str

LangGraph는 상태 기반 흐름이므로 각 노드 간 전달될 데이터를 명확히 정의

(2). 🧩 노드 구성

노드 이름역할
get_user_query사용자 입력을 받아 user_query 업데이트
analyze_inputLLM으로 입력이 메뉴 관련인지 분석 → is_menu_related 필드 업데이트
search_menu_info벡터DB로 문서 검색 → search_result 필드 업데이트
generate_menu_response검색된 문서 기반으로 메뉴 관련 답변 생성 → final_answer
generate_general_response메뉴와 무관한 일반 질문에 대한 LLM 응답 생성 → final_answer

(3). 🔀 조건부 분기 함수

def decide_next_step(state: MenuState) -> str:
    return "search_menu_info" if state.is_menu_related else "generate_general_response"

analyze_input 노드 실행 후 이 함수가 실행되어 다음 노드를 결정

(4). 🔧 그래프 구성

① 노드 등록

builder.add_node("get_user_query", get_user_query_node)
builder.add_node("analyze_input", analyze_input_node)
builder.add_node("search_menu_info", search_menu_info_node)
builder.add_node("generate_menu_response", generate_menu_response_node)
builder.add_node("generate_general_response", generate_general_response_node)

② 엣지 연결

builder.set_entry_point("get_user_query")
builder.add_edge("get_user_query", "analyze_input")

# 조건부 분기 적용
builder.add_conditional_edges(
    "analyze_input",
    condition=decide_next_step,
    path_map={
        "search_menu_info": "search_menu_info",
        "generate_general_response": "generate_general_response"
    }
)

# 이어지는 흐름
builder.add_edge("search_menu_info", "generate_menu_response")
builder.add_edge("generate_menu_response", END)
builder.add_edge("generate_general_response", END)

(5). 🧪 실행 흐름 예시

✅ 케이스 A: 메뉴 관련 질문

사용자 입력: “스테이크 가격 알려줘”
→ 메뉴 관련 분석 (True)
search_menu_infogenerate_menu_response
→ 벡터DB 검색 후 메뉴 설명과 가격 포함 응답

✅ 케이스 B: 일반 상식 질문

사용자 입력: “미국의 수도는 어디인가요?”
→ 메뉴 관련 분석 (False)
generate_general_response → 워싱턴 DC 출력

(6). 🔁 반복 실행 구조

while True:
    result = graph.invoke(initial_state={})
    print(result['final_answer'])

    if input("다른 질문이 있으신가요? (Y/N): ").lower() != 'y':
        break

그래프 전체를 루프 안에서 반복 실행해 대화형 흐름 유지

🧠 요약 다이어그램

[Start]
  ↓
[get_user_query] → 사용자 입력 받기
  ↓
[analyze_input] → 메뉴 관련 질문 여부 판단
   ↙               ↘
[search_menu_info]   [generate_general_response]
   ↓                       ↓
[generate_menu_response]   [END]
   ↓
 [END]

✅ 학습 포인트 요약

포인트설명
상태 기반 처리상태 객체를 통해 모든 노드 간 데이터 공유
조건부 엣지분기 로직을 파이썬 함수로 분리하여 깔끔한 흐름 설계
다단계 체인 구성LLM → 판단 → 도구 실행 → 응답 생성 흐름 완성
반복 실행 가능사용자와의 대화 시나리오로 확장 가능
profile
Lee_AA

0개의 댓글