실패를 숨기는 성공 코드
요약
HTTP 200 응답이 실제 작업의 성공을 보장하지 않는다는 점을 지적하며, 특히 에이전트와 모델 게이트웨이 환경에서 발생할 수 있는 '조용한 실패'의 위험성을 경고합니다. 상태 코드뿐만 아니라 응답 본문의 내용을 검증하는 설계의 중요성을 강조합니다.
핵심 포인트
- HTTP 200 응답이 실제 비즈니스 로직의 성공을 의미하지 않음
- 에이전트 루프에서 도구 호출(tool call)의 성공 여부 검증 필요
- 모델 게이트웨이의 거절이나 응답 저하를 감지하기 위한 본문 검사 필수
- 상태 코드에만 의존하는 모니터링 시스템의 함정 주의
오늘 왜 알림을 놓쳤는지 추적하는 데 한 시간을 보냈고, 그 답이 너무나 신경 쓰여서 이렇게 글로 남깁니다.
저는 작은 알림 설정을 운영하고 있습니다. 인프라에 문제가 생기면 봇이 채팅 채널에 메시지를 게시하여 제가 실제로 확인할 수 있게 합니다. 전송 호출(send call)은 HTTP 200을 반환합니다. 저의 상태 지표(health metric)는 200이 아닌 응답을 감시하며 몇 주 동안 평온한 녹색 상태를 유지했기에, 저는 알림 시스템이 정상이라고 생각했습니다.
하지만 그렇지 않았습니다. 며칠 동안 아무것도 전달되지 않았습니다.
봇이 사용하는 토큰이 교체되었는데, 제가 한 곳에서 업데이트하는 것을 잊어버렸던 것입니다. 그래서 전송 호출은 만료된 토큰으로 API를 호출하고 있었습니다. 그리고 저를 당황하게 만든 부분은 바로 이것입니다: API가 어쨌든 200 OK를 반환했다는 점입니다. 실제 실패는 JSON 본문(body) 안에 들어 있었습니다: invalid_auth 에러 코드와 함께 ok: false가 적혀 있었습니다. 상태 라인(status line)은 성공이라고 말했지만, 본문은 "아니오"라고 말하고 있었습니다.
제 지표는 오직 상태 코드(status code)만 확인했습니다. 그래서 제가 가진 모든 대시보드는 "전송됨"이라고 표시했습니다. 현실은 문제가 생겼을 때 저에게 알려주는 것이 유일한 임무인 그 시스템이 조용히 작동을 멈췄고, 그것을 감시하는 장치는 잘못된 필드(field)를 보고 있었다는 것입니다.
이것이 단순한 챗봇의 특이한 현상이 아닌 이유
제가 이 글을 쓰는 이유는, 에이전트(agent)를 실제 도구(tool)에 연결하기 시작하면 어디에서나 동일한 함정이 도사리고 있으며, 그 위험성이 저의 경우보다 훨씬 더 크기 때문입니다.
모델 게이트웨이(model gateway)는 거절(refusal), 잘린 응답(truncated response), 또는 요청한 것과 전혀 다른 폴백 모델(fallback model)의 출력값이 담긴 본문을 포함하면서도 200을 반환할 수 있습니다. HTTP 계층은 만족스럽지만, 콘텐츠는 저하되었고 다운스트림(downstream)의 그 누구도 이를 알지 못합니다.
에이전트 루프(agent loop) 내부에서는 상황이 더 악화됩니다. 도구 호출(tool call)은 아무것도 수행하지 않았음에도 "성공"으로 돌아올 수 있습니다. 왜냐하면 성공한 것은 당신이 원하는 동작이 아니라, 호출 자체가 완료된 것이기 때문입니다. 에이전트는 "완료"를 읽고 다음 단계로 넘어가며, 결국 주장한 바를 아무것도 달성하지 못한 채 깔끔하게 실행된 결과물만을 얻게 됩니다.
한 가지 명확히 구분해 둘 미묘한 차이가 있습니다. 채팅 API (chat API)가 인증 오류 (auth error)를 200 상태 코드로 터널링하여 전달하는 것은 명백한 스펙 이탈 (spec deviation)이라고 볼 수 있습니다. 반면, 게이트웨이 (gateway)가 거절 시 200을 반환하는 것은 그렇지 않습니다. 그것은 올바른 HTTP 동작입니다. 추론 (inference) 자체는 실제로 성공했지만, 단지 그 내용이 당신이 원했던 것이 아닐 뿐입니다. 원인은 다르지만, 하류 (downstream)에서 당신을 기다리는 함정은 동일합니다.
200은 요청이 수신되었고 응답이 돌아왔음을 알려줍니다. 하지만 당신이 원했던 작업이 실제로 수행되었는지에 대해서는 아무것도 말해주지 않습니다. 대부분의 단순한 파이프라인 (plumbing)에서는 이 간극이 문제를 일으키지 않습니다. 하지만 알림 경로 (alerting path), 에이전트 (agent)가 단계를 차례로 쌓아 올리는 과정, 또는 결제 (payment)와 같이 조용한 무작정 동작 (no-op)이 누적되는 곳에서는 비용을 치르게 될 것이며, 나중에야 그 사실을 깨닫게 될 것입니다.
따라서 해결책은 영리할 필요가 없습니다. 그저 상태 코드 (status code)를 성공의 증거로 신뢰하는 것을 멈추는 것입니다. 본문 (body)을 읽는 것은 시작일 뿐 끝이 아닙니다. 왜냐하면 다단계 에이전트 (multi-step agent)에게는 본문 또한 거짓말을 할 수 있기 때문입니다. 요청이 돌아왔다는 사실이 아니라, 당신이 실제로 원했던 효과 (effect)에 대해 단언 (assert)하십시오. 만약 작업이 "메시지 전달"이었다면, 메시지가 실제로 전달되었는지 확인하십시오. 만약 "에이전트가 변경 사항을 적용함"이었다면, 외부 상태 (external state)를 확인하십시오. 즉, 응답이 주장하는 내용이 아니라 DB 행 (DB row)이 존재하는지, 디스크 상의 파일이 실제로 변경되었는지를 확인해야 합니다.
실제 결과와 연결되지 않은 초록불은 불이 없는 것보다 더 나쁩니다. 적어도 불이 없다면 당신은 확인하러 갈 테니까요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기