인터페이스가 200을 반환했는데 Agent 작업은 왜 여전히 실패하는가

비동기 수리(受理) 모드에서 200은 서버가 **검증을 마치고 큐에 넣었다(수리 완료)**는 뜻일 뿐, 업무가 정말로 완료되었다는 뜻이 아니다.

메시지 큐가 정체되었는지, 하류 컨슈머가 죽었는지, 쿠폰 재고가 정말로 차감되었는지는 전부 200이 반환된 이후에 벌어진다. 따라서 Agent는 비동기 인터페이스를 호출한 뒤 반드시 능동적으로 대사(對賬)해야 하며, 상태 코드만 신뢰해서는 안 된다.

핵심 원칙

Agent는 비동기 인터페이스를 호출한 뒤 반드시 능동적으로 대사해야 하며 상태 코드만 믿어서는 안 된다. 진짜 작업 성공의 표지는 업무 측 데이터의 최종 일관성이지 HTTP 계층의 응답 코드가 아니다.

1층: 비동기 수리 모드 이해하기

쿠폰 발급 인터페이스를 예로 들면, 서버는 요청을 받고 세 가지만 한다

  1. 파라미터 검증 통과
  2. 수리 일련번호 생성
  3. 요청을 Kafka에 투입
  4. 그리고 즉시 200 + 수리번호 반환

2층: 대사 전략

수리번호를 받은 뒤 손을 놓아서는 안 되고, 일정 시간 간격을 두고 능동적으로 작업의 실제 실행 결과를 조회해야 한다.

Agent --(수리번호 지참)--> 폴링 인터페이스 --> 최종 상태: 활성화됨 / 실패

핵심 공학 제약:

  • 대사 로직을 Agent의 도구 호출 플로우에 써넣을 것
  • 대사 전용 도구를 하나 줄 것
  • 자연어로 작업 성공 여부를 추측하게 하지 말 것

3층: 타임아웃과 폴백 (Timeout Control / Fallback Mechanism)

타임아웃 후의 두 출구:

  1. 알람 발생
  2. 이상 작업을 사람 처리 큐로 이관

정리

「비동기 인터페이스 호출마다 대사 프로세스를 시작하고, 수리번호로 결과 인터페이스를 폴링한다. 3회 폴링 후에도 최종 상태가 아니면 이상 티켓을 보고하며, 동시에 Agent가 자동 보상 능력을 갖추도록 보장한다.」

핵심 통찰

  1. HTTP 상태 코드가 서술하는 것은 「전송」이지 「업무」가 아니다 — 200의 의미 경계는 「요청이 서버에 수신되어 성공적으로 파싱됨」이며, 비동기 아키텍처에서는 한 걸음 더 나아가 「큐에 들어감」으로 퇴화한다. 프로토콜 계층의 신호를 업무 계층의 결론으로 삼는 모든 설계는 잘못된 추상 층위에서 판단하는 것이다.
  2. 비동기 인터페이스의 200은 「시작 신호」이지 「종료 신호」가 아니다 — 서버가 수리 일련번호 + 메시지 큐를 도입하는 순간, 반환 시점과 완료 시점은 영구히 디커플링된다. 성공 여부를 판단하는 유일한 권위는 업무 측 데이터의 최종 일관성(쿠폰 코드가 정말 차감되었는가)이지 응답 코드가 아니다.
  3. 수리번호는 비동기 계약의 핵심 자산이다 — 그것은 동시에 세 역할을 맡는다: 호출 영수증, 대사 증빙, 보상 근거. 인터페이스 설계 시 조회 가능한 번호를 반환하지 않으면 Agent는 영원히 추측만 할 수밖에 없다. 이것은 비동기 인터페이스 설계의 좋고 나쁨을 평가하는 첫 번째 지표이기도 하다.
  4. 대사는 반드시 명시적 도구여야 하며 모델의 자연어 추론이면 안 된다 — Agent에게 「대사 전용 도구」를 주어야 한다고 명확히 요구한다. 모델이 언어로 「완료되었을 것 같다」를 판단하게 하는 것은 확정적인 상태 조회를 확률적인 언어 생성으로 바꾸는 것이며, Agent 사고의 가장 흔한 원인이다.
  5. 폴링에는 상한이 있어야 하고 타임아웃에는 출구가 있어야 한다 — 「무한 대기 금지」는 재시도 전략에 횟수를 못 박고(3회) 타임아웃 후의 동작(알람 / 사람 큐 이관)을 미리 정의해야 한다는 뜻이다. 출구 없는 대기는 Agent에서 「조용한 멈춤」으로 나타나 실패보다 트러블슈팅이 어렵다.
  6. 실패 탐지는 절반만 완성한 것이고 보상까지 해야 폐루프다 — 「쿠폰 최종 활성화 실패」를 발견한 뒤 자동 재발급이나 사용자 재수령 안내가 없으면, 시스템은 사실 장애를 「아무도 모름」에서 「로그에는 있음」으로 격상시킨 것에 불과하다. 폐루프의 판단 기준은 사용자가 여전히 고객센터에 전화해야 하는가이다.
  7. 대사 간격 자체가 조정이 필요한 공학 변수다 — 간격이 너무 길면 장애 발견이 느려지고, 너무 짧으면 정상적인 비동기를 이상으로 오판해 무효 알람과 사람 개입을 만든다. 신뢰성 설계에서 오탐과 미탐은 같은 지렛대의 양 끝이다.
  8. 좋은 폴백 능력은 인터페이스 설계 단계에서 확보해야지 Agent 측에서 억지로 버티면 안 된다 — 멱등 조회 인터페이스 + 명확한 상태 머신(처리 중/활성화됨/실패/만료됨)이 불확실성을 원천에서 없앤다. Agent 측에서 재시도 로직을 아무리 많이 짜도 의미가 모호한 상태 필드를 메울 수는 없다.

엔지니어링 실전 Tips

  • Agent 프롬프트와 코드에서 「200」의 이름을 바꿀 것: success가 아니라 accepted로 명명해 변수명부터 오판을 차단할 것.
  • 조회 인터페이스는 반드시 멱등이어야 한다: 대사는 태생적으로 반복 호출되므로, 멱등하지 않은 조회 인터페이스는 폴링 하에서 부작용이나 카운트 오염을 만든다.
  • 상태 머신에 「만료됨」 상태가 있어야 한다: 「성공/실패/처리 중」만 있으면 타임아웃 작업이 영원히 「처리 중」에 머물러 대사 프로세스가 수렴하지 않는다.
  • 재시도 횟수는 프롬프트가 아니라 설정에 쓸 것: 3회는 예시값이며 실제 값은 「업무 측 P99 완료 소요」가 결정해야 하고, 간격은 그 소요의 자릿수보다 커야 한다.
  • 이상 티켓에는 전체 컨텍스트를 담을 것: 수리번호, 호출 시각, 폴링 기록을 함께 보고해야 사람이 개입할 때 현장을 다시 재현하지 않아도 된다.
  • 「작업 실패」와 「대사 미완료」를 구분할 것: 전자는 보상 링크로, 후자는 사람 큐로 간다. 둘을 뒤섞으면 아직 대기 중인 작업에 중복 재발급을 발기하게 된다.
  • 보상 동작 자체도 중복 방지가 필요하다: 자동 재발급도 마찬가지로 수리번호 기반 멱등 통제를 해야 하며, 그렇지 않으면 한 번의 오판이 중복 발급으로 이어진다.