AI 에이전트가 고객에게 결제를 두 번 처리했고 모든 대시보드는 녹색을 유지했습니다
요약
AI 에이전트가 시간 초과로 인해 결제 API 호출을 재시도했을 때, 기존 SRE 지표로는 감지하기 어려운 '사일런트 실패' 유형의 위험성을 경고합니다. 특히 쓰기 접근 권한을 가진 에이전트는 동일하고 되돌릴 수 없는 액션을 중복 실행할 위험이 높습니다. 이를 방지하기 위해 결정론적 액션 키를 생성하거나, 슬라이딩 TTL 기반의 중복 제거 로직을 구현하는 것이 중요하다고 강조합니다.
핵심 포인트
- SRE 지표가 놓치는 '사일런트 실패' 유형에 대한 경고
- LLM 에이전트는 비결정론적 재시도로 인해 위험할 수 있음
- 쓰기 경로를 보호하기 위해 결정론적 액션 키(Fingerprint) 사용 권장
- 슬라이딩 TTL을 이용해 제한된 시간 내 중복 요청 명시적으로 차단해야 함
AI 에이전트가 시간 초과(timeout)를 겪었다. 이로 인해 결제 API 호출을 재시도했다. 하지만 첫 번째 요청은 이미 성공한 상태였다.
모든 모니터링 대시보드는 녹색으로 유지되었다.
이것은 기존의 Site Reliability Engineering (SRE) 지표가 완전히 놓치는 '사일런트 실패(silent failure)' 유형이다. 우리는 "요청이 HTTP 200을 반환했는가?"라고 묻는다. 하지만 쓰기 접근 권한(write access)을 가진 에이전트에게 중요한 질문은 다음과 같다: 에이전트가 동일하고 되돌릴 수 없는 액션(irreversible action)을 두 번 실행했는가?
LLM 에이전트가 비결정론적 재시도(non-deterministic retries) 하에 작동할 때, 상태 코드(status code)는 실제로 무슨 일이 일어났는지에 대한 진실을 알려주지 못한다.
에이전트가 상황을 악화시키는 이유
일반적인 재시도 로직은 지루하고 예측 가능하다. 하지만 에이전트는 그렇지 않다:
- 재시도가 쌓인다(Retries stack). HTTP 클라이언트가 재시도하고, 오케스트레이션 프레임워크(orchestration framework)가 재시도하며, 모델은 "다시 시도하자"고 결정한다. 각 계층은 자신이 유일한 주체라고 가정한다.
- 재시도가 재구성된다(Retries get rephrased). 시간 초과 후 에이전트는 액션을 재계획하고 매개변수(parameters)를 순서 변경하거나 약간 다르게 하여 다시 발행할 수 있으며, 이로 인해 하위 계층은 이를 중복으로 인식하지 못한다.
- 시간 초과는 "실패"가 아니라 "알 수 없음(unknown)"을 의미한다. 에이전트는 쓰기 작업이 완료되었는지 알 수 없으며, 따라서 "다시 실행하자"고 추측하는 경향이 있다.
중요한 쓰기 경로를 위한 세 가지 규칙
1. 결정론적 액션 키(Deterministic action keys)
전송하기 전에 액션을 지문 인식(Fingerprint)하라:
key = hash(action_name + sorted_parameters + caller_scope)
페이로드(payload)가 일치하면 에이전트는 중복된 변경(duplicate mutation)을 발생시킬 수 없다. 논리적으로 동일한 페이로드가 같은 해시 값을 갖도록 키를 정렬하고 타입을 표준화하라. 관련 없는 사용자 간의 충돌을 방지하기 위해 호출자 범위(caller scope, 테넌트, 세션, 워크플로우 실행 등)와, 동일한 액션이 합법적으로 반복될 수 있는 경우 의도 ID(intent ID)를 포함해야 한다.
2. 슬라이딩 TTL 중복 제거(Sliding TTL deduplication)
제한된 시간 창(예: 300초) 내에서 반복되는 시그니처를 차단한다.
중복 요청을 조용히 무시하지 마세요. 그렇게 하면 버그가 그 뒤에 숨겨집니다. 호출(call)을 가로채서 명시적인 오류와 함께 거부하고 카운터를 증가시키세요. '슬라이딩(Sliding)'은 차단된 시도가 창(window)을 새로 고치므로, 루프에 갇힌 에이전트는 계속 차단 상태를 유지합니다.
- 중복 액션 비율 (Duplicate Action Rate, DAR)을 핵심 SLI로 활용하기
DAR = blocked_duplicate_actions / total_action_attempts
만약 DAR이 급증한다면, 에이전트 탓을 하지 마세요. 그것은 거의 항상 처리되지 않은 타임아웃(timeout), 느린 데이터베이스 잠금(database locks) 또는 상위 시스템 지연(upstream lag)에 대한 보상 작용입니다. DAR은 고객보다 먼저 이러한 문제를 보여주는 경향이 있습니다.
Python에서 최소한의 게이트 구현하기
import hashlib
import json
import threading
import time
class DuplicateActionError(Exception):
"""동일한 액션이 이미 해당 창 내에 제출되었을 때 발생합니다."""
class DedupGate:
def __init__(self, ttl_seconds: float = 300, clock=time.monotonic):
self.ttl = ttl_seconds
self._clock = clock
self._seen: dict[str, float] = {}
self._lock = threading.Lock()
self.allowed = 0
self.blocked = 0
@staticmethod
def action_key(action_name: str, params: dict, caller_scope: str) -> str:
canonical = json.dumps(
...
결제 호출 주변에 사용하기:
gate = DedupGate(ttl_seconds=300)
def charge(order_id: str, amount_cents: int, tenant: str):
params = {"order_id": order_id, "amount_cents": amount_cents}
key = gate.acquire("create_charge", params, caller_scope=tenant)
try:
# 제공업체가 IDEMPOTENCY KEY를 지원하는 경우 `key`도 함께 전달하세요
return payments.create_charge(**params, idempotency_key=key)
except PaymentDeclined:
gate.release(key) # 결정적인 실패: 재시도가 안전함
raise
# 타임아웃 발생 시: 절대 release하지 마세요. 대신 charge를 조회하여 조정하세요.
에이전트는 루프에 빠지는 대신 추론할 수 있는 유형의 오류(
⚠️ [IMG:N] 형식 토큰은 이미지 placeholder 입니다. 번역하지 말고 원래 위치에 그대로 유지하세요.
주의사항 (Gotchas)
모호한 타임아웃 처리. 타임아웃이 발생했다고 해서 키를 해제해서는 안 됩니다. 먼저 다운스트림 시스템과 대조(Reconcile)해야 합니다. 명확하게 실패했을 때만 해제하세요.
인메모리 상태는 확장성(scale out)을 지원하지 않습니다. 이 게이트는 하나의 프로세스에 존재합니다. 여러 워커를 사용할 경우, Redis SET key value NX EX 300과 같은 공유 원자적 저장소(shared atomic store)나 DynamoDB의 조건부 쓰기(conditional write)로 백업해야 합니다.
다층 방어 (Defense in depth). 가능한 경우 액션 키를 제공자(provider)의 Idempotency Key로 전달하세요. 이 게이트는 당신의 에이전트로부터 보호하며, 제공자 키는 다른 모든 것들로부터 당신을 보호합니다.
정당한 반복 실행. 의도적인 10달러 송금 두 건은 중복이 아닙니다. 키에 의도 ID(intent ID)를 넣으세요.
배포 위치 (Where this lives)
저는 이 중복 제거 게이트를 오픈 소스 agentsre 원격 측정 프레임워크에 통합하여 DAR가 에이전트가 방출하는 다른 신호들 옆에 자리하도록 했습니다: [https://lnkd.in/e7Qpux2j]
토론 (Discussion)
에이전트들이 읽기 전용 어시스턴트(read-only assistants)에서 프로덕션 쓰기 시스템으로 이동함에 따라, 귀하의 팀은 Idempotency와 중복 실행을 어떻게 처리하고 있습니까? 에이전트 계층에서 강제할 것인가요, API 계층에서 강제할 것인가요, 아니면 둘 다에서 강제할 것인가요? 경험담을 듣고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기