죽지 않는 에이전트 엔지니어링 #2: Idempotency Shield — 에이전트를 어떤 횟수로 재시도하든 한 번만 실행되게 만들기
요약
에이전트 시스템에서 재시도 시 작업이 중복 실행되는 문제를 방지하기 위한 멱등성(Idempotency) 설계 방법을 다룹니다. 에이전트의 실행 단위가 크기 때문에 발생하는 중복 실행 위험을 3계층 방어 아키텍처를 통해 해결하는 가이드를 제공합니다.
핵심 포인트
- 에이전트는 일반 프로그램보다 실행 세분화 수준이 커서 멱등성 결여 시 재앙적 결과 초래
- IdempotencyKey를 활용한 요청 중복 제거 레이어 구축
- 미들웨어 수준에서 동작하는 요청 중복 제거 로직 구현
- 시스템 안정성을 위한 3계층 방어 아키텍처 설계
죽지 않는 에이전트 엔지니어링 #2: Idempotency Shield — 에이전트를 어떤 횟수로 재시도하든 한 번만 실행되게 만들기
사전 조건:
- 관측 가능성(Observability) 배포 완료 (07-19)
- 이것은 "죽지 않는 에이전트 엔지니어링" 시리즈 #2입니다.
새벽 3시, 당신의 에이전트는 하나의 지침을 받습니다: "모든 유료 사용자에게 연간 보고서를 전송하세요."
에이전트는 처리를 시작합니다—데이터베이스를 조회하고, 보고서를 생성하며, 이메일을 보냅니다. 총 1,200개의 이메일 중 998개가 발송됩니다.
그러자 당신의 서버가 강제로 재시작됩니다.
에이전트는 깨어나서 할 일 목록을 확인합니다: "남은 이메일 202개." 에이전트는 계속 진행합니다.
결과: 동일한 사용자 기반에게 총 1,400개의 이메일이 발송되었습니다—그중 998개가 중복입니다.
당신의 클라이언트들이 전화해서 묻습니다: "이게 무슨 쓰레기 같은 건가요?"
이것은 버그가 아닙니다. 이것은 누락된 Idempotency(멱등성) 문제입니다.
1. 에이전트가 일반 프로그램보다 Idempotency 재앙에 더 취약한 이유
전통적인 프로그램: 버튼을 한 번 누르면, 그것은 한 번 실행됩니다.
에이전트: 버튼을 한 번 누르지만, 17개의 도구를 호출하고, 3개의 API에 접근하며, 프로세스 중간에 5번이나 재시작할 수 있습니다.
근본적인 원인: 에이전트의 실행 세분화 수준(execution granularity)은 "단일 함수 호출"보다 훨씬 크기 때문입니다.
| 시나리오 | 비멱등적 결과 | 일반적인 손실 |
|---|---|---|
| 결제 요청 재시도 | 이중 청구 | $2,000~50,000 |
| 데이터베이스 쓰기 재시도 | 중복 레코드 생성 | 데이터 오염 |
| API 호출 타임아웃 재시도 | 중복 리소스 생성 | 리소스 낭비 |
| 작업 중단 복구 | 부분적인 작업이 두 번 실행 | 로직 오류 |
2. 3계층 Idempotency 방어 아키텍처
┌─────────────────────────────────────────────────────────┐
│ IdempotencyGuard │
├───────────────┬─────────────────┬─────────────────────┤
...
3. 레이어 1: IdempotencyKey 요청 중복 제거
import hashlib
import time
from datetime import datetime, timedelta
...
4. 레이어 2: 요청 중복 제거 미들웨어
from functools import wraps
import asyncio
from typing import TypeVar, ParamSpec
...
python
guard = IdempotencyGuard()
middleware = IdempotencyMiddleware(guard)
@middleware.idempotent(
agent_id="my-agent",
operation="send_email"
)
async def send_email_task(recipients: list, subject: str):
# Actual sending logic
return await email_service.send(recipients, subject)
```
"""
def decorator(func: Callable[P, T]) -> Callable[P, T]:
@wraps(func)
...
5. Layer 3: 자연적 Idempotency (Natural Idempotency) — 자체적으로 Idempotent한 작업 설계하기
모든 작업이 idempotency guard가 필요하지는 않습니다. 자연적으로 idempotent한 작업을 설계하는 것이 더 나은 전략입니다:
class NaturallyIdempotent:
"""
Natural Idempotency Library — 이 작업들은 부작용(side effects)을 생성하지 않습니다...
sql
-- 예시: 사용자 구독 상태 업데이트
UPDATE subscriptions
SET status = 'active', updated_at = NOW()
WHERE user_id = '123'
INSERT INTO subscriptions (user_id, status, created_at, updated_at)
SELECT '123', 'active', NOW(), NOW()
WHERE NOT EXISTS (
SELECT 1 FROM subscriptions WHERE user_id = '123'
)
```
"""
pass # 데이터베이스에 따라 조정하세요
...
6. 완전한 에이전트 Idempotency 통합 예제
# agent_idempotency_example.py
"""
완전한 에이전트 Idempotency 통합 예제
...
7. Idempotency 배포 체크리스트
배포 전 확인 사항:
- 모든 결제/환불 작업에 idempotency guard를 사용했는가
- 모든 생성(creation) 작업에서 INSERT 대신 UPSERT를 사용하는가
- 모든 외부 API 호출에 idempotency key가 있는가
- 작업 재시작 복구 로직이 idempotent한가
- 데이터베이스 트랜잭션 롤백이 idempotency 상태에 영향을 주지 않는가
- Idempotency key의 TTL(Time To Live)이 합리적인가 (보통 24시간이면 충분함)
- Idempotency guard 자체가 스레드 안전(thread-safe)한가
8. Unkillable Agent Engineering과의 통합
Idempotency Guard는 "Unkillable Agent Engineering"의 핵심 구성 요소 중 하나입니다:
| 계층 (Layer) | 구성 요소 (Component) | 책임 (Responsibility) |
|---|---|---|
| 관측성 (Observability) | TraceGuard | 문제 발견 |
| ... |
이 다섯 가지 계층은 함께 작동하여 완전한 에이전트 생존 시스템 (Agent survival system)을 형성합니다.
이는 또한 우리가 ARK Trust에서 멱등성 (Idempotency) 모듈을 구축할 때 적용한 핵심 사고방식이기도 합니다. 즉, 사후 조치 (remediation)가 아니라, 설계 단계부터 모든 작업이 안전하게 재실행 (re-executable)될 수 있도록 보장하는 것입니다.
추가 읽을거리:
- "Unkillable Agent Engineering #1: 관측성 (Observability)의 세 가지 기둥"
- "당신의 AI 에이전트가 죽는 일곱 가지 방식"
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기