Part 8. Agent 아키텍처: Planner/Executor, 상태 머신, 작업 큐
운영 가능한 에이전트는 체인이 아니라 상태 기반 시스템이다. Planner/Executor 분리, 큐, 가드레일, 복구 전략을 실무 관점으로 정리한다.
에이전트 데모는 비교적 쉽게 만든다. 사용자가 요청하면 모델이 계획을 세우고 도구를 호출해 결과를 반환하면 된다. 문제는 운영이다. 요청이 길어지고, 도구가 느려지고, 실패가 반복되면 단순 체인 구조는 빠르게 무너진다. 운영 가능한 에이전트는 "프롬프트 체인"이 아니라 "상태 기반 워크플로 시스템"으로 설계해야 한다.
문제 제기
실무에서 에이전트가 실패하는 전형적 패턴은 다음과 같다.
- Planner가 매 단계 재계획을 반복해 토큰 비용과 지연이 급증한다.
- 도구 호출 실패를 모델 재호출로만 처리해 원인 없는 반복 루프에 빠진다.
- 장기 작업에서 프로세스 재시작 시 상태가 유실되어 작업이 중복 실행된다.
- 동시 요청이 늘면 에이전트 실행이 API 워커를 점유해 전체 서비스 지연을 유발한다.
실전 예시 A: 온보딩 자동화 에이전트
신규 고객 계정 설정, 권한 부여, 초기 데이터 검증을 처리하는 에이전트에서 외부 API 속도가 느려지자 타임아웃이 늘어났다. 체인 구조로 동기 실행하던 요청이 워커를 오래 점유했고, 결국 일반 API까지 영향을 받았다.
실전 예시 B: 데이터 리포트 생성 에이전트
사용자 요청마다 Planner가 전체 계획을 다시 생성했다. 동일한 작업 템플릿인데도 매번 재계획해 비용이 불필요하게 높아졌고, 일부 단계는 중복 수행되어 결과 일관성이 깨졌다.
핵심 개념
운영 가능한 Agent 아키텍처는 최소 네 가지 요소를 분리한다.
- Planner: 목표를 단계(task)로 분해
- Executor: 각 단계를 정책 하에서 실행
- State Machine: 단계 전이와 복구 조건 관리
- Queue/Scheduler: 비동기 실행, 동시성 제어, 재시도 제어
| 컴포넌트 | 책임 | 실패 시 영향 | 설계 포인트 |
|---|---|---|---|
| Planner | 목표 -> 단계 계획 | 잘못된 계획 전파 | 계획 템플릿, 계획 검증 |
| Executor | 도구 호출/결과 수집 | 단계 실패, 루프 | 툴 권한, timeout, retry budget |
| State Store | 진행 상태 영속화 | 중복 실행/유실 | 멱등 키, checkpoint |
| Queue | 작업 분산/백프레셔 | 시스템 전체 지연 | 우선순위, 동시성 cap |
| Policy Guard | 위험 실행 차단 | 보안/품질 사고 | allow-list, 승인 플로우 |
실전 패턴
패턴 1: Planner/Executor 분리 + 계획 템플릿
Planner가 매번 자유 생성하면 품질 편차와 비용 변동이 커진다. 도메인별 템플릿을 두고, Planner는 템플릿 선택과 파라미터 바인딩에 집중시키는 방식이 안정적이다.
type PlanStep = {
id: string;
tool: "fetchProfile" | "validatePolicy" | "createTicket" | "notifyUser";
requiresApproval: boolean;
timeoutMs: number;
};
type Plan = {
planId: string;
templateId: "onboarding_v1" | "refund_v2";
steps: PlanStep[];
};
export function buildPlan(taskType: string): Plan {
if (taskType === "onboarding") {
return {
planId: crypto.randomUUID(),
templateId: "onboarding_v1",
steps: [
{ id: "1", tool: "fetchProfile", requiresApproval: false, timeoutMs: 1000 },
{ id: "2", tool: "validatePolicy", requiresApproval: false, timeoutMs: 800 },
{ id: "3", tool: "createTicket", requiresApproval: true, timeoutMs: 1200 },
{ id: "4", tool: "notifyUser", requiresApproval: false, timeoutMs: 600 },
],
};
}
throw new Error("unsupported task");
}
운영 포인트:
- 계획 템플릿 변경은 코드 리뷰/테스트 대상로 관리한다.
- free-form replan은 실패 복구 시 제한적으로만 허용한다.
- 단계별 timeout과 승인 요구사항을 계획에 명시한다.
패턴 2: 상태 저장 + 멱등 실행(Exactly-once가 아닌 At-least-once 안전화)
분산 환경에서 정확히 한 번 실행을 보장하려면 비용이 크다. 대신 "적어도 한 번 실행"을 전제로 멱등성을 설계해 안전하게 만든다.
type AgentState = {
jobId: string;
currentStepId: string | null;
completedSteps: string[];
retryCount: number;
idempotencyKey: string;
status: "PLANNED" | "EXECUTING" | "FAILED" | "COMPLETED";
};
export async function executeStep(state: AgentState, step: PlanStep) {
const dedup = await stateStore.findResult(state.idempotencyKey, step.id);
if (dedup) return dedup;
const result = await runTool(step.tool, { timeoutMs: step.timeoutMs });
await stateStore.saveResult(state.idempotencyKey, step.id, result);
await stateStore.markStepCompleted(state.jobId, step.id);
return result;
}
운영 포인트:
- step 단위 체크포인트를 저장해 재시작 시 이어서 처리한다.
- 멱등 키는
jobId + stepId + tenantId로 구성한다. - 실패 재처리 시 completed step 재실행을 금지한다.
패턴 3: 작업 큐와 인간 개입(Human-in-the-loop) 분리
위험 단계는 자동 실행 대신 승인 큐로 보내야 한다. 이는 보안뿐 아니라 운영 책임 분리를 위해서도 필요하다.
# 위험 단계 승인 큐 투입
./ops/agent/enqueue-review.sh \
--job-id job_20260303_8821 \
--step-id 3 \
--reason "requiresApproval=true"
# 실행 워커 동시성 상한
./ops/agent/worker-start.sh \
--queue agent_exec \
--concurrency 20 \
--max-retry 2 \
--dead-letter agent_dlq
{
"job_id": "job_20260303_8821",
"state": "QUEUED_REVIEW",
"plan_template": "onboarding_v1",
"current_step": "createTicket",
"risk_level": "medium",
"requires_human_approval": true,
"approved_by": null
}
운영 포인트:
- 자동 실행 큐와 승인 큐를 분리해 책임과 권한을 명확히 한다.
- DLQ(dead-letter queue) 기준을 명시해 무한 재시도를 방지한다.
- 승인 SLA를 운영 지표로 포함해 병목을 관리한다.
실패 사례/안티패턴
장애 시나리오: "에이전트 무한 루프와 큐 적체"
상황:
- Planner가 "도구 실패 시 재계획" 규칙을 과도하게 적용했다.
- 외부 API 권한 오류(고정 실패)가 발생했는데도 replan -> execute 루프가 계속됐다.
- 30분 내 큐 길이가 10배 증가하고, 신규 요청 처리 지연이 4배 상승했다.
탐지 절차:
agent_replan_rate급등 경보tool_error_code=PERMISSION_DENIED반복 패턴 확인- 동일
jobId에서 상태 전이 사이클(NEEDS_REPLAN <-> PLANNED) 다수 탐지
완화 절차:
- 권한 오류를 non-retriable로 즉시 분류
- replan 횟수 상한 적용(예: max 1)
- 문제 job를 리뷰 큐로 강제 이동
회복 절차:
- 상태 머신에 순환 전이 감지 규칙 추가
- 계획 템플릿에 "권한 실패 시 human escalation" 명시
- 운영 대시보드에
replan_loop_count지표 추가
대표 안티패턴
- Planner와 Executor를 하나의 프롬프트 호출로 합치는 설계
- 상태 영속화 없이 메모리 객체로만 진행 상태 유지
- 도구 실패를 전부 모델 재시도로 처리
- 위험 단계도 동일 큐에서 자동 실행
체크리스트
- Planner와 Executor가 논리/프로세스 수준에서 분리되어 있는가?
- 상태 머신 전이가 명시되어 있고 순환/무한 루프 감지가 가능한가?
- step 단위 체크포인트와 멱등 실행이 보장되는가?
- non-retriable 오류(권한/정책/스키마)를 재시도에서 제외하는가?
- 자동 실행 큐와 human-review 큐가 분리되어 있는가?
- DLQ 기준과 복구 runbook이 존재하는가?
-
replan_rate,queue_depth,approval_sla를 운영 지표로 모니터링하는가?
요약
에이전트를 운영 가능하게 만드는 핵심은 모델 능력이 아니라 시스템 제어다. Planner/Executor 분리, 상태 머신, 체크포인트, 큐, 승인 플로우를 갖추면 실패를 국소화하고 복구 시간을 줄일 수 있다. 반대로 체인 기반 구현을 그대로 운영에 올리면 문제는 품질이 아니라 신뢰성에서 먼저 터진다.
다음 편 예고
다음 편에서는 제품화(Productization)를 다룬다. 실패 UX, 기대치 관리, human-in-the-loop 경험 설계, 운영 거버넌스를 연결해 "기술적으로 맞지만 사용자에게 실패한" LLM 기능을 어떻게 제품으로 완성하는지 설명한다.