LangGraph 런타임(Pregel) 이해하기

LangGraph 런타임(Pregel) 이해하기

Pregel은 LangGraph의 런타임을 구현해서 LangGraph 애플리케이션의 실행을 관리해요. StateGraph를 컴파일하거나 @entrypoint를 만들면 입력으로 호출할 수 있는 Pregel 인스턴스가 생성돼요. 이 가이드는 런타임을 높은 수준에서 설명하고, Pregel로 애플리케이션을 직접 구현하는 방법도 알려드려요.

참고: Pregel 런타임은 그래프를 이용한 대규모 병렬 계산의 효율적 방법을 설명한 Google의 Pregel 알고리즘에서 이름을 따왔어요.

출처: 공식문서

개요 (Overview)

LangGraph에서 Pregel은 actorschannels을 하나의 애플리케이션으로 결합해요. Actors는 channels에서 데이터를 읽고 channels에 데이터를 써요. Pregel은 애플리케이션 실행을 여러 스텝으로 조직하며, Pregel 알고리즘/Bulk Synchronous Parallel 모델을 따르죠. 각 스텝은 세 단계로 이뤄져요.

  • Plan: 이 스텝에서 실행할 actors를 정합니다. 예를 들어 첫 스텝에선 특수 input channels에 구독하는 actors를 고르고, 이후 스텝에선 이전 스텝에서 갱신된 channels에 구독하는 actors를 골라요.
  • Execution: 선택된 모든 actors를 병렬로 실행합니다. 전부 완료되거나, 하나가 실패하거나, 타임아웃에 도달할 때까지요. 이 단계 동안 channel 갱신은 다음 스텝까지 actors에 보이지 않아요.
  • Update: 이 스텝의 actors가 쓴 값으로 channels를 갱신합니다.

실행할 actors가 없어지거나 최대 스텝 수에 도달할 때까지 반복해요.

Actors

actorPregelNode예요. channels에 구독하고, 거기서 데이터를 읽고, 거기에 데이터를 써요. Pregel 알고리즘의 actor로 생각할 수 있죠. PregelNodes는 LangChain의 Runnable 인터페이스를 구현해요.

Channels

Channels은 actors(PregelNodes) 사이의 통신에 쓰여요. 각 channel은 값 타입(value type), 갱신 타입(update type), 그리고 갱신 함수(update function)를 갖는데, 갱신 함수는 일련의 갱신을 받아 저장된 값을 수정해요. 채널은 한 체인에서 다른 체인으로 데이터를 보내거나, 한 체인이 미래 스텝의 자기 자신에게 데이터를 보내는 데 쓸 수 있어요.

LastValue

LastValue는 기본 channel 타입이에요. 마지막으로 쓰인 값을 저장하고 이전 값을 덮어써요. 입력·출력 값이나 한 스텝에서 다음 스텝으로 데이터를 넘길 때 사용해요.

Topic

Topic은 구성 가능한 PubSub channel로, actors 간 여러 값을 보내거나 스텝에 걸쳐 출력을 누적하는 데 유용해요. 값을 중복 제거하거나 실행 중 쓰인 모든 값을 누적하도록 구성할 수 있어요.

BinaryOperatorAggregate

BinaryOperatorAggregate는 현재 값과 각 새 갱신에 이진 연산자를 적용해 갱신되는 영속 값을 저장해요. 스텝에 걸쳐 실행 누적 집계(running aggregates)를 계산할 때 사용해요.

DeltaChannel

DeltaChannel는 완전히 누적된 값 대신 각 스텝의 증분 델타만 저장해요. 자주 쓰이고 시간이 지나며 큰 값을 누적하는 채널에 가장 유용하죠. 예를 들어 긴 스레드의 대화 메시지 목록이 그렇습니다. 델타 저장이 없으면 전체 목록이 매 체크포인트에 다시 직렬화되는데, DeltaChannel을 쓰면 각 스텝에서 쓰인 새 메시지만 저장돼요. DeltaChannel은 일반 reducer를 쓰는 것처럼 Annotated 타입 어노테이션에 사용하면 됩니다.

Bulk reducer 요구사항

DeltaChannel에 넘기는 reducerbulk reducer예요. pairwise가 아니라 단일 호출에서 현재 상태와 이번 스텝의 모든 쓰기 시퀀스를 받죠. 이는 StateGraphAnnotated에서 쓰는 키별 reducer(갱신마다 한 번씩 호출)와 다르죠. 가장 흔한 두 경우의 bulk reducer는 다음과 같아요.

둘 다 결합적(associative)이어서, 배치를 하나씩 적용한 결과와 함께 적용한 결과가 같아요.

읽기 지연을 제한하는 snapshot_frequency

스냅샷 없이는 DeltaChannel 값을 읽으려면 전체 쓰기 이력을 다시 재생해야 해요 — N 스텝 스레드에서 O(N)이죠. snapshot_frequency=K로 설정하면 매 K개의 pregel 스텝마다 전체 스냅샷을 써서 읽기 깊이를 최대 K 스텝으로 제한해요.

snapshot_frequency 값이 높을수록 저장 오버헤드는 줄지만 읽기 지연은 늘어나요. 값이 낮을수록 지연을 더 촘촘히 제한하되 체크포인트가 커지죠. 기본값인 None은 스냅샷을 완전히 건너뛰는데, 읽기가 드물거나 스레드가 짧을 때 적합해요.

버전 호환성과 롤백

예시 (Examples)

대부분의 사용자는 StateGraph API나 @entrypoint 데코레이터로 Pregel과 상호작용하지만, Pregel과 직접 상호작용하는 것도 가능해요. 아래는 Pregel API의 감을 잡게 해주는 몇 가지 예시예요.

  • Single node
  • Multiple nodes
  • Topic
  • BinaryOperatorAggregate
  • Cycle

이 예시는 BinaryOperatorAggregate 채널로 reducer를 구현하는 방법을 보여줘요.

이 예시는 체인이 구독하는 채널에 쓰게 해서 그래프에 사이클(cycle)을 도입하는 방법을 보여줘요. 채널에 None 값이 쓰일 때까지 실행이 계속돼요.

고수준 API (High-level API)

LangGraph는 Pregel 애플리케이션을 만드는 두 가지 고수준 API를 제공해요. StateGraph (Graph API)Functional API예요.

  • StateGraph (Graph API)
  • Functional API

StateGraph (Graph API)는 Pregel 애플리케이션 생성을 단순화하는 더 높은 수준의 추상화예요. 노드·엣지의 그래프를 정의할 수 있게 해주죠. 그래프를 컴파일하면 StateGraph API가 자동으로 Pregel 애플리케이션을 만들어줘요.

컴파일된 Pregel 인스턴스는 노드와 채널 목록과 연결돼요. 이 노드·채널을 출력해서 확인할 수 있어요.

다음과 같은 결과가 보일 거예요.

Functional API에서는 @entrypoint로 Pregel 애플리케이션을 만들 수 있어요. entrypoint 데코레이터는 입력을 받아 출력을 반환하는 함수를 정의할 수 있게 해줘요.

더 알아보기 (Learn more)