결제 요청을 보냈는데 응답이 오지 않았다. 이때 같은 요청을 다시 보내도 될까. 서버에서는 이미 결제를 처리했을 수 있다. 응답을 받지 못했다는 사실만으로 결제 실패를 판단하면 중복 청구가 생길 수 있다.
AI 에이전트도 이 문제를 만난다. 도구를 올바르게 선택했더라도 재시도 과정에서 같은 일을 두 번 실행할 수 있다. 무엇을 실행할지 판단하는 능력과, 실행을 중복 없이 처리하는 능력을 함께 갖춰야 한다.
응답 실패와 실행 실패를 구분해야 한다
타임아웃은 요청의 최종 결과를 모른다는 뜻일 수 있다. 서버가 요청을 받기 전에 연결이 끊겼는지, 처리를 마친 뒤 응답이 사라졌는지 호출자는 바로 알 수 없다.
에이전트에서는 자동 재시도 외에 대화 상태도 영향을 준다. 실행 기록이 요약에서 빠지거나 다음 작업에 전달되지 않으면 모델이 같은 도구를 다시 호출할 수 있다. 모델에게 “중복 실행하지 마라”라고 지시하는 것만으로는 서버의 상태까지 보장할 수 없다.
결제·예약·이메일 발송처럼 외부에 영향을 주는 작업은 이 점을 고려해야 한다. 읽기 요청도 사용량이나 과금에 영향을 줄 수 있으므로, 모든 재시도를 똑같이 안전한 것으로 취급하지 않는 편이 좋다.
같은 작업에는 같은 키를 쓴다
멱등성은 같은 요청을 여러 번 처리해도 의도한 효과가 한 번만 발생하도록 만드는 성질이다. 이를 구현하는 방법 중 하나가 멱등 키다.
Stripe의 API 문서는 요청에 멱등 키를 붙이고, 같은 키로 재요청하면 앞서 저장한 상태 코드와 응답을 반환하는 방식을 설명한다. 키 보존 기간과 오류 처리 조건도 있으므로 사용하는 API의 계약을 확인해야 한다.
키는 모델이 호출할 때마다 새로 만드는 값이어서는 안 된다. 예를 들어 하나의 주문에 대한 한 번의 결제 시도라면, 그 작업의 ID를 저장해 재시도에서도 같은 키를 사용해야 한다. 새로 결제해야 하는 별도 주문에는 다른 키가 필요하다.
서버도 키와 처리 결과를 안전하게 기록해야 한다. 같은 키의 요청 두 개가 동시에 들어왔을 때 둘 다 실행되지 않도록 동시성 처리까지 확인해야 한다. 결제는 끝났는데 실행 기록만 저장하지 못하는 경우도 점검 대상이다.
실행 상태를 남겨야 복구할 수 있다
작업 기록에는 무엇을 요청했는지, 어떤 키를 사용했는지, 결과가 확정됐는지가 필요하다. 결과를 모르는 상태는 실패나 성공으로 임의 변환하지 않고 따로 남긴다. 이후 같은 키로 재조회하거나 서비스가 제공하는 조회 API로 상태를 확인한다.
외부 서비스가 멱등 키를 지원하지 않는다면 자체 작업 대장, 중복 확인, 사람의 검토 등 그 서비스에 맞는 대책을 정해야 한다. 이메일을 잘못 보낸 뒤 기록을 지우는 것으로 발송이 취소되지는 않는다.
검증할 때도 정상 호출만 보면 부족하다. 응답 유실, 동시 요청, 프로세스 재시작을 재현해 실제 결제나 예약이 몇 건 생겼는지 확인해야 한다. 에이전트가 완료했다고 말하는 것과 시스템에 한 건만 처리된 것은 각각 확인할 결과다.