RAG (Retrieval-Augmented Generation)
- LLM의 한계를 극복하기 위해 '지식 검색'과 '언어 생성'을 결합한 프레임워크
RAG 동작 과정
질문 이해 및 처리(Query Encoder) | 지식 검색(Retrieval) | 생성(Generaton) |
- 사용자의 질문을 이해하기 위한 언어 모델 - 질문을 벡터 형태로 인코딩 |
- 외부 지식 베이스에서 관련 정보를 검색 | - LLM이 쿼리와 검색된 지식을 이용하여 응답 생성 - LLM Prompting |
RAG 장단점
1. 장점
- 외부 지식 활용하여 LLM의 지식 부족 문제를 해결하고, 최신 정보 또는 특정 도메인 지식을 활용할 수 있습니다.
- 증거를 기반 생성하여 검색된 지식 정보를 증거로 활용하여 보다 사실에 기반한 답변 생성, 생성된 답변의 출처를 명시함으로써 신뢰성 향상
2. 문제점
→ RAG 파이프라인이 단방향 구조이기 때문에 모든 단계를 한 번에 다 잘해야 함.
- 사전에 정의된 데이터 소싱(PDF, DB, Table 등) 자원
- 사전에 정의된 Fixed Size Chunk
- 사전에 정의된 Query 입력
- 사전에 정의된 검색 방법
- 신뢰하기 어려운 LLM 혹은 Agent
- 고정된 프롬프트 형식
- LLM의 답변 결과에 대한 문서와의 관련성/신뢰성
LangGraph
🤔 RAG 믿을만한가?
- LLM이 생성한 답변이 Hallucination이 아닐까?
- RAG를 적용하여 받은 답변이 문서에 없는 "사전지식"으로 답변한 건 아닐까?
- "문서 검색에서 원하는 내용이 없을 경우" 인터넷 혹은 논문에서 부족한 정보를 검색하여 지식을 보강할 수는 없을까?
1. LangGraph 등장
- Node, Edge, State(상태관리)를 통해 LLM을 활용한 워크플로우에 순환(Cycle) 연산을 추가하여 손쉽게 흐름을 제어.
- RAG 파이프라인을 보다 유연하게 설계, 세부 단계별 흐름 제어가 가능
- Node: 각 세부과정
- Edge: 이전노드 > 다음노드 연결
평가자(Evaluator) → 질문 재작성 (Query Trnsform)
→ 추가 검색기(web search)를 통해 문맥(context) 보강
→ 답변 평가를 통해 검증 및 re-generate
LangGraph는 랭체인을 사용하지 않아도 그래프 형식으로 LLM(거대 언어 모델) 기반 애플리케이션의 흐름을 잡아주는 매우 유용한 프레임워크입니다. 이 프레임워크는 RAG(Retrieval Augmented Generation) 개발 단계에서 마주하는 다양한 고민, 예를 들어 LLM 답변의 할루시네이션(환각) 우려, 문서에 없는 내용으로 답변하는 문제, 부족한 정보를 보강하는 필요성, 복잡하고 단방향적인 RAG 파이프라인 구조의 한계 등을 해결하기 위해 탄생했습니다. 특히, 전통적인 RAG 파이프라인이 단방향 구조로 인해 모든 단계를 한 번에 잘 수행해야 하는 문제점을 가졌던 것에 반해, LangGraph는 다이나믹한 특성을 통해 파이프라인을 더욱 정교하게 만들어낼 수 있습니다.
2. LangGraph의 주요 개념 및 구성 요소
LangGraph는 기술 발전이 기존의 불편함과 갈증을 해소하기 위해 시작되었듯이, RAG 개발의 어려움을 극복하고자 등장했습니다. 핵심적으로 다음과 같은 구성 요소와 개념을 포함합니다.
- 노드 (Nodes):
- LangGraph에서 각 세부 과정은 노드라고 불립니다. 예를 들어, 데이터 로더, 스플릿, 임베딩, 저장, 검색, 프롬프트, LLM 답변 생성 등 각각의 역할 하나하나가 노드로 정의될 수 있습니다.
- 노드는 파이썬 함수로 구현되며, 입력과 출력 모두 '상태(State)'를 가집니다. 이는 노드 내부 로직을 자유롭게 구성할 수 있음을 의미하며, 랭체인 문법에 대한 의존성을 크게 줄일 수 있습니다. 심지어 회사 내부의 DB 조회 API와 같은 사용자 정의 함수도 노드 안에 포함될 수 있습니다.
- 노드들은 서로 독립적이며, 마치 '서로를 모르는 회사들'처럼 동작합니다.
- 엣지 (Edges):
- 노드와 노드를 연결하는 부분을 엣지라고 합니다. 이를 통해 이전 노드에서 다음 노드로의 흐름을 정의합니다.
- 조건부 엣지 (Conditional Edges): LangGraph의 강력한 기능 중 하나로, 중간에 파이프라인의 분기 처리를 가능하게 합니다. 예를 들어, 답변 유형(추상적/구체적)에 따라 다른 노드로 보내거나, LLM의 평가 결과에 따라 다른 흐름으로 전환할 수 있습니다. 조건부 엣지는 두 갈래뿐만 아니라 세 갈래, 네 갈래 등 다중 분기 처리가 가능합니다.
- 상태 관리 (State Management):
- LangGraph에서 매우 중요한 개념으로, 노드와 노드 간의 정보를 전달하는 매개체입니다. 노드들은 이 '상태' 객체에 작업 결과물을 담아 다음 노드로 전달하며, 다음 노드는 이 상태를 통해 이전 노드의 작업 내용을 알 수 있습니다.
- 상태는 TypeDict를 상속받은 GraphState에 담겨 전달됩니다. TypeDict는 일반 파이썬 딕셔너리에 타입 힌팅 기능을 추가한 개념으로, 키-값 쌍의 타입을 명시할 수 있습니다.
- 리서 (Reaser): add_messages와 같은 개념으로, 리스트 형태의 상태 값에 새로운 값을 **덮어씌우지 않고 자동으로 추가(누적)**하는 기능을 제공합니다. 이를 통해 멀티턴 대화 구현이 용이해집니다.
- 상태는 모든 값을 다 채울 필요가 없으며, 새로운 노드에서 기존 값을 덮어쓰거나 새로운 값을 추가할 수 있습니다.
- 순환 연산 (Cyclical Operations):
- LangGraph는 LLM을 활용한 워크플로우에 순환 기능을 추가하여 정보 보강이나 더 좋은 답변 생성을 위해 사이클적으로 돌아가면서 구성을 할 수 있게 합니다. 이는 단방향 파이프라인의 한계를 극복하는 핵심 기능입니다.
- 휴먼 인 더 루프 (Human-in-the-Loop):
- 노드에 사람이 개입할 수 있도록 허용하는 기능입니다. 특정 노드에 도달했을 때 입력값을 조정하거나, 진행 여부를 사람이 판단할 수 있도록 구성할 수 있습니다.
- 체크포인트 (Checkpoint):
- 과거 실행 과정의 내용을 저장하는 역할을 합니다. 이 덕분에 멀티턴 대화 구현이 매우 쉬워지며, 이전에 수행했던 결과에 대한 수정이나 리플레이(Replay) 기능도 제공하여 특정 단계부터 다시 실행하는 것이 가능합니다. Thread ID를 통해 대화 내용을 구분하여 저장할 수 있어, 여러 대화방을 오가는 것처럼 유연한 관리가 가능합니다.
3. LangGraph 파이프라인 구축 과정
LangGraph로 그래프를 생성하는 주요 단계는 다음과 같습니다:
- 스테이트 정의 (State Definition): TypeDict를 상속받은 GraphState 클래스를 정의하여 노드 간 공유할 정보의 키와 타입을 명시합니다.
- 노드 정의 (Node Definition): 각 노드는 상태를 입력으로 받고 상태를 반환하는 파이썬 함수로 구현합니다.
- 그래프 정의 및 엣지 연결 (Graph Definition & Edge Connection):
- StateGraph를 사용하여 워크플로우 객체를 생성합니다.
- add_node를 통해 각 노드의 이름과 해당 함수를 추가합니다.
- add_edge를 통해 노드 간의 순차적 흐름을 정의합니다 (from-to 방식).
- add_conditional_edges를 통해 조건부 분기를 추가합니다. 이는 노드의 결과에 따라 다음 노드를 결정하는 함수(콜러블)를 포함합니다.
- set_entry_point를 통해 그래프의 시작점을 지정합니다.
- 컴파일 (Compile): 정의된 워크플로우를 컴파일하여 실행 가능한 그래프를 만듭니다. 이때 메모리(체크포인트) 설정을 통해 대화 기록 저장 기능을 추가할 수 있습니다.
- 시각화 (Visualization): get_graph().draw_mermaid_png()와 같은 기능을 통해 복잡한 코드만으로는 파악하기 어려운 흐름을 한눈에 시각적으로 확인할 수 있습니다.
- 실행 (Execution):
- invoke_graph (디버깅 용이): 노드별 반환값 모니터링에 유용하며, 노드의 출력값을 확인할 수 있습니다.
- stream_graph (실시간 출력): LLM의 답변 생성 부분을 토큰 단위로 실시간 스트리밍 출력하여 서비스 단계에 적합합니다.
- RunnableConfig를 통해 recursion_limit (순환 로직에 빠지는 것을 방지하기 위한 최대 노드 방문 수)와 thread_id (멀티턴 대화 시 대화 내용을 구분하여 저장)를 설정할 수 있습니다.
- get_state를 통해 최종적으로 모든 정보가 누적된 상태 값을 확인하고 원하는 값을 추출할 수 있습니다.
4. LangGraph를 활용한 RAG 파이프라인 구현 사례
LangGraph는 다양한 RAG 파이프라인을 유연하게 구현하는 데 사용될 수 있습니다.
- 기본 RAG (Naive RAG): 사용자 질문을 받아 문서 검색을 수행하고, 검색된 문서를 LLM에 전달하여 답변을 생성하는 가장 간단한 구조를 LangGraph로 구현할 수 있습니다. 이때 검색된 문서를 XML 태깅 등으로 포매팅하여 LLM이 더 잘 이해하도록 도울 수 있습니다.
- 관련성 체크 추가 (Relevance Check): 검색된 문서가 질문과 관련성이 있는지 LLM 저지(judge)를 통해 평가하는 로직을 추가할 수 있습니다. 관련성이 없으면 다시 문서 검색 단계로 돌아가도록 조건부 엣지를 설정할 수 있어, 할루시네이션 가능성을 줄이고 RAG의 정확도를 높일 수 있습니다.
- 웹 검색 모듈 추가 (Web Search): 문서 내에 정보가 없을 경우 웹 검색을 통해 부족한 정보를 보강하는 로직을 추가할 수 있습니다. 관련성 체크 결과가 '노(No)'일 때, 기존 문서 검색 대신 웹 검색 노드로 분기하여 외부 지식을 활용할 수 있습니다.
- 쿼리 재작성 모듈 추가 (Query Rewriting): 사용자의 질문이 불분명하거나 개선이 필요할 경우, LLM을 통해 질문을 재작성(rewrite)하는 노드를 추가할 수 있습니다. 재작성된 쿼리로 다시 문서 검색을 수행하여 더 적절한 답변을 유도합니다. 원본 질문은 보존하면서 리라이트된 질문을 누적하여 활용할 수 있습니다.
- 에이전트 RAG (Agentic RAG): 에이전트를 LangGraph 노드 안에 통합하여, 에이전트가 스스로 도구를 사용할지 말지 판단하도록 할 수 있습니다. 예를 들어, 간단한 상식 질문에는 LLM이 직접 답변하고, 특정 문서 검색이 필요한 질문에만 리트리버 도구를 사용하도록 하여 불필요한 토큰 낭비와 지연을 줄일 수 있습니다. 툴(Tool) 정의 시 이름과 상세한 설명을 제공하여 LLM이 적절한 툴을 선택하도록 유도하는 것이 중요합니다.
- 적응형 RAG (Adaptive RAG): 쿼리 분석과 능동적 자기 수정(Self-Reflective RAG)을 결합한 고급 전략입니다.
- 쿼리 라우팅: LLM으로 하여금 질문의 성격(예: 벡터 스토어 검색 필요, 웹 검색 필요)을 분석하여 적절한 데이터 소스로 라우팅하도록 합니다. with_structured_output을 사용하여 LLM의 출력값을 정형화된 형태로 유도할 수 있어, 에이전트보다 더 세밀한 제어가 가능합니다.
- 문서 평가 및 압축: 검색된 문서들을 개별적으로 평가하여 관련성 없는 노이즈를 제거하고, 알짜배기 정보만 압축하여 LLM에 제공함으로써 답변 품질을 향상시킵니다.
- 할루시네이션 체커 및 답변 관련성 평가: 생성된 답변이 문서에 기반했는지(할루시네이션 여부)와 질문에 대한 관련성이 있는지(렐러번스)를 LLM 평가자를 통해 판단합니다. 할루시네이션이 있으면 다시 답변 생성을 유도하고, 관련성이 없으면 쿼리 재작성을 통해 새로운 탐색을 시도합니다.
5. 장점 및 고려 사항
LangGraph는 모듈화된 구조 덕분에 각 기능을 독립적인 노드로 만들고 조립하듯이 파이프라인을 구축할 수 있어, 흐름 변경이 매우 유연하고 간편합니다. 이를 통해 복잡한 RAG 파이프라인도 손쉽게 설계하고 테스트할 수 있습니다. 또한, 순환 및 체크포인트 기능은 멀티턴 대화와 디버깅을 효과적으로 지원합니다.
하지만, LangGraph는 다수의 LLM 호출과 중간 단계의 처리로 인해 지연 시간이 늘어날 수 있다는 단점도 있습니다. 답변의 품질은 높아지지만, 사용자 경험 측면에서 대기 시간이 길어질 수 있는 것이죠. 이러한 지연을 완화하기 위해, Perplexity.ai와 같이 중간 과정(검색, 평가 등)을 사용자에게 실시간으로 보여주는 UX를 고려하거나, 스트리밍 출력 기능을 활용하는 것이 중요합니다.
6. 이해를 돕는 비유
LangGraph로 복잡한 RAG 파이프라인을 구축하는 것은 마치 건축가가 정교한 건축물을 짓는 과정과 같습니다.
- 노드는 건축물을 구성하는 다양한 종류의 모듈화된 벽돌이나 블록에 비유할 수 있습니다. 각 블록(노드)은 독립적인 기능을 하지만, 전체 구조의 일부로 사용됩니다.
- 엣지는 이 블록들을 연결하는 접착제나 연결 통로입니다. 어떤 블록 다음 어떤 블록이 올지, 그리고 어떻게 연결될지를 정의합니다.
- 조건부 엣지는 건축물의 자동 제어 시스템과 같습니다. 예를 들어, 날씨가 비가 오면 자동으로 창문을 닫거나, 태양의 위치에 따라 블라인드를 조절하는 것처럼, 특정 조건에 따라 건축물(파이프라인)의 흐름이 자동으로 바뀌는 것을 의미합니다.
- 상태 관리는 블록들을 옮길 때 필요한 정보를 담은 컨테이너입니다. 각 블록은 컨테이너에 담긴 정보를 바탕으로 작업을 수행하고, 그 결과를 다시 컨테이너에 담아 다음 블록으로 전달합니다. 이전 블록이 어떤 작업을 했는지 일일이 알 필요 없이, 컨테이너의 내용만 확인하면 됩니다.
- 순환 연산은 건축 과정에서 문제가 발생했을 때 특정 공정으로 되돌아가 재작업하는 것과 같습니다. 한번에 완벽하지 않아도, 문제가 해결될 때까지 반복하여 최종 결과물의 품질을 높일 수 있습니다.
- 체크포인트는 건축물의 중간 설계도면이나 스냅샷입니다. 언제든 이 설계도로 돌아가 수정하거나, 이전 단계를 다시 시작할 수 있어 복잡한 프로젝트 관리와 변경에 매우 유용합니다.
이처럼 LangGraph는 유연하고 강력한 구조를 통해 복잡한 LLM 애플리케이션, 특히 RAG 파이프라인을 체계적이고 효율적으로 구축할 수 있도록 돕는 도구입니다.
Last Updated. 2025.07.22
🔖 참고 자료
'NLP | LLM' 카테고리의 다른 글
로컬 환경에서 필수인 Ollama에 대해 알아보기 (3) | 2025.06.18 |
---|---|
AI Agent의 모든 것 (5) | 2025.06.17 |
[LangChain] 1. LangChain의 모든 것 (3) | 2025.06.17 |
[RAG] 2. Query 추론 및 재생성 (0) | 2025.06.10 |
[Text Summarization] 1. TextRank (0) | 2022.10.17 |