
LangGraph는 LangChain 기반의 프레임워크로, 복잡한 LLM 워크플로우(Agent + Tool + 흐름 제어)를 상태 머신(State Machine) 방식으로 설계할 수 있게 해주는 도구입니다.
| 항목 | LangChain | LangGraph |
|---|---|---|
| 처리 흐름 | 직선적 순차 처리 (Chain 중심) | 분기, 반복, 병렬, 조건 흐름 등 유연한 그래프 기반 흐름 |
| 복잡한 워크플로우 | 어렵거나 코드 분기 복잡함 | 복잡한 시나리오를 자연스럽게 구성 가능 |
| 에이전트 구조 | 한 번 호출 후 도구 판단, 실행 | 노드 단위의 에이전트 정의 가능, 다단계 설계 가능 |
| 상태 관리 | 히스토리 수동 전달 필요 | 상태(state)를 명시적으로 관리 가능 |
SearchAgent, SummarizeAgent 등 각 역할 분리 가능[User Input]
↓
[Agent #1: 이해 및 판단]
├── 도구 사용 필요 O → 도구 실행 → 결과 전달
↓
[Agent #2: 요약 or 분석]
├── 작업 완료 여부 판단
└── 반복 수행 or 종료
| 기능 | 설명 |
|---|---|
| 🔁 반복 처리 | 특정 조건에서 반복 실행 가능 (while 같은 흐름 설계) |
| 🔀 분기 처리 | 조건에 따라 다른 경로 선택 (if-else처럼 작동) |
| 🧠 에이전트 연계 | 도구가 바인딩된 LLM 기반 Agent를 노드로 구성 가능 |
| 📦 상태 공유 | 이전 노드의 결과를 상태(state)에 저장해서 다음 노드에서 활용 가능 |
| 🔚 종료 제어 | 명시적으로 작업 종료 지점(end) 지정 가능 |
문서 분석 파이프라인
다단계 에이전트 시나리오
모듈형 업무 흐름
LangChain이 선형적 단일 에이전트/체인 중심이라면,
LangGraph는 LLM 기반 복잡한 플로우를 명확하게 설계할 수 있도록 지원합니다.
| 구성 요소 | 설명 |
|---|---|
| 상태 (State) | 전체 워크플로우의 공유 데이터. 모든 노드에서 공유하며, 클래스(Pydantic 모델)로 정의 |
| 노드 (Node) | 작업을 수행하는 함수. 상태를 입력으로 받아 상태를 출력으로 반환 |
| 엣지 (Edge) | 노드 간의 연결 경로. 조건 분기 등 흐름 제어 역할 수행 |
Initial State)가 그래프에 전달됨⚙️ 각 노드는 상태의 필드를 업데이트하거나 새로운 정보를 추가함.
class OverallState(BaseModel):
text: str
def node_fn(state: OverallState) -> OverallState:
state.text = "반갑습니다"
return state
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()
initial_state = OverallState(text="안녕하세요")
result = graph.invoke(initial_state)
print(result.text) # 출력: 반갑습니다
| 개념 | 설명 |
|---|---|
| 상태 공유 | 모든 노드는 동일한 상태 구조를 공유함 |
| 노드 함수 | 입력 상태 → 처리 → 출력 상태 (상태 업데이트) |
| 엣지 | 다음 노드를 결정하는 흐름 제어 장치 |
| 컴파일 | 정의한 그래프를 실행 가능한 워크플로우로 변환 |
| invoke() | 그래프 실행 메서드, 초기 상태를 전달함 |
builder.add_conditional_edges("check_node",
condition_fn=lambda state: state.text == "A",
true_node="A_node", false_node="B_node"
)
State Graph = 상태 기반 + 노드 기반 + 조건 흐름 제어가 가능한 유연한 워크플로우 그래프
1. 상태(State) 정의
2. 상태를 처리하는 노드(Node) 정의
3. 노드들을 연결하는 엣지(Edge) 구성
4. 그래프 컴파일 및 시각화
5. 그래프 실행 및 결과 출력
MenuState = TypedDict("MenuState", {
"user_preference": Optional[str],
"recommended_menu": Optional[str],
"menu_info": Optional[str],
})
user_preference)recommended_menu)menu_info)각 노드는 MenuState를 입력 받고, 해당 필드를 업데이트함
get_user_preference()user_preference 필드를 업데이트recommend_menu()user_preference에 따라 추천 메뉴 선택recommended_menu 필드 업데이트provide_menu_info()menu_info 필드 업데이트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 → 선호도 → 추천 → 정보제공 → Endinitial_state = {"user_preference": None, "recommended_menu": None, "menu_info": None}
result = app.invoke(initial_state)
MenuState 형태의 딕셔너리User Preference: 해산물
Recommended Menu: 랍스터 파스타
Menu Info: 신선한 랍스터와 크림소스를 곁들인 파스타
from langgraph.graph import MermaidDrawer
MermaidDrawer(graph).draw_svg()
| 구성 요소 | 내용 |
|---|---|
| 상태 (State) | 공유되는 데이터 구조 (유저 선호, 추천 메뉴, 메뉴 정보) |
| 노드 (Node) | 상태를 입력받아 처리하고 다시 상태를 반환하는 함수 |
| 엣지 (Edge) | 노드 간의 실행 흐름 연결 |
| 실행 (Invoke) | 초기 상태를 입력 → 자동으로 순차적 노드 실행 |
| 출력 | 최종 MenuState 결과 반환 |
사용자의 입력에 따라 두 가지 다른 경로로 처리되는 분기형 그래프 구현:
class MenuState(BaseModel):
user_query: str
is_menu_related: bool
search_result: list[str]
final_answer: str
LangGraph는 상태 기반 흐름이므로 각 노드 간 전달될 데이터를 명확히 정의
| 노드 이름 | 역할 |
|---|---|
get_user_query | 사용자 입력을 받아 user_query 업데이트 |
analyze_input | LLM으로 입력이 메뉴 관련인지 분석 → is_menu_related 필드 업데이트 |
search_menu_info | 벡터DB로 문서 검색 → search_result 필드 업데이트 |
generate_menu_response | 검색된 문서 기반으로 메뉴 관련 답변 생성 → final_answer |
generate_general_response | 메뉴와 무관한 일반 질문에 대한 LLM 응답 생성 → final_answer |
def decide_next_step(state: MenuState) -> str:
return "search_menu_info" if state.is_menu_related else "generate_general_response"
analyze_input노드 실행 후 이 함수가 실행되어 다음 노드를 결정
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)
사용자 입력: “스테이크 가격 알려줘”
→ 메뉴 관련 분석 (True)
→search_menu_info→generate_menu_response
→ 벡터DB 검색 후 메뉴 설명과 가격 포함 응답
사용자 입력: “미국의 수도는 어디인가요?”
→ 메뉴 관련 분석 (False)
→generate_general_response→ 워싱턴 DC 출력
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 → 판단 → 도구 실행 → 응답 생성 흐름 완성 |
| 반복 실행 가능 | 사용자와의 대화 시나리오로 확장 가능 |