재시도 전에 아이뎀포턴시(Idempotency): AI 에이전트를 위한 더 안전한 도구 실행
요약
AI 에이전트가 도구를 호출할 때 발생하는 네트워크 시간 초과 문제와 중복 실행 위험을 다룹니다. 단순히 재시도하는 것만으로는 부족하며, 애플리케이션 레벨에서 '아이뎀포턴시(Idempotency)'를 구현하여 동일한 논리적 작업의 반복 요청이 단일 요청과 같은 효과를 갖도록 설계해야 합니다.
핵심 포인트
- 네트워크 시간 초과는 실패가 아닌 결과 미전달일 수 있습니다.
- 재시도만으로는 부족하며, 아이뎀포턴스 처리가 필수입니다.
- 작업을 식별하는 '운영 키(operation key)'를 부여하여 중복 실행을 방지해야 합니다.
- 운영 기록에는 테넌트, 도구 유형, 매개변수 해시 등을 포함해야 합니다.
AI 에이전트가 응답을 받지 못했더라도 도구 호출은 성공할 수 있습니다. 결제 서비스에 환불 요청을 제출하는 에이전트를 상상해 보십시오. 서비스는 환불 처리를 하지만, 에이전트가 확인을 받기 전에 네트워크 연결 시간이 초과됩니다. 에이전트는 오류를 보고 재시도합니다. 만약 두 번째 요청이 또 다른 환불을 생성한다면, 일시적인 통신 실패가 중복된 비즈니스 작업으로 변질됩니다.
문제는 반드시 모델의 추론에 있는 것이 아닙니다. 그것은 에이전트와 에이전트가 변경할 수 있는 시스템 사이의 실행 경계(execution boundary) 문제입니다. 재시도는 도움이 되지만, 아이뎀포턴스를 대체하지는 못합니다. 에이전트가 상태를 변경하는 도구 호출을 재시도하도록 허용하기 전에, 애플리케이션은 요청된 작업이 이미 승인되었거나 완료되었음을 인식할 수 있는 신뢰할 수 있는 방법이 필요합니다.
실패한 요청과 실패한 작업의 차이점
분산 시스템에서는 작업이 실패했는지 아니면 결과를 전달하지 못한 채 성공했는지를 구별하기 어렵게 만듭니다.
다음 시퀀스를 고려해 보십시오:
- 에이전트가 도구 게이트웨이를 통해 작업을 요청합니다.
- 게이트웨이가 다운스트림 서비스로 요청을 전송합니다.
- 서비스가 변경 사항을 커밋(commit)합니다.
- 응답이 손실되거나 클라이언트 마감 시간 이후에 도착합니다.
- 에이전트가 시간 초과를 받고 재시도할 것을 고려합니다.
4단계에서 호출자는 부수 효과(side effect)가 발생했는지 알지 못합니다. 모든 시간 초과를 실패의 증거로 간주하는 것은 안전하지 않습니다.
동일한 모호성은 작업자가 데이터베이스 트랜잭션을 커밋했지만 큐 메시지를 확인하기 전에 충돌할 때도 나타납니다. 원래 처리가 성공했음에도 불구하고 메시지가 다시 전달될 수 있습니다.
이것이 바로 재시도 정책과 작업 의미론(operation semantics)을 함께 설계해야 하는 이유입니다. AWS의 Well-Architected 가이던스는 변경하는 작업(mutating operations)을 아이뎀포턴하게 만들어 반복된 요청이 단일 요청과 동일한 효과를 갖도록 권장합니다. 중요한 구분점은 아이뎀포턴스가 반복의 효과를 보호할 뿐, 네트워크 요청이 오직 한 번 실행됨을 보장하지는 않는다는 것입니다.
각 논리적 액션에 안정적인 운영 키(operation key) 부여하기
에이전트는 여러 이유로 동일한 도구를 여러 번 호출할 수 있습니다. 따라서 애플리케이션은 하나의 논리적 액션을 재시도하는 것과 진정으로 새로운 액션을 구분해야 합니다. 예를 들어, 지원 티켓을 생성하는 워크플로우를 고려해 봅시다. 유용한 운영 키는 영속적인(durable) 워크플로우 실행 식별자와 안정적인 단계 식별자로부터 파생될 수 있습니다:
workflow-8472:create-ticket
이는 예시적인 키이며, 규정된 형식은 아닙니다. 프로덕션 시스템에서는 적절한 고유성(uniqueness), 범위(scope), 테넌트 격리(tenant isolation)를 가진 식별자를 사용해야 합니다.
이 키는 동일한 논리적 작업이 재시도될 때 변경되지 않아야 합니다. 시도마다 새로운 무작위 키를 생성하는 것은 목적을 달성하지 못합니다. 다운스트림 서비스는 각 시도를 다른 요청으로 인식하기 때문입니다. 반대로, 의도적인 두 번의 티켓 생성이라 할지라도 입력값이 비슷하다는 이유만으로 같은 키를 공유해서는 안 됩니다.
견고한 운영 기록(operation record)은 다음 항목과 키를 연관시켜야 합니다:
- 인증된 테넌트 또는 주체(principal).
- 도구 및 운영 유형.
- 정규화된 요청 매개변수(normalized request parameters)의 해시 값.
- 현재 실행 상태(execution state).
- 사용 가능한 경우 다운스트림 운영 또는 리소스 식별자.
- 최종 결과 또는 이를 검색하기에 충분한 정보.
매개변수 해시가 중요한 이유는 운영 키가 다른 액션을 조용히 승인해서는 안 되기 때문입니다. 동일한 키가 다른 매개변수와 함께 도착하면, 이전 결과를 재사용하는 대신 충돌(conflict)을 거부해야 합니다. Stripe은 Idempotent 요청에 대해 유사한 패턴을 문서화합니다: 같은 키는 재시도를 식별하며, 매개변수가 일치하지 않으면 거부됩니다. 그 정확한 보존 및 응답 캐싱 동작 방식은 Stripe에 특정한 것이므로 모든 도구 게이트웨이에 적용된다고 가정해서는 안 됩니다.
상태 기계(state machine)로서의 모델 실행
'완료됨(completed)'과 같은 단일 부울 값만으로는 도구 운영을 안전하게 설명하기에 충분하지 않습니다. 시스템은 시작되지 않은 작업, 진행 중인 작업, 완료된 작업, 그리고 불확실한 결과들을 구분해야 합니다.
가능한 상태 모델은 다음과 같습니다:
PENDING: 작업이 기록되었지만 실행되지 않았습니다.
RUNNING: 워커가 실행 시도를 소유하고 있습니다.
SUCCEEDED: 부작용(side effect)이 확인되었고 그 결과가 기록되었습니다.
FAILED_FINAL: 자동 재시도해서는 안 되는 방식으로 작업이 실패했습니다.
UNKNOWN: 결과를 아직 결정할 수 없습니다.
정확한 상태는 다운스트림 시스템에 따라 다릅니다. 특히, 외부 서비스가 어떤 작업을 수행했을 수 있지만 애플리케이션이 이를 확인할 수 없는 경우 UNKNOWN 상태가 중요합니다.
간소화된 흐름은 다음과 같습니다:
도구 요청 받기
|
v
신원(identity), 정책 및 매개변수 유효성 검사
|
v
작업 키 조회
|
+-- 완료됨(Completed) --> 기록된 결과 반환
|
+-- 실행 중(Running) ---> 대기, 폴링 또는 진행 상황 보고
|
+-- 새 항목(New) --------> 작업 기록 및 실행
|
v
결과 조정(Reconcile the outcome)
|
+---------+---------+
| |
확인됨(Confirmed) 불확실함(Uncertain)
| |
v v
성공(Succeeded) 알 수 없음(Unknown)
상태 전이와 실행 클레임은 동시성 안전(concurrency-safe)해야 합니다. 두 워커가 모두 누락된 기록을 관찰하고 독립적으로 동일한 작업을 실행해서는 안 됩니다. 데이터베이스의 고유 제약 조건(uniqueness constraint), 조건부 쓰기(conditional write) 또는 이와 동등한 원자적 조정 메커니즘이 주어진 작업 키에 대한 단일 소유자를 확립하는 데 도움이 될 수 있습니다.
하지만, 데이터베이스에서 키를 예약한다고 해서 외부 부작용(side effect)이 그 예약과 자동으로 원자적으로 되는 것은 아닙니다. 만약 워커가 외부 작업이 성공한 후 로컬 기록이 업데이트되기 전에 충돌한다면, 애플리케이션은 여전히 불확실한 결과에 직면할 수 있습니다. 이 간극에는 명시적인 복구 전략(recovery strategy)이 필요합니다.
알 수 없는 결과를 맹목적으로 재시도하지 마십시오
재시도 정책은 오류와 부작용(side effect)을 모두 고려해야 합니다. 요청이 전송되기 전에 발생한 연결 실패는 재시도가 안전할 수 있습니다. 유효성 검사 오류(validation error)는 보통 수정된 요청이 필요합니다. 속도 제한 응답(rate-limit response)은 적절한 지연 시간 후에 재시도가 가능할 수 있습니다. 잠재적으로 성공적인 쓰기 작업 이후 발생하는 타임아웃(timeout)은 다릅니다. 이 경우, 애플리케이션은 다시 시도하기 전에 다운스트림 시스템에 쿼리를 하거나 작업을 조정해야 할 수도 있습니다.
상태를 변경하는 도구(state-changing tool)의 경우, 실용적인 정책은 다음과 같습니다:
- 동일한 논리적 작업(logical action)에 대해 동일한 작업 키(operation key)를 재사용합니다.
- 적격한 일시적 실패(transient failures)에 대해 백오프(backoff)와 지터(jitter)가 적용된 제한된 재시도(bounded retries)를 적용합니다.
- 다운스트림 서비스의 재시도 지침과 속도 제한을 준수합니다.
- 결과가 불확실할 때 작업 상태를 확인합니다.
- 시스템이 해당 작업을 반복하는 것이 안전하다고 확신할 수 없을 때는 자동 재시도를 중단합니다.
- 중복된 작업이 심각한 결과를 초래할 수 있는 경우 에스컬레이션(Escalate)을 수행합니다.
모든 오류를 모델의 결정으로 바꾸어서는 안 됩니다. 모델은 실패를 해석하는 데 도움을 줄 수는 있지만, 결정론적인 애플리케이션 코드가 재시도가 허용되는지 강제해야 합니다. 이러한 분리는 환불 발행(issuing refunds), 계정 권한 변경(changing account permissions), 외부 메시지 전송(sending external messages) 또는 인프라 프로비저닝(provisioning infrastructure)과 같은 작업에 특히 중요합니다.
트랜잭션 경계의 중요성 (The transaction boundary matters)
멱등성(Idempotency)은 작업과 그 결과가 하나의 트랜잭션 경계 내에서 커밋될 수 있을 때 가장 쉽습니다. 예를 들어, 티켓을 생성하고 해당 작업 기록을 같은 데이터베이스 트랜잭션에 저장하는 서비스는 중복 요청이 원래의 티켓을 반환하도록 만들 수 있습니다. 작업 키에 대한 고유성 제약 조건(uniqueness constraint)은 동시 요청이 동일한 논리적 작업에 대해 별도의 레코드를 생성하는 것을 방지합니다.
외부 부작용은 더 어렵습니다. 데이터베이스 트랜잭션으로는 이미 전송된 이메일이나 다른 서비스에 의해 이미 처리된 환불을 정상적으로 롤백(roll back)할 수 없습니다. 작업이 여러 시스템에 걸쳐 있는 경우, 통합에 적합한 패턴을 사용해야 합니다:
트랜잭션 아웃박스(Transactional outbox). 로컬 데이터베이스 변경이 메시지를 생성해야 할 때, 변경 사항과 아웃박스 레코드를 하나의 트랜잭션으로 커밋합니다. 퍼블리셔는 나중에 해당 레코드를 전송할 수 있습니다. 컨슈머는 여전히 중복에 안전한(duplicate-safe) 처리가 필요합니다. 왜냐하면 메시지 전달이 한 번 이상 발생할 수 있기 때문입니다.
다운스트림 아이뎀포턴시(Downstream idempotency). 외부 API가 아이뎀포턴시 키를 지원한다면, 비즈니스 작업에 범위가 지정된 안정적인 키를 전달합니다. API의 키 보존 규칙과 동시 또는 불일치 요청에 대한 동작을 확인해야 합니다.
정산(Reconciliation). 외부 시스템이 아이뎀포턴시를 지원하지 않는다면, 사용 가능한 경우 영속적인 작업 식별자(durable operation identifier)나 조회 가능한 비즈니스 참조(queryable business reference)를 사용합니다. 모호한 타임아웃 후에는 다른 변경을 시도하기 전에 해당 작업이 이미 존재하는지 확인해야 합니다.
보상(Compensation). 다단계 워크플로우의 경우, 나중에 단계가 실패했을 때 완료된 단계를 어떻게 보상할지 정의해야 합니다. 보상은 역사가 완전히 되돌아가는 것(true rollback of history)이 아니라 새로운 비즈니스 행동입니다. 예를 들어, 환불은 원래의 트랜잭션을 지우지 않으면서 재정적 효과를 되돌릴 수 있습니다.
이러한 패턴들 중 어느 것도 임의의 외부 시스템 전반에 걸쳐 정확히 한 번 실행(exactly-once execution)을 보장하지는 않습니다. 이들은 정의된 가정 하에서 중복 효과가 발생할 가능성을 줄이고, 감지하거나 복구하기 쉽게 만듭니다.
에이전트 상태는 재시작 시에도 유지되어야 함 (Agent state must survive restarts)
에이전트의 대화 기록은 신뢰할 수 있는 실행 원장(execution ledger)이 아닙니다. 프로세스가 재시작되거나, 대화가 잘리거나, 작업자가 재할당되면, 시스템은 여전히 어떤 행동이 시도되었는지, 확인되었는지, 거부되었는지, 아니면 불확실한 상태로 남겨졌는지를 알아야 합니다.
운영 상태(operation state)를 일시적인 모델 컨텍스트 외부에서 영속화해야 합니다. 재개 시, 에이전트는 마지막 메시지로부터 성공 또는 실패를 추론하기보다는 애플리케이션에 기존 작업의 상태를 조회해야 합니다.
실행 기록을 에이전트의 계획과 분리하여 유지하세요 (Keep the execution record separate from the agent's plan):
- 플랜(plan)은 에이전트가 수행하려는 작업을 설명합니다.
- 오퍼레이션 레코드(operation record)는 시스템이 수락했고 발생한 것으로 알려진 내용을 설명합니다.
- 인증 정책(authorization policy)은 해당 작업이 허용되는지 여부를 결정합니다.
- 감사 기록(audit record)은 조사에 필요한 신원, 결정 및 결과를 기록합니다.
이러한 분리는 재시도가 인증 우회(authorization bypass)가 되는 것을 방지하기도 합니다. 이전에 승인된 작업이 본질적으로 다른 요청, 다른 테넌트 또는 권한이 변경된 작업을 자동으로 승인해서는 안 됩니다.
모델 호출뿐만 아니라 오퍼레이션을 관찰하라
성공적인 모델 응답이 도구 작업의 성공을 의미하지 않습니다. 마찬가지로, 모델 시간 초과(timeout)가 다운스트림 부작용(side effect)이 발생했는지 알려주지 않습니다. 워크플로우 실행, 논리적 작업, 재시도 시도 및 다운스트림 요청 식별자를 연결하는 추적(trace) 또는 동등한 상관관계 메커니즘으로 전체 실행 경로를 계측해야 합니다.
유용한 운영 신호에는 다음이 포함됩니다:
- 최종 상태별 작업 횟수.
- 재시도 시도 및 재시도 소진(retry exhaustion).
- RUNNING 또는 UNKNOWN 상태에 머무른 시간.
- 중복 키 충돌 및 매개변수 불일치.
- 조정 결과(Reconciliation outcomes).
- 다운스트림 지연 시간, 속도 제한 및 오류.
- 인간 검토로 라우팅된 작업.
텔레메트리에는 비밀 정보, 무제한 프롬프트 또는 민감한 도구 인수를 포함하지 않도록 주의하세요. 접근 제어 및 데이터 보존에 적절한 안정적인 식별자와 신중하게 선택된 속성을 사용해야 합니다. 가장 유용한 경고는 단순히 “도구 호출 실패”가 아닐 때가 많습니다. 그것은 “상태 변경 작업이 예상 복구 기간을 넘어서 해결되지 않고 남아있다”입니다. 이는 운영자에게 개입이 필요할 수 있는 지점을 알려줍니다.
커밋과 승인 사이의 실패를 테스트하라
해피 패스(Happy-path) 테스트는 재시도 안전성에 대해 거의 아무것도 증명하지 못합니다. 의도적으로 모호한 경계(ambiguous boundary)를 테스트하세요.
유용한 테스트 스위트에는 다음이 포함되어야 합니다:
- 동일한 작업 키를 사용한 두 개의 동시 요청.
- 하위 액션은 성공했지만 응답이 손실된 후의 재시도.
- 사이드 이펙트(side effect)는 발생했지만 결과가 기록되기 전에 워커가 충돌하는 경우.
- 다른 매개변수로 제출된 동일한 키.
- 하위 타임아웃(timeout) 이후 성공적인 조정(reconciliation).
- 만료되었거나 사용할 수 없는 아이뎀포턴시 레코드.
- 알 수 없는 상태에서 작업을 재개하는 재시작.
- 다른 테넌트나 권한이 없는 주체로부터의 반복 요청.
각 테스트에 대해 HTTP 응답뿐만 아니라 비즈니스 결과도 검증해야 합니다. 동일한 오류를 두 번 반환한다고 해서 중복된 사이드 이펙트가 방지되었다는 것을 증명하지 못합니다. 또한 복구 경로(recovery path)도 테스트해야 합니다. 안전하지 않은 재시도를 올바르게 거부하지만 모든 작업을 영구적으로 UNKNOWN 상태에 남겨두는 시스템은 좁은 의미에서는 안전할 수 있지만, 운영상으로는 완전하지 않습니다.
작업의 의미론적 결과로 재시도 만들기
AI 에이전트는 또 다른 의사 결정 계층을 도입하지만, 근본적인 분산 시스템 문제를 바꾸지는 못합니다. 애플리케이션은 여전히 내구성 있는 상태(durable state), 안정적인 작업 식별자(stable operation identity), 동시성 제어(concurrency control), 권한 부여(authorization) 그리고 불확실한 결과를 조정할 방법이 필요합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기