RAG 만들다보면 요즘 다들 LangGraph 얘기를 하길래, 대체 뭐가 다른지 정리해봤다.
틀린 부분이나 피드백이 있다면 댓글로 알려주면 감사하겠다.
이 글은 테디노트님의 🔥LangGraph 개념 완전 정복 몰아보기(3시간)🔥 영상을 보며 정리한 내용이다.
LangChain은 커스터마이징에 한계가 있어서 호불호가 갈렸다.
그 한계를 풀어준 게 LangGraph, 그래서 인기가 많다.
그리고 LangGraph는 모듈러 RAG를 만들기 위한 도구이기도 하다.
일반 RAG는 문서에 있는 내용은 채우지만, 없는 내용은 못 채운다.
??? : 그럼 부족한 정보를 웹 검색해서 채워주면 되잖아?
반은 맞다. 근데 검색 결과에 틀린 정보가 섞이거나 결과 자체가 없으면? → 그대로 할루시네이션으로 이어진다.
이게 다 전통적인 RAG 파이프라인이 아래처럼 사전에 다 정해져 있어서 생기는 문제다.
단방향 파이프라인이라 한 번에 잘 해내야 하고, 이전 단계로 되돌아가기도 어렵다.
LangGraph는 각 과정을 Node로 정의하고, 그 사이를 Edge로 잇는다.
게다가 조건부 Edge로 분기 처리까지 가능해서, RAG 파이프라인을 훨씬 유연하게 짤 수 있다.
이제는 파이프라인이 아니라 Flow 엔지니어링을 하는 셈.
예를 들면 단계 중간에 평가자 노드를 넣어서 할루시네이션 확률을 확 줄일 수 있다.
State는 보통 TypedDict(타입힌트 붙은 dict)로 만들지만, 그냥 딕셔너리라고 생각해도 무방하다.
여기서 중요한 게 reducer(예: add_messages)다.
State의 모든 값을 매번 다 전달할 필요는 없고, 필요한 키만 노드가 읽고 쓰면 된다. 병합이 필요할 때 reducer가 그 역할을 한다.
def route_function(state: State) -> str:
if state["score"] > 0.8:
return "high_confidence"
return "low_confidence"
graph.add_conditional_edges(
"from_node",
route_function,
{
"high_confidence": "node_a",
"low_confidence": "node_b",
}
)
add_node("이름", 함수) 로 노드 등록add_edge("from", "to") 로 흐름 연결MemorySaver()로 대화 기록을 저장한다.draw_mermaid_png()).invoke(state, config). 이때 recursion_limit(뺑뻉이 방지용 최대 실행 횟수), thread_id(추적용)를 설정해줄 수 있다.결국 LangGraph는 RAG 파이프라인을 "고정된 한 방향"이 아니라 "노드 + 조건부 분기"로 유연하게 짤 수 있게 해주는 도구다.