본문으로 건너뛰기

© 2026 Molayo

LangChain헤드라인2026. 05. 21. 03:44

LangGraph 구축하기: 제1원칙에 기반한 에이전트 런타임 (Agent Runtime) 설계

요약

LangGraph는 프로덕션 환경에서 신뢰할 수 있는 AI 에이전트를 구축하기 위해 설계된 저수준(low-level) 프레임워크입니다. 기존 LangChain의 한계를 극복하기 위해 높은 추상화 대신 제어력과 내구성에 집중하며, 긴 지연 시간과 비결정론적 특성을 관리하는 데 최적화되어 있습니다.

핵심 포인트

  • LangGraph는 높은 추상화보다 제어력(control)과 내구성(durability)을 우선시하는 설계 원칙을 가집니다.
  • 프로덕션 환경에서의 확장성과 커스터마이징을 위해 저수준 프레임워크로 설계되었습니다.
  • 에이전트 구축의 핵심 도전 과제로 긴 지연 시간, 재시도 비용, 비결정론적 특성을 정의합니다.
  • LinkedIn, Uber, Klarna 등 주요 기업들이 프로덕션 준비가 된 에이전트 구축을 위해 사용하고 있습니다.

Nuno Campos 작성

요약: 우리는 약 2년 전 저수준 (low level) 에이전트 프레임워크로서 LangGraph를 출시했으며, 이미 LinkedIn, Uber, Klarna와 같은 기업들이 이를 사용하여 프로덕션 준비가 된 (production ready) 에이전트를 구축하는 것을 목격했습니다. LangGraph는 매우 인기 있는 LangChain 프레임워크로부터 얻은 피드백을 바탕으로 구축되었으며, 프로덕션 환경에서 에이전트 프레임워크가 어떻게 작동해야 하는지를 재고합니다. 우리는 AI 에이전트를 위한 적절한 추상화 (abstraction)를 찾는 것을 목표로 했고, 그것이 추상화를 거의 하지 않는 것이라는 결론을 내렸습니다. 대신, 우리는 제어력 (control)과 내구성 (durability)에 집중했습니다. 이 포스트에서는 신뢰할 수 있는 에이전트를 구축하며 배운 점을 바탕으로 LangGraph를 설계한 우리의 설계 원칙과 접근 방식을 공유합니다.

LangGraph ALPHA

방금 LangGraph 1.0의 알파 버전을 출시했습니다!

우리는 2년 전 LangChain의 매우 인기 있는 체인 (chains)과 에이전트 (agents)를 재부팅하는 것으로 LangGraph를 시작했습니다. 우리는 처음 langchain 오픈 소스 라이브러리가 출시된 이후 받은 모든 피드백(수많은 GitHub 이슈, 토론, Discord, Slack 및 Twitter 게시물)을 처리하기 위해 새롭게 시작하는 것이 가장 많은 여유를 줄 것이라고 결정했습니다. 우리가 들은 주요 내용은 langchain이 시작하기는 쉽지만, 커스터마이징 (customize)하고 확장 (scale)하기는 어렵다는 것이었습니다.

이번에 우리의 최우선 목표는 LangGraph를 여러분이 프로덕션에서 에이전트를 실행할 때 사용할 수 있는 도구로 만드는 것이었습니다. 트레이드오프 (tradeoffs)를 해야 할 때, 우리는 사람들이 시작하기 얼마나 쉬운가보다 프로덕션 준비성 (production-readiness)을 우선시했습니다.

이 포스트에서 우리는 LangGraph의 범위를 정하고 설계하는 과정을 공유할 것입니다.

  • 첫째: 전통적인 소프트웨어와 비교했을 때 에이전트를 구축하는 것이 무엇이 다른지 다룹니다.
  • 다음: 이러한 차이점을 어떻게 필수 기능으로 전환했는지 논의합니다.
  • 마지막으로: 이러한 요구 사항에 맞춰 우리의 프레임워크를 어떻게 설계하고 테스트했는지 보여줍니다.

그 결과, 에이전트의 규모와 처리량 (throughput) 모두에 따라 확장 가능한 저수준의 프로덕션 준비가 된 에이전트 프레임워크가 탄생했습니다.

1. 에이전트에게 필요한 것은 무엇인가?

우리가 던진 첫 두 가지 질문은 "우리가 실제로 LangGraph를 구축해야 하는가?" 그리고 "왜 기존 프레임워크를 사용하여 에이전트를 프로덕션(production) 환경에 배포할 수 없는가?"였습니다. 이 질문들에 답하기 위해, 우리는 무엇이 에이전트를 이전의 소프트웨어와 다르게(또는 유사하게) 만드는지 정의해야 했습니다. 우리가 직접 수많은 에이전트를 구축하고 Uber, LinkedIn, Klarna, Elastic과 같은 팀들과 협업하면서, 우리는 이를 세 가지 핵심적인 차이점으로 압축했습니다.

  • 지연 시간(latency)이 길어질수록 사용자의 참여를 유지하기가 더 어려워짐
  • 실행 시간이 긴 작업이 실패했을 때 재시도(retrying)하는 것은 비용이 많이 들고 시간이 오래 걸림
  • AI의 비결정론적(non-deterministic) 특성으로 인해 체크포인트(checkpoints), 승인(approvals), 그리고 테스트가 필요함

지연 시간(latency) 관리

LLM 기반 에이전트의 첫 번째 정의적 특성이자 도전 과제는 **지연 시간(latency)**입니다. 과거에는 백엔드 엔드포인트의 지연 시간을 밀리초(milliseconds) 단위로 측정했습니다. 이제 우리는 에이전트의 실행 시간(run times)을 초, 분, 또는 곧 시간 단위로 측정해야 합니다.

이는 LLM 자체가 느릴 뿐만 아니라, 테스트 시간 연산(test-time compute)과 함께 점점 더 느려지고 있기 때문입니다. 또한, 루핑 에이전트(looping agents)를 통해 원하는 결과를 얻기 위해 여러 번의 LLM 호출이 필요할 때가 많고, 이전 출력을 수정하기 위해 LLM 프롬프트(prompts)를 체이닝(chaining)해야 하기 때문이기도 합니다. 그리고 우리는 보통 LLM 호출 전후에 LLM이 아닌 단계들을 추가해야 합니다. 예를 들어, 데이터베이스 행(rows)을 컨텍스트(context)에 가져오거나, LLM 호출의 정확성을 확인하기 위한 가드레일(guardrails) 및 검증기(verifiers)를 생성해야 할 수도 있습니다.

이 모든 지연 시간은 새로운 것들을 구축할 수 있게 해주지만, 그럼에도 불구하고 최종 사용자의 참여를 계속 유지해야 합니다. 따라서 우리는 에이전트를 구축할 때 유용하게 쓰일 두 가지 기능을 식별했습니다:

병렬화 (Parallelization). 에이전트의 단계가 여러 개일 때, 다음 단계가 이전 단계의 출력을 필요로 하지 않는다면 이들을 병렬로 실행할 수 있습니다. 하지만 이를 프로덕션 환경에서 안정적으로 수행하려면 병렬 단계 간의 데이터 경합 (Data Race)을 피해야 합니다.

스트리밍 (Streaming). 에이전트의 결과물을 저하시키지 않으면서 실제 지연 시간 (Latency)을 더 이상 줄일 수 없을 때는 인지된 지연 시간 (Perceived Latency)에 주목해야 합니다. 여기서 핵심적인 해결책은 에이전트가 실행되는 동안 사용자에게 유용한 정보를 보여주는 것입니다. 이는 진행 표시줄(Progress Bar)이나 에이전트가 수행한 주요 작업부터, LLM 메시지를 실시간으로 토큰 단위 (Token-by-token)로 최종 사용자에게 스트리밍하는 것까지 포함될 수 있습니다.

신뢰성 관리 (Managing reliability)

LLM 에이전트의 느린 속도는 다른 영향도 미칩니다. 우리 모두가 너무나 잘 알고 있듯이, 모든 소프트웨어는 필연적으로 버그가 발생합니다. 실행 시간이 긴 에이전트는 더 자주 실패하는데, 실행 시간이 길어질수록 무언가 잘못될 기회가 더 많아지기 때문입니다.

전통적인 소프트웨어에 버그가 발생하면 보통 재시도 (Retry)를 원하게 됩니다. 하지만 AI 에이전트의 경우는 어떨까요? 그것이 최선의 접근 방식이 아닐 수도 있습니다. 만약 에이전트가 10분 중 9분째에 실패한다면, 처음부터 다시 시작하는 것은 매우 시간이 많이 걸리고 비용도 많이 듭니다.

따라서 우리는 목록에 두 가지 기능을 더 추가해야 한다는 것을 알았습니다.

태스크 큐 (Task queue). 큐 (Queue)는 에이전트의 실행을 이를 트리거한 요청으로부터 분리함으로써 흔한 실패 원인 중 하나를 제거합니다. 큐는 필요할 때 에이전트를 안정적이고 공정하게 재시도할 수 있는 기본 요소 (Primitives)를 제공합니다.

체크포인팅 (Checkpointing). 이는 중간 단계에서 계산 상태 (Computation State)의 스냅샷을 저장하며, 실패했을 때 재시도하는 비용을 훨씬 저렴하게 만들어 줍니다.

비결정론적 LLM 관리 (Managing non-deterministic LLMs)

다음으로, LLM의 비결정론적 (Non-deterministic) 특성은 두 가지 과제를 더 만들어냅니다. 전통적인 소프트웨어를 작성할 때는 코드가 무엇을 해야 하는지, 그리고 의도한 대로 구축했을 때 어떤 결과가 나와야 하는지를 최소한 알고 있습니다. 생성형 AI (Generative AI)는 분명히 이 점을 변화시킵니다.

LLM (Large Language Models)의 경우, 입력과 출력 모두가 개방형(open-ended)입니다. ChatGPT를 사용했을 때, 어제 사용했던 것과 동일한 프롬프트(prompt)를 입력했음에도 다른 결과가 나오거나, 질문을 다양한 방식으로 던져도 유사한 결과를 얻을 수 있는 상황을 상상해 보십시오.

이 점은 LLM 에이전트(agent)를 기존의 다른 소프트웨어와 비교했을 때 매우 강력하게 만드는 핵심 요소이지만, 동시에 이를 프로덕션(production) 환경으로 가져갈 때 여러 도전 과제를 안겨줍니다.

개발 과정에서 수행하는 테스트로는 사용자들이 에이전트를 사용하는 수많은 놀라운 방식들을 거의 확실히 놓치게 될 것입니다. 사용자가 에이전트와 상호작용하는 모든 방식이나 LLM이 어떻게 응답할지를 진정으로 모두 계획할 수는 없습니다. 따라서 프로덕션 단계로 넘어갈 때는 다음 두 가지 기능이 매우 유용해집니다.

Human-in-the-loop (인간 참여형 루프). 그때까지 수행한 작업을 다시 할 필요 없이, 어느 시점에서든 에이전트를 중단하고 재개할 수 있는 도구를 갖추는 것은 AI 에이전트를 위한 많은 필수적인 UX 패턴을 가능하게 합니다. 예를 들어, 동작을 승인하거나 거부하고, 다음 동작을 편집하거나, 명확한 질문을 던지거나, 심지어 이전 단계로 타임 트래블(time travel)하여 작업을 다시 수행할 수 있습니다.

Tracing (트레이싱). 규모 있는 시스템을 구축하기 위해, 개발자는 에이전트 루프(agent loops)의 세부 사항 내부에서 어떤 일이 일어나고 있는지에 대한 명확한 가시성(visibility)이 필요합니다. 에이전트의 입력, 궤적(trajectories), 그리고 출력을 확인해야 합니다. 그렇지 않으면 사용자가 무엇을 요청하는지, 에이전트가 이를 어떻게 처리하는지, 그리고 사용자가 결과에 만족하는지를 알 수 없습니다.

개발자가 에이전트를 구축하기 위해 필요한 것

이것이 우리가 에이전트를 프로덕션으로 가져갈 때 대부분의 개발자에게 가장 필요한 6가지 기능의 후보 목록을 만든 방식입니다.

  • 병렬화 (Parallelization) – 실제 지연 시간 (latency)을 줄이기 위해
  • 스트리밍 (Streaming) – 체감 지연 시간을 줄이기 위해
  • 작업 큐 (Task queue) – 재시도 (retry) 횟수를 줄이기 위해
  • 체크포인팅 (Checkpointing) – 각 재시도 비용을 줄이기 위해
  • Human-in-the-loop – 사용자와 협업하기 위해
  • 트레이싱 (Tracing) – 사용자가 어떻게 사용하는지 학습하기 위해

만약 여러분이 구축 중인 에이전트가 이러한 기능 대부분을 필요로 하지 않는다면 (예: 도구가 없는 매우 짧은 에이전트이며 단일 프롬프트로 구성된 경우), LangGraph나 다른 프레임워크가 필요하지 않을 수도 있습니다.

이러한 각각의 기능들을 구축하는 것에 대해 고민하면서, 우리는 개발자들이 자신의 LLM 애플리케이션이 최종 사용자에게 체감될 정도로 느려지는 대가를 치르면서까지 그 모든 기능을 제공하는 프레임워크를 채택하지는 않을 것이라는 점도 깨달았습니다. 특히 챗봇 (Chatbot)으로 배포되는 에이전트의 경우 더욱 그렇습니다. 이로 인해 **낮은 지연 시간 (Low latency)**이 우리의 최종적인 최우선 요구 사항이 되었습니다.

다음으로, 우리는 이러한 기능들을 LangGraph에 어떻게 구축했는지 다룰 것입니다.

2. 왜 굳이 LangGraph를 구축하는가?

우리의 본질적인 질문으로 돌아가서, 새로운 것을 구축해야 할까요, 아니면 LLM 이전에 구축된 기존의 오픈 소스 프레임워크 중 하나를 채택해야 할까요? 우리가 선정한 기능 목록을 바탕으로 그 결정을 내리는 것은 꽤 쉬웠습니다.

왜 새로운 프레임워크가 필요했는가?

기존 프레임워크들은 주로 두 가지 범주로 나뉘어 있었습니다.

DAG 프레임워크 (Apache Airflow 등에 의해 대중화됨)

LLM 에이전트는 루핑 (Looping), 즉 LLM 에이전트의 계산 그래프 (Computation graph)가 순환적 (Cyclical)이라는 점으로부터 큰 이점을 얻기 때문에, 이름만 보고도 이들은 제외해야 했습니다. 따라서 DAG 알고리즘으로는 이를 처리할 수 없습니다.

내구 실행 엔진 (Durable execution engines, Temporal 등에 의해 대중화됨)

이러한 옵션들은 더 근접했지만, 결국 LLM 에이전트가 등장하기 전에 설계되었기 때문에 스트리밍 (Streaming)과 같은 특정 기능들이 부족했습니다. 또한, 이러한 엔진들은 단계 사이에 지연 시간 (Latency)을 유발하여 챗봇 개발자들이 이를 체감할 수 있었습니다. 마지막으로, 설계 특성상 히스토리에 단계가 많아질수록 성능이 저하되는데, 이는 LLM 에이전트가 더 길어지고 복잡해짐에 따라 좋지 않은 선택이 될 것으로 보였습니다.

결국 우리의 대답은 '그렇다'였습니다. LLM은 충분히 다르기 때문에, 이전의 프로덕션 인프라가 새로운 시대에 적합해지려면 새로운 아이디어가 주입되어야 했습니다. 그래서 우리는 LangGraph 구축에 착수했습니다.

3. 우리의 설계 철학

우리는 두 가지 핵심 원칙을 바탕으로 LangGraph를 설계했습니다.

우리는 AI의 미래가 어떻게 될지 모릅니다. 미래에 대해 가정을 적게 할수록 더 좋습니다. 1년, 2년, 3년 후 LLM(Large Language Models)을 사용하여 개발하는 것이 어떤 모습일지는 아무도 정말로 알 수 없습니다. 따라서 프레임워크 설계에 가정을 적게 포함할수록 미래에도 더 유효할 것입니다. 우리가 설계에 포함하고 싶었던 유일한 가정은 위에서 언급한 깨달음, 즉 LLM은 느리고, 불안정하며(flaky), 개방적(open-ended)이라는 점입니다.

코드를 작성하는 것과 같은 느낌을 주어야 합니다. 프레임워크의 공개 API(Public API)는 일반적인 프레임워크 없는 코드를 작성하는 것과 최대한 유사해야 합니다. 개발자의 코드에 부과하는 모든 요구사항은 정말로 높은 가치를 지닌 기능을 가능하게 함으로써 정당화되어야 합니다. 그렇지 않으면 프레임워크를 아예 건너뛰고 싶어 하는 유혹이 너무 강력해집니다. 모든 코드 프레임워크의 가장 큰 경쟁자는 언제나 '프레임워크를 사용하지 않는 것'입니다.

이러한 원칙들은 이후로도 계속 유지되어 온 몇 가지 핵심적인 설계 결정에 영향을 미쳤습니다.

첫째, 라이브러리의 런타임(Runtime)은 개발자 SDK와 독립적입니다. SDK는 개발자들이 에이전트(Agent)를 구축할 때 사용하는 공개 인터페이스(클래스, 함수, 메서드, 상수 등)입니다. 현재 우리는 두 가지를 제공하고 있습니다. 바로 StateGraph와 **명령형/함수형 API (imperative/functional API)**입니다. 런타임(우리는 이를 PregelLoop라고 부릅니다)은 앞서 나열된 각 기능을 구현하고, 각 에이전트 호출에 대한 계산 그래프(Computation Graph)를 계획하며, 이를 실행합니다. 이러한 설계 덕분에 우리는 개발자 API와 런타임을 독립적으로 발전시킬 수 있습니다. 예를 들어, SDK 측면에서는 명령형 SDK를 도입할 수 있었고, 공유 상태(shared state)가 없는 그래프 인터페이스였던 우리가 제공했던 최초의 SDK를 폐기(deprecate)할 수 있었습니다.

런타임(runtime) 측면에서, 이를 통해 우리는 지난 2년 동안 공개 API(public APIs)에 아무런 영향을 주지 않으면서도 많은 성능 개선을 구현할 수 있었으며, 런타임에 대한 더 급진적인 변화를 실험할 수 있었습니다. 이에 대한 자세한 내용은 분산 실행(distributed execution)을 다룰 때 나중에 설명하겠습니다. 둘째로, 우리는 6가지 기능 각각을 빌딩 블록(building blocks)으로 제공하여, 개발자가 자신의 에이전트(agent)에서 언제든 원하는 기능을 자유롭게 선택할 수 있도록 하고 싶었습니다. 예를 들어, Human-in-the-loop 시나리오를 위한 중단/재개(interrupt/resume) 기능은 사용자가 직접 필요로 하기 전까지는 방해가 되지 않습니다(이는 노드 중 하나에서 interrupt() 함수를 호출하는 것만큼이나 간단합니다). 결과적으로 LangGraph는 유행하는 고수준 추상화(high-level abstraction)에 모든 것을 걸라고 개발자들을 설득하려는 수많은 다른 프레임워크들 사이에서 독보적인 저수준(low-level) 프레임워크로 자리 잡았습니다. 우리는 이러한 프레임워크들이 나타났다 사라지는 것을 보아왔으며, LangGraph는 여전히 유효하게 남아 있습니다. 따라서 우리는 지금까지의 접근 방식에 만족하고 있습니다.

4. LangGraph 런타임 (The LangGraph runtime)

이 모든 점을 염두에 두고, LangGraph가 우리가 원했던 6가지 기능(다시 말씀드리지만, 이는 병렬화(parallelization), 스트리밍(streaming), 체크포인팅(checkpointing), Human-in-the-loop, 트레이싱(tracing), 그리고 작업 큐(task queue)입니다)을 어떻게 구현하는지 살펴보겠습니다.

이산적 단계(discrete steps)를 가진 구조화된 에이전트 (Structured agents with discrete steps)

AI 자동 생성 콘텐츠

본 콘텐츠는 LangChain Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0