LangGraph - State Machine

LangGraph는 LangChain에서 Blackbox의 Chain 형태로 표현된 Agent Workflow를 Graph와 State Machine으로 모델링한다.
핵심 구조를 정리하면 아래와 같다.
- State: 그래프 전체의 현재 상태를 담은 데이터 구조 (예: 대화 히스토리, 중간 결과, 선택된 툴, 에러 정보, 등)
- Node(노드): 실제 일을 하는 함수들 (LLM 호출, MCP Tool 호출, 룰 기반 로직 등)
- Edge(엣지): “어떤 노드가 끝난 뒤, 다음에 어떤 노드가 실행될지”를 정하는 전이(transition) 규칙
이렇게 State Machine 구조를 도입함으로써 LangGraph는 LangChain에 비해 다음의 이점을 누릴 수가 있게 되었다.
5-1. 제어 흐름이 명시적이다
- LangChain Agent:
- “Tool 선택/순서를 LLM이 프롬프트 안에서 알아서 결정”
- 제어 흐름이 LLM의 내적 추론에 묻혀 있음 → opaque
- LangGraph:
- 가능한 노드/경로가 그래프(상태머신) 구조로 정해져 있음
- LLM은 필요할 때 Node 안에서 쓰일 뿐, 전체 워크플로우는 코드/그래프로 눈으로 보고 관리 가능
5-2. 복잡한 정책/제약을 “전이 규칙”으로 강제 가능
- 예:
- AUTH 노드가 성공해야만 CALL_API 노드로 전이
- 에러 플래그 state["error"]가 True면, 항상 RECOVER 노드로 전이
- 특정 툴은 하루 1회만 허용 → Edge에서 rate-limit 체크 후 전이 제어
이걸 프롬프트가 아니라, router 함수 + State 필드 + 그래프 구조로 강제하기 때문에 디버깅/테스트가 용이하고, “이 경로는 절대 안 일어나게 해라” 같은 걸 정형적으로 보장할 수 있다.
5-3. Multi-agent / Tool 그룹화를 상태머신 위에 자연스럽게 얹기 좋음


위의 그림처럼 LangGraph 팀이 공식 블로그에서 이야기하는 multi-agent 구조도 사실 “상태머신”의 다른 표현이다.
- 각 Agent = 하나의 Node (또는 서브그래프)
- Router = 상태 기반 전이함수
- Shared Scratchpad / 개별 Scratchpad = State 필드 설계 방식
그래서:
- Agent A → 작업 → State 업데이트
- Router가 State 보고 “이제 Agent B로 넘어가자” 전이
- 이런 식으로 multi-agent workflow = 상태머신으로 표현된다.
State의 구성

LangGraph가 State Machine 구조라는 것은 곧, 노드들 간의 정보 전달이 State 구조로 이루어진다는 뜻이다.
이때 각 노드는 Dictionary(key와 value 상) 형태의 State에 자신이 맡은 부분을 채워넣는 식으로 동작한다.
따라서 LangGraph의 State 정의는 다음과 같은 형태를 띤다.
from typing import Annotated, TypedDict
# 가장 단순한 GraphState 예시
class GraphState(TypedDict):
question: Annotated[str, "UserQuestion"] # 사용자의 질문
context: Annotated[str, "RetrievedContext"] # 검색된 정보
answer: Annotated[str, "FinalAnswer"] # 최종 답변
이 구조에서 User가 "What is LangGraph?"라는 메시지를 던졌다고 하자, 이게 우선 State의 Question에 저장된다.
state = GraphState(question="What is LangGraph?", context="", answer="")
| Key | Value |
| question | What is LangGraph? |
| context | (empty) |
| answer | (empty) |
이 State는 검색 노드에 들어가고, 검색 노드는 다음의 Retrieved Context를 추가한다.
state["context"] = "LangGraph is a state-machine framework for LLM orchestration."
| Key | Value |
| question | What is LangGraph? |
| context | LangGraph is a state-machine framework for LLM orchestration. |
| answer | (empty) |
현재 State에 Question과 Retrieved Context가 채워진 상태인데,
최종적으로 답변 노드가 이 State를 받고 최종 answer를 생성한다.
state["answer"] = "LangGraph is a framework that lets you build LLM agents using a deterministic state-machine architecture."
| Key | Value |
| question | What is LangGraph? |
| context | LangGraph is a state-machine framework for LLM orchestration. |
| answer | LangGraph is a framework that lets you build LLM agents using a deterministic state-machine architecture. |
Node의 구성
그럼 위 예시에서 question에 대한 문서를 검색하고, context를 반환하는 Node는 다음과 같이 구성될 수 있다.
def retrieve_document(state: GraphState) -> GraphState:
retrieved_docs = pdf_retriever.invoke(state["question"])
return GraphState(context=format_docs(retrieved_docs))
이때, 위와 같은 함수 전체가 LangGraph에서 하나의 Node가 된다.
LangGraph에서는 각 노드가 GraphState → GraphState 변환 함수이기 때문이다.
즉, 입력으로 GraphState("Question")을 받아서 내부 로직으로 문서를 검색하고, GraphState("context")를 출력하는 구조가 Node의 정의와 완전히 동일하다.
Node를 만드는 방법은 아래와 같다.
from langgraph.graph import END, StateGraph
# GraphState 타입을 기반으로 하는 stateful workflow 생성
# 이후에 노드를 붙여넣는 컨테이너 역할
workflow = StateGraph(GraphState)
# 노드 추가
workflow.add_node("retrieve", retrieve_document) # 정의한 retrieve_document
workflow.add_node("llm_answer", llm_answer) # 정의한 llm_answer
이처럼 노드를 추가하는 방법은 add_node를 사용한다.
왼쪽에는 노드 이름, 오른쪽에는 함수 명을 적어주면 된다.
이전 단계에서 retrieve_document라는 함수가 여기에 들어간 것. 그래서 retrieve_document 있고, llm_answer라는 함수가 있었다. 그럼 retrieve라는 노드의 이름을 지어주는 것이고, 오른쪽에는 함수를 넣어주는 것. 이를 통해 retrieve_document라는 노드와 llm_answer라는 노드가 생기는 것이다.
Edge의 구성
우리가 retrieve_document, 혹은 llm_answer라는 노드가 있다면, 어떤 노드에서 어떤 노드로 갈 지를 정의를 해줘야 한다.
이는 add_edge라는 함수로 가능하다.
workflow.set_entrypoint("retrieve") # 시작 노드
workflow.add_edge("retrieve", "llm_answer") # 노드 연결
workflow.add_edge("llm_answer", END) # 종료
이를 통해 retrieve 노드가 question을 받아서 Context를 추가하고, 추가된 State가 llm_answer 노드로 들어가서 최종 답변을 생성하는 LangGraph Pipeline이 완성된다.
이때, 조건부 Edge를 통해 더 복잡한 분기를 만들 수 있다.
만약, relevance_check를 통해서 llm_answer 노드가 내놓은 답을 검증하고 싶다면?
relevance_check 노드를 만들고, 이를 llm_answer와 Node로 연결하는 것 까지는 동일한데,
relevance_check을 통해 다시 llm_answer, 혹은 retrieve로 돌아가는 edge를 추가하면 된다.
조건부 Edge의 구조는 아래와 같다. (node3에서 조건부로 다른 노드로 routing되는 구조)

'Agentic AI 구축 > Agentic AI 아키텍처' 카테고리의 다른 글
| Cisco의 LangGraph 기반 Agentic AI 프레임워크 (Supervisor Framework) (0) | 2026.03.14 |
|---|---|
| LLM Agent Architecture의 발전 과정 총정리 (ReAct, Multi-Agent, Deep-Agent) (0) | 2025.12.07 |
| Deep Agent의 개념과 아키텍처 (Multi-Agent와의 차이점) (0) | 2025.12.07 |
| Multi-Agent 협업 네트워크 아키텍처 (0) | 2025.12.07 |