LLM WikiAccess-protected knowledge portal

WIKI

LangGraph 1.2: 장기 실행 에이전트를 위한 DeltaChannel·TimeoutPolicy·스트리밍 v2

에이전트가 길어질수록 체크포인트 크기는 어떻게 변하는가 LLM 에이전트를 단순한 request/response가 아니라 장기 실행 워크플로로 운영할 때 가장 먼저 나타나는 문제는 checkpoint 크기 폭증 이다. LangGraph는 상태를 그래프 실행 경계마다 직렬화해 checkpoint store에 저장한다. 에이전트가 대화를 이어가면 messages 채널처럼 누적되는 값은 스텝마다 전체를 다시 직렬화한다. 500 턴 대

경로human/study/content/ai-frontier/09-langgraph-1-2-deltachannel-timeout-streaming.md
카테고리Study
태그#ai-review #deltachannel #frontier #langgraph #streaming #study #timeout

에이전트가 길어질수록 체크포인트 크기는 어떻게 변하는가

LLM 에이전트를 단순한 request/response가 아니라 장기 실행 워크플로로 운영할 때 가장 먼저 나타나는 문제는 checkpoint 크기 폭증이다.

LangGraph는 상태를 그래프 실행 경계마다 직렬화해 checkpoint store에 저장한다. 에이전트가 대화를 이어가면 messages 채널처럼 누적되는 값은 스텝마다 전체를 다시 직렬화한다. 500 턴 대화라면 1 턴의 delta가 아니라 500 턴 전체가 담긴 blob이 매 스텝 저장된다.

상황저장 방식500 턴 시점 blob 크기
기존 add_messages 채널전체 메시지 목록 재직렬화약 219 MB
DeltaChannel (snapshot_frequency 없음)delta만 기록약 3 KB
DeltaChannel (snapshot_frequency=10)10 스텝마다 full snapshot약 5배 절감, 읽기 지연 실용적 수준

2026년 5월 12일 출시된 LangGraph 1.2.0은 이 문제를 DeltaChannel로 구조적으로 해결했다. 같은 릴리스에서 TimeoutPolicy와 streaming v2 API도 함께 출시됐다.

이 장은 2026년 7월 15일 기준으로 작성했다. LangGraph 1.2.0은 2026-05-12에 출시됐고 최신 패치는 1.2.9(2026-07-10)다. DeltaChannel과 TimeoutPolicy는 1.2.0에서 beta로 표시됐으며 streaming v2는 opt-in이다. API 고정보다 검증 가능한 운영 계약에 초점을 맞춘다.


LangGraph 상태 관리와 채널 구조

LangGraph의 상태는 그래프 전역 TypedDict(또는 dataclass)로 선언한다. 각 필드는 채널(channel)에 대응하며, 채널은 스텝 사이 값을 저장하고 reducer를 통해 값을 합친다.

LangGraph 1.2: 채널 유형과 체크포인트 저장 경계 State (TypedDict) add_messages (기존) BinaryOperatorAggregate: 전체 list 재직렬화 DeltaChannel (1.2 신규) delta만 저장, 복원 시 replay current_task: str LastValue 채널 (기본) 그래프 실행 (Pregel) Node A async def (timeout 적용 가능) Node B TimeoutPolicy 부착 스텝 완료 → reducer 적용 BinaryOperatorAggregate: 전체 값 직렬화 DeltaChannel: delta 값만 직렬화 후 저장 Checkpoint Store 기존 blob 500 턴 → 219 MB (전체 list) DeltaChannel blob 500 턴 → 3 KB (delta + N마다 snapshot) TimeoutPolicy (async 노드 전용) run_timeout 하드 wall-clock 제한 idle_timeout 진행 신호(write·chunk)마다 리셋 → NodeTimeoutError 발생 (retryable by default) Streaming v2 (opt-in: version="v2") StreamPart type · ns · data TypedDict per mode langgraph.types에서 import v1 호환 유지 기본 동작 변경 없음 모든 스트림 청크를 단일 구조로 통일
LangGraph 1.2 채널·체크포인트·TimeoutPolicy·스트리밍 구조

기존 add_messages 어노테이션은 BinaryOperatorAggregate 채널로 연결된다. 이 채널은 reducer가 반환한 값 전체를 매 스텝 직렬화한다. 메시지가 100개면 100개짜리 리스트, 500개면 500개짜리 리스트가 checkpoint에 들어간다.

DeltaChannel은 이 직렬화 경계를 바꾼다. 노드가 쓴 값만 delta로 기록하고, restore 시 sentinel 값부터 delta를 순서대로 replay해 원래 값을 재구성한다.


DeltaChannel: delta를 checkpoint에 저장하는 fold-reducer 패턴

DeltaChannel을 쓰면 필드 어노테이션이 바뀐다.

from langgraph.channels.delta import DeltaChannel
from typing import Annotated

class State(TypedDict):
    messages: Annotated[list[BaseMessage], DeltaChannel(snapshot_frequency=10)]

DeltaChannelsnapshot_frequency 파라미터 하나로 저장 공간과 읽기 지연을 조정한다.

snapshot_frequency동작저장 공간restore 지연
None (기본)delta만 기록, snapshot 없음최소전체 history replay (history 비례)
정수 KK 스텝마다 full snapshot 삽입중간최근 snapshot부터 replay
1매 스텝 full snapshot기존과 동일최소

snapshot_frequency=None은 저장 공간은 가장 작지만 history가 길수록 restore 지연이 늘어난다. snapshot_frequency=10은 약 5배 절감 효과를 유지하면서 실용적인 읽기 지연을 확보한다.

내부 동작을 이해하면 적합한 채널을 고를 수 있다.

  1. 노드가 새 메시지를 쓰면 DeltaChannel은 그 delta 값만 checkpoint row에 직렬화한다.
  2. 저장 크기는 turn 수와 무관하게 마지막 write 크기에 비례한다.
  3. thread를 복원할 때 가장 최근 full snapshot부터 이후 delta를 순서대로 적용해 현재 값을 재구성한다.
  4. snapshot_frequency=K는 K 스텝마다 그 시점의 전체 값을 snapshot row로 저장해 replay 길이에 상한을 둔다.

중요한 제약: DeltaChannel은 1.2.0에서 beta API다. reducer가 pure function이 아닌 채널에는 적합하지 않다. 예를 들어 timestamp를 자동으로 붙이거나 random 값을 사용하는 reducer는 replay 시 일관된 결과를 보장하지 않는다. 채택 전에 reducer가 동일 입력에 대해 항상 동일 출력을 내는지 확인한다.


TimeoutPolicy: async 노드에 하드 시간 경계를 추가하는 방법

장기 실행 에이전트가 외부 API를 호출하거나 LLM 응답을 기다릴 때 특정 노드가 무기한 실행될 수 있다. 이를 제어하려면 TimeoutPolicyadd_node()에 전달한다.

from langgraph.types import TimeoutPolicy

async def tool_node(state: State) -> dict:
    result = await some_slow_api_call(state)
    return {"messages": [result]}

graph.add_node(
    "tool",
    tool_node,
    timeout=TimeoutPolicy(run_timeout=60.0, idle_timeout=10.0)
)

run_timeoutidle_timeout은 독립적으로 작동하며 먼저 도달하는 쪽이 발동한다.

run_timeout: 노드 시작부터 hard wall-clock을 센다. 진행 상황과 무관하게 60초가 지나면 즉시 NodeTimeoutError를 발생시킨다. 대용량 파일 처리처럼 실행 총시간에 상한이 필요한 경우에 적합하다.

idle_timeout: 마지막 진행 신호 이후 경과 시간을 센다. LangChain LLM 모델의 스트리밍 청크, 채널 write, 자식 태스크 이벤트가 모두 진행 신호다. 스트리밍 LLM 응답은 청크마다 타이머를 리셋하므로 total 응답 시간이 길더라도 idle_timeout에 걸리지 않는다. 진짜로 멈춘 호출만 잡아낸다.

TimeoutPolicy(
    run_timeout=300.0,   # 5분 초과 실행 금지
    idle_timeout=30.0    # 30초 이상 진행 없으면 종료
)

NodeTimeoutError는 기본적으로 retryable이다. RetryPolicy와 함께 쓰면 타임아웃 후 자동 재시도가 가능하다. 단 run_timeout에 걸렸다면 재시도해도 같은 시간이 걸릴 가능성이 높으므로 retry budget과 함께 설계한다.

핵심 제약: TimeoutPolicy는 async 노드만 지원한다. sync 함수에 timeout을 붙이면 compile-time에 오류가 발생한다. 기존 sync 노드를 마이그레이션할 때는 반드시 async def로 바꿔야 한다. 이때 event loop 안에서 blocking I/O를 수행하는 코드는 asyncio.to_thread()loop.run_in_executor()로 감싸야 한다.

표준 LangChain 콜백 없이 자체 루프를 도는 노드에서 idle_timeout이 너무 공격적으로 발동한다면 refresh_on="heartbeat" 모드와 runtime.heartbeat() 명시 호출을 사용한다.


Streaming v2: 타입 안전한 통합 스트리밍 API

기존 stream()astream()은 mode 파라미터(values, updates, messages, custom 등)에 따라 반환 타입이 달라 타입 체킹이 어려웠다. 1.2의 streaming v2는 모든 청크를 StreamPart로 통일한다.

async for chunk in agent.astream(
    {"messages": [("user", "안녕하세요")]},
    version="v2"
):
    chunk_type = chunk["type"]   # "values" | "updates" | "messages" | ...
    namespace = chunk["ns"]      # 이벤트 발생 노드/서브그래프
    data = chunk["data"]         # mode별 TypedDict payload

StreamPart의 구조는 세 키로 고정된다.

역할예시
type청크 종류"values", "updates", "messages"
ns이벤트 발생 namespace["agent", "tools"]
data실제 payloadmode별 TypedDict

각 mode별 TypedDict는 langgraph.types에서 직접 import할 수 있어 IDE 자동완성과 mypy 검사가 쉬워진다. 서브그래프가 중첩된 경우에도 ns 필드로 이벤트 출처를 구분할 수 있어 디버깅이 편해진다.

v2는 opt-in이다. version 파라미터를 명시하지 않으면 기존 v1 동작을 그대로 유지한다. 기존 스트리밍 코드를 즉시 마이그레이션하지 않아도 된다.


운영 고려사항

DeltaChannel 도입 시 확인할 세 가지

저장소 row 수: DeltaChannel을 켜면 단일 row 크기는 줄지만 스텝 수만큼 row 수가 늘어난다. snapshot_frequency=10이면 10 스텝마다 full snapshot row도 추가된다. checkpoint store의 row 수 상한이나 쿼리 scan 비용이 있다면 함께 확인한다.

restore 지연: 현재 스레드를 복원할 때 store에서 여러 row를 순서대로 읽어야 한다. store latency가 높은 환경에서는 snapshot_frequency를 낮춰 read 횟수를 제한한다.

beta API 안정성: DeltaChannel은 1.2.0에서 beta다. minor version update에서 API가 변경될 수 있다. 프로덕션 적용 전 SDK 버전을 lockfile에 고정하고 변경사항을 추적한다.

TimeoutPolicy와 외부 서비스

idle_timeout의 진행 신호 인식은 LangChain LLM 모델에 최적화되어 있다. 자체 HTTP 클라이언트나 다른 스트리밍 프로토콜을 쓴다면 refresh_on="heartbeat" 모드로 전환하고 명시적으로 runtime.heartbeat()를 호출해야 신호가 전달된다.

run_timeout을 너무 짧게 설정하면 RetryPolicy와 결합했을 때 (run_timeout × max_attempts) 만큼의 wall-clock 시간이 소모된다. 총 실행 시간 예산과 함께 설정한다.

streaming v2와 Platform 호환성

v2는 opt-in이므로 기존 consumer를 즉시 바꿀 필요는 없다. 그러나 LangGraph Platform이나 LangGraph Cloud를 사용한다면 서버 SDK 버전도 v2를 지원하는지 확인한다. 로컬 SDK와 플랫폼 서버 SDK 버전이 다르면 StreamPart 구조가 달라질 수 있다.


채택 판단표

DeltaChannel — 우선 채택 조건
채널이 누적 증가하고 500 스텝 이상 실행되는 에이전트에 먼저 적용한다.
checkpoint store 비용이나 직렬화 latency가 실제 병목인 경우 before/after를 측정 후 채택한다.
reducer가 pure function인지 먼저 확인한다. timestamp·random 포함 reducer는 replay 불일치를 일으킨다.
snapshot_frequency 설정 전 restore latency 요구사항과 store row 수 상한을 정한다.
TimeoutPolicy — 우선 채택 조건
외부 API·LLM 호출·file I/O가 있는 모든 async 노드에 기본값으로 설정한다.
idle_timeout으로 hung call을 잡고, run_timeout으로 전체 실행 상한을 둔다.
sync 노드는 반드시 async로 변환해야 한다. event loop 안에서 blocking 코드를 executor로 감싸야 한다.
RetryPolicy와 조합 시 총 wall-clock 예산(run_timeout × max_attempts)을 계산한다.
Streaming v2 — 우선 채택 조건
신규 consumer를 작성할 때는 v2를 기본으로 사용한다. TypedDict import로 IDE 자동완성을 활용한다.
기존 v1 consumer는 깨지지 않으면 마이그레이션을 서두르지 않는다.
LangGraph Platform·Cloud를 쓴다면 서버 SDK 버전이 v2를 지원하는지 먼저 확인한다.
공통 rollback 기준
DeltaChannel restore 불일치나 state 손상이 발생하면 해당 채널을 BinaryOperatorAggregate로 되돌린다.
TimeoutPolicy가 정상 응답을 차단한다면 idle_timeout을 올리거나 heartbeat 모드로 전환한다.
beta API 변경으로 동작이 바뀌면 SDK 버전을 이전 lockfile로 복귀한다.
LangGraph 1.2 기능별 채택 기준

정리

LangGraph 1.2의 핵심 변화는 세 축으로 정리된다.

DeltaChannel: 장기 실행 에이전트의 checkpoint 크기 폭증을 구조적으로 해결한다. 500 턴 기준 219 MB → 3 KB는 저장 비용, 직렬화 시간, restore 지연 모두에 영향을 준다. snapshot_frequency로 공간과 지연의 트레이드오프를 조정한다. reducer가 pure function이어야 한다는 제약을 먼저 확인한다.

TimeoutPolicy: async 노드에 run_timeout(하드 wall-clock)과 idle_timeout(진행 신호 기반)을 붙여 hung call과 무한 대기를 차단한다. NodeTimeoutError는 retryable이므로 RetryPolicy와 조합해 장애 복구 루프를 설계할 수 있다.

Streaming v2: 모든 청크를 StreamPart(type, ns, data)로 통일해 타입 안전한 스트림 소비를 가능하게 한다. opt-in이므로 기존 코드는 즉시 바꿀 필요 없다.

세 기능 모두 1.2.0에서 beta 표시가 있다. 운영 환경 적용 전 restore 일관성, timeout-retry 비용 조합, Platform SDK 호환성을 먼저 검증한다.

References