
에이전트 루프(Agent Loop)에서 내구적 실행(Durable Execution)으로: 프로덕션 에이전트를 위한 아키텍처 가이드
요약
프로덕션 환경에서 에이전트 프레임워크와 오케스트레이션 엔진의 차이점을 분석합니다. 에이전트 루프, 프레임워크, 인프라 계층의 역할을 구분하여 내구적 실행(Durable Execution)의 필요성을 설명합니다.
핵심 포인트
- 에이전트 루프는 애플리케이션 레벨의 추론 로직을 담당함
- 에이전트 프레임워크는 빠른 배포를 위한 엔지니어링 스캐폴딩을 제공함
- 오케스트레이션 엔진은 상태 외부화를 통해 크래시 복구와 긴 대기 시간을 관리함
- 프로덕션 에이전트 구축 시 인프라 계층의 내구적 실행이 필수적임
Claude SDK, LangGraph, Strands와 같은 에이전트 프레임워크(Agent frameworks)는 이미 우리에게 많은 것을 제공하고 있습니다. 에이전트 루프(Agent Loop), 에러 핸들링(error handling), 태스크 디스패치(task dispatch), 메모리 관리(memory management), 그리고 에이전트를 빠르게 프로덕션 환경에 배포하기 위한 엔지니어링 스캐폴딩(engineering scaffolding) 등이 그것입니다.
그래서 제가 프로덕션 아키텍처에서 Temporal이나 Lambda Durable Functions와 같은 오케스트레이션 엔진(orchestration engines)을 처음 접했을 때, 제 반응은 이랬습니다: 왜 또 다른 레이어가 필요한 거지?
여러 구현 사례를 조사하고 소스 코드를 읽어본 후, 저는 이러한 혼란이 좀처럼 함께 다뤄지지 않는 세 가지 질문에서 비롯된다는 것을 깨달았습니다:
- 프레임워크가 이미 에이전트 루프(Agent Loop)와 기본적인 프로덕션 요구사항을 처리하고 있는데, 왜 그 위에 오케스트레이션(orchestration)을 추가해야 하는가?
- 오케스트레이션이 실행을 관리하게 되면, 그 결과물은 여전히 "에이전트(Agent)"인가, 아니면 단순한 "워크플로(Workflow)"인가?
- 오케스트레이션 엔진은 실제로 무엇을 하는가 — 프레임워크가 해결하지 못하는 어떤 문제를 해결하는가?
이 포스트는 이 문제들을 풀어내려는 저의 시도입니다. 1장은 질문 1과 2를 다루며, 2장은 구체적인 시나리오를 통해 질문 3을 다룹니다.
제1장: 에이전트 루프(Agent Loop)와 에이전트 오케스트레이션(Agent Orchestration)
1.1 세 가지 개념: 에이전트 루프(Agent Loop), 에이전트 프레임워크(Agent Framework), 그리고 오케스트레이션 엔진(Orchestration Engine)
질문 1에 답하기 위해서는 먼저 자주 혼동되는 세 가지 개념을 구분해야 합니다:
에이전트 루프 (Agent Loop) — 애플리케이션 레벨의 추론 로직(reasoning logic). 에이전트가 도구를 선택하고, 컨텍스트(context)를 관리하며, 계획을 세울지 실행할지 결정하고, 언제 멈출지를 결정하는 방식입니다. 이것이 에이전트를 에이전트답게 만드는 요소입니다.
에이전트 프레임워크 (Agent Framework) (Claude SDK, LangGraph, Strands) — 애플리케이션 레벨의 엔지니어링 레이어(engineering layer). 에이전트 루프를 에러 핸들링(error handling), 메모리 인제스션(memory ingestion), 도구 등록(tool registration), 로깅(logging), 트레이싱(tracing), 멀티 에이전트 태스크 디스패치(multi-agent task dispatch)와 같은 프로덕션 준비가 된 스캐폴딩(scaffolding)으로 감싸줍니다. 이것의 가치는 가속화(acceleration)에 있습니다. 즉, 에이전트 루프를 빠르게 배포 가능한 무언가로 바꾸어 주는 것입니다.
Orchestration Engine (Temporal, Lambda Durable Functions) — 인프라 계층 (infrastructure layer). 실행 상태 (execution state)를 외부화하여 프로덕션 규모에서 발생하는 문제들을 해결합니다: 긴 대기 시간 (인간의 승인을 위한 며칠간의 대기), 프로세스 경계(process boundaries)를 넘나드는 크래시 복구 (crash recovery), 에이전트 간의 분산 조정 (distributed coordination), 그리고 세밀한 재시도 정책 (fine-grained retry policies).
┌─────────────────────────────────────────────────────────┐
│ Application Logic: Agent Loop │
│ "에이전트가 생각하고 행동하는 방식" │
│ │
...
프레임워크 (Frameworks)는 개발을 단순화하고 프로덕션 투입 시간을 단축시키며, 오케스트레이션 엔진 (orchestration engines)은 대규모 환경에서의 안정성과 신뢰성을 보장합니다. 이 둘은 경쟁 관계가 아니라 상호 보완적인 관계입니다.
왜 에이전트에게 특히 이 인프라 계층이 필요할까요? Temporal의 블로그에서는 다음과 같이 설명합니다:
"AI 애플리케이션과 에이전트는 분산 시스템 (distributed systems)입니다. 저는 심지어 이들이 **스테로이드를 맞은 분산 시스템 (distributed systems on steroids)**이라고 제안합니다. 왜냐하면 사용자의 경험을 충족시키기 위해 여러분의 앱이 결과적으로 10배(an order of magnitude) 더 많은 원격 요청을 보낼 수도 있기 때문입니다." — Temporal Blog
단일 에이전트 작업에는 10개 이상의 LLM 호출, 외부 API, 도구 실행 (tool executions), 그리고 며칠씩 걸리는 인간의 승인 대기 등이 포함될 수 있습니다. 이 모든 과정은 실패하거나, 타임아웃(timeout)이 발생하거나, 중단될 수 있습니다. 프레임워크 수준의 에러 핸들링 (error handling)은 기본적인 재시도를 다루지만, 프로세스 경계를 넘어 상태를 유지(persist state)하거나, 포드 퇴거 (pod evictions)에서 살아남거나, 며칠에 걸쳐 멀티 에이전트 워크플로 (multi-agent workflows)를 조정할 수는 없습니다. 이것이 바로 우리에게 오케스트레이션 엔진이 필요한 이유입니다. 이들은 프레임워크만으로는 메울 수 없는 신뢰성의 간극을 채워줍니다.
1.2 오케스트레이션이 에이전트의 자율성을 해치는가?
제가 오케스트레이션 엔진을 처음 접했을 때, 그것이 어떻게 작동하는지 완전히 이해하기 전의 즉각적인 걱정은 이것이었습니다: 오케스트레이션이 에이전트의 자율성 (autonomy)을 상실한다는 것을 의미하는가?
무엇이 에이전트를 에이전트로 만드는지 생각해 보십시오. PPT 생성 에이전트를 예로 들어보겠습니다. 당신은 에이전트에게 도구(슬라이드 생성, 이미지 검색, 텍스트 작성)와 목표("2분기 실적에 대한 발표 자료 만들기")를 부여합니다. 에이전트는 스스로의 계획을 자율적으로 결정합니다. 아마도 먼저 개요를 작성한 다음, 슬라이드별로 콘텐츠를 생성하며, 진행 과정에서 반복(iterating)하고 수정(revising)할 것입니다. 도구 선택, 실행 순서, 그리고 루프 횟수(loop count)는 모두 LLM에 의해 실시간으로 결정됩니다. 이러한 자율적인 의사결정(autonomous decision-making)이 바로 "에이전트"의 본질입니다.
이제 오케스트레이션 엔진(orchestration engine)을 도입해 봅시다. 그렇다면 에이전트의 의사결정이 미리 오케스트레이션되었다는 뜻일까요? 즉, "1단계: 개요 생성 → 2단계: 콘텐츠 작성 → 3단계: 파일 생성"과 같이 각 단계가 사전에 정의된 것을 의미할까요? 만약 그렇다면, 에이전트는 자율성을 상실한 것입니다. 우리가 가진 것은 더 이상 "에이전트"가 아니라 미리 정의된 "워크플로 (Workflow)"입니다. 오케스트레이션된 결과물은 여전히 에이전트일까요, 아니면 워크플로가 되어버린 것일까요?
여러 프로덕션 구현 사례를 연구하고 소스 코드를 정독한 끝에, 저는 세 가지 패턴을 발견했습니다. 저의 결론은 다음과 같습니다: 오케스트레이션이 에이전트의 자율성을 죽일 필요는 없습니다. 세 가지 패턴 모두에서 에이전트는 완전한 의사결정 능력을 유지할 수 있습니다.
패턴 A — 외부 오케스트레이션 (블랙박스로서의 에이전트 루프 (Agent Loop as Black Box))
이것은 가장 직관적인 패턴입니다. 오케스트레이션 계층(orchestration layer)은 각 에이전트 호출을 하나의 불투명한(opaque) 단계로 취급합니다. 오케스트레이션 계층은 에이전트들 '사이의' 흐름을 관리하지만, 내부를 들여다보지는 않습니다.
오케스트레이션 계층 (Temporal / Lambda DF)
1단계: 예산 에이전트 호출
[에이전트 루프 (블랙박스): LLM → 도구 → LLM → 완료]
...
각 단계는 내부적으로 완전한 에이전트 루프 (Agent Loop)를 실행합니다. 즉, 에이전트가 어떤 도구 (tools)를 호출할지, 몇 번의 반복 (iterations)을 수행할지, 언제 멈출지를 결정합니다. 오케스트레이션 레이어 (orchestration layer)는 오직 "예산 에이전트 시작 → 예산 에이전트 종료"만을 확인합니다. 오케스트레이션 레이어는 순서 지정 (sequencing), 실패 시 재시도 (retry), 그리고 단계 사이의 인간 참여 (human-in-the-loop, HITL) 대기 상태를 관리합니다.
오케스트레이션이 추론 루프 (reasoning loop)의 완전히 외부에서 작동하기 때문에 에이전트의 자율성 (autonomy)은 온전히 보존됩니다. 이에 따른 트레이드오프 (trade-off)는 관찰 가능성 (observability)입니다. 즉, 관찰의 입도가 거칠어집니다 (coarse-grained). "에이전트-1 시작 → 5.1초 → 성공"은 볼 수 있지만, 그 5.1초 동안 내부에서 어떤 일이 일어났는지는 볼 수 없습니다.
이 패턴에는 구체적인 공개 사례가 있습니다. AWS APN Blog에서는 AgentCore와 Temporal을 사용하여 구축된 멀티 에이전트 금융 어드바이저를 게시했습니다. 원본 소스 코드에서 아키텍처는 다음과 같습니다. 오케스트레이터 에이전트 (Orchestrator Agent)가 전문 에이전트들(예산 에이전트, 금융 분석 에이전트)에게 작업을 할당하며, 각 에이전트는 자신만의 완전한 에이전트 루프 (Agent Loop)를 실행합니다.
그 후 블로그 작성자는 이를 Temporal로 오케스트레이션되는 버전으로 리팩토링(refactored)했습니다. 여기서는 각 에이전트 호출이 하나의 Temporal 액티비티 (Activity)가 되며, 전체 에이전트 루프 (Agent Loop)가 하나의 내구적 단계 (durable step)로 래핑(wrapped)됩니다.
이것이 정확히 패턴 A (Pattern A)입니다. 오케스트레이션이 에이전트 간의 흐름(순서 지정, 재시도, HITL 대기)을 관리하는 동안, 각 에이전트는 내부적으로 완전한 자율적 의사결정 능력을 유지합니다.
패턴 B — 내부 오케스트레이션 (모든 단계가 가시적인 방식)
패턴 B는 에이전트 루프(Agent Loop)를 오케스트레이션 엔진(orchestration engine)과 결합합니다. 즉, 모든 LLM 추론(inference)과 모든 도구 호출(tool call)이 독립적인 오케스트레이션 단계로 래핑(wrapped)됩니다.
┌─ 오케스트레이션 계층 (Orchestration Layer) ──────────────────────────┐
│ 단계 1: LLM 추론 (inference) → "calculate_budget 호출" 반환 │
│ 단계 2: calculate_budget 실행 → {income: 6000} │
...
이를 보면 즉시 미리 정의된 워크플로(workflow)처럼 느껴집니다. 단계 1, 단계 2, 단계 3...과 같이 고정된 순서로 진행되는 것처럼 말이죠. 에이전트가 단순히 순차적인 파이프라인(pipeline)으로 축소된 것 아닌가요? 이것은 그냥 워크플로(Workflow) 아닌가요?
정답은 '아니오'입니다. 왜냐하면 패턴 B는 완전히 동적(dynamic)일 수 있기 때문입니다. 실제 코드로 구현하면 다음과 같습니다.
// 동적 패턴 B (Lambda Durable Functions)
export const handler = withDurableExecution(
async (event, context) => {
...
while(true) 루프와 동적으로 생성되는 단계 이름은 다음을 의미합니다: LLM이 완전한 자율성(autonomy)을 유지한다는 것입니다. LLM은 다음에 어떤 도구를 호출할지, 몇 번의 반복(iteration)을 수행할지, 그리고 언제 멈출지를 스스로 결정합니다. 아무것도 미리 정의되어 있지 않습니다.
패턴 B가 실제로 하는 일은 에이전트 루프(Agent Loop)를 프레임워크 내부의 블랙박스(black-box)에서 오케스트레이션 API로 작성된 명시적인 루프로 옮기는 것입니다. 동작(behavior) 측면에서는 프레임워크가 관리하는 에이전트 루프와 동일합니다. _인프라 측면의 이점(infrastructure benefit)_은 모든 단계가 체크포인트(checkpointed)된다는 것입니다. 만약 단계 3 이후에 프로세스가 충돌(crash)하더라도, 처음부터 다시 시작하는 대신 단계 4부터 재개됩니다.
이것은 여전히 에이전트입니다. 단지 내구적(durable)인 에이전트일 뿐입니다.
공식 예시:
Temporal AI Cookbook (Claude를 사용한 에이전틱 루프(Agentic Loop));
커뮤니티 프로젝트 temporal-ai-agent (별 545개).
패턴 3 — 하이브리드 (복합형, Hybrid/Composite)
프로덕션 시스템은 종종 동일한 워크플로 내에서 두 패턴을 혼합하여 사용합니다: 전문 서브 에이전트(sub-agents)를 위한 블랙박스 에이전트 호출(패턴 A) + 핵심 운영을 위한 세밀한 단계(패턴 B) + 인간 개입(HITL, Human-in-the-loop) 대기. 이것이 바로 "워크플로로 오케스트레이션되는 에이전트(Workflow-orchestrated Agent)" 패턴입니다.
Temporal 워크플로 (복합 패턴, Composite Pattern)
┌─ 단계 1: call_llm() — 초기 분석 ──────────────────────────┐
...
비교 및 결정 가이드
세 가지 패턴을 모두 살펴보았으므로, 주요 차원(dimensions)에 따른 비교 결과는 다음과 같습니다:
| 차원 | 패턴 A (External) | 패턴 B (Internal) | 하이브리드 (Hybrid) |
|---|---|---|---|
| 에이전트 자율성 (Agent autonomy) | ✔ 완전히 보존됨 | ✔ 보존됨 (동적) | ✔ 자율성 + 제어된 체크포인트 |
| ... | |||
| 표 1: 주요 차원에 따른 패턴 비교 |
실제 적용 시에는 특정 시나리오에 따라 선택이 달라지는 경우가 많습니다. 다음 표는 어떤 상황에 어떤 패턴이 적합한지 요약한 것입니다:
| 시나리오 | 권장 패턴 | 이유 |
|---|---|---|
| 멀티 에이전트 분산 협업 (Multi-agent distributed collaboration) | 패턴 A | 에이전트들이 독립적으로 배포되며, 오케스트레이션(orchestration)이 조율을 담당함 |
| ... | ||
| 표 2: 시나리오별 패턴 선택 가이드 |
물론 실제 시스템이 하나의 범주에 딱 들어맞는 경우는 드뭅니다. 많은 프로덕션 배포 환경은 진화합니다. 처음에는 단순함을 위해 패턴 A로 시작했다가, 컴플라이언스(compliance)나 신뢰성 요구사항이 커짐에 따라 점진적으로 중요한 단계들을 패턴 B로 감싸는(wrapping) 방식을 취합니다. 적절한 패턴은 팀의 숙련도, 규제 환경, 그리고 장애가 발생했을 때 실제로 타격이 큰 지점이 어디인지에 따라 달라집니다.
제2장: 오케스트레이션 엔진의 핵심 메커니즘
제1장에서는 왜 오케스트레이션이 필요한지, 그리고 자율성을 해치지 않으면서 어떻게 에이전트와 통합되는지를 살펴보았습니다. 그렇다면 오케스트레이션 엔진은 인프라 수준에서 실제로 무엇을 할까요? 이 장에서는 질문 3에 대한 답을 다룹니다.
단계(Step) → 체크포인트(Checkpoint) → 재생(Replay)
어떤 패턴을 선택하든, 모든 오케스트레이션 엔진은 동일한 근본적인 메커니즘을 통해 문제를 해결합니다: 각 단계의 결과를 프로세스 외부의 저장소에 기록하고, 중단 시 재실행하는 대신 기록된 내용으로부터 재개(resume)하는 것입니다.
① 단계 (Step, 주요 작업 표시) — 개발자는 context.step() 또는 액티비티(Activity)를 사용하여 각 LLM 호출, 각 도구 실행(tool execution), 각 외부 API 요청과 같은 "의미 있는 작업"을 표시합니다. 이는 엔진에 "이 단계의 결과는 기억할 가치가 있다"라고 알려주는 역할을 합니다.
② 체크포인트 (Checkpoint, 자동 지속성) — 각 단계가 완료된 후, 엔진은 프로세스 외부의 지속성 저장소(Persistent Storage)에 결과를 자동으로 기록합니다 (Temporal: 이벤트 히스토리(Event History); Lambda DF: 체크포인트 스토어(Checkpoint Store)). 개발자는 체크포인트 코드를 작성할 필요가 전혀 없으며, 엔진이 인프라 수준에서 이를 처리합니다.
③ 리플레이 (Replay, 결정론적 복구) — 중단(충돌/일시 중지/타임아웃) 발생 후: 엔진은 코드를 리플레이(Replay)합니다. 이때 이미 완료된 단계에 대해서는 재실행하지 않고 캐시된 결과를 반환하며, 완료된 모든 단계를 건너뛰고 첫 번째로 완료되지 않은 단계부터 계속 진행합니다. 부수 효과(Side effects)는 반복되지 않습니다. 추가적인 토큰 소모도 없습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
