에이전트는 데모에서는 티켓을 읽고, 세 개의 API를 호출하고, 요약을 게시합니다. 하지만 배포 후에는 같은 고객에게 이메일을 두 번 보내거나, 재시도 루프에서 하루치 토큰 예산을 소진하거나, 프론트엔드가 파싱할 수 없는 페이로드를 반환할 수 있습니다.
프로토타입과 신뢰할 수 있는 프로덕션 에이전트의 차이는 대개 모델이 아니라 API 경계에서 발생합니다. 에이전트는 도구 호출 루프이며, 각 도구 호출은 실패·스로틀링·시간 초과·예상 밖의 응답을 반환할 수 있는 HTTP 요청입니다. 따라서 일반 API 통합처럼 계약을 검증하고, 실패를 모의(mock)하고, 실제 반환값을 어설션해야 합니다.
이 글에서는 에이전트 장애를 다섯 가지 모드로 나누고, 각 모드를 API 테스트 관점에서 구현하는 방법을 설명합니다. Apidog를 사용하면 도구 계약을 정의하고, 종속 API를 모의하며, 응답과 복구 동작을 검증할 수 있습니다.
에이전트는 프롬프트가 아닌 API 경계에서 실패합니다
프로덕션에서 에이전트가 잘못 동작하면 프롬프트부터 수정하기 쉽습니다. 그러나 실제 원인은 다음과 같은 API 통합 문제인 경우가 많습니다.
- 응답 시간이 예상보다 길다.
- 응답 본문이 스키마와 다르다.
-
429 Too Many Requests또는500 Internal Server Error가 발생한다. - 필수 필드가 누락되거나 타입이 바뀐다.
- API가 오류 정보를 가진
200 OK를 반환한다.
에이전트 단계는 보통 다음 흐름을 가집니다.
- 모델이 도구를 선택한다.
- 애플리케이션 코드가 도구 선택을 HTTP 요청으로 변환한다.
- 외부 API가 응답한다.
- 애플리케이션 코드가 결과를 모델에 다시 전달한다.
이 중 모델 자체와 직접 관련된 단계는 하나뿐입니다. 나머지는 기존 API 테스트 기법으로 검증할 수 있습니다.
핵심 질문은 다음입니다.
모델이 충분히 똑똑한가가 아니라, 에이전트의 API 호출이 실패할 수 있는 방식을 모두 테스트했는가?
실패 모드 1: 계약에서 벗어나는 도구 호출
가장 흔한 문제는 모델이 API 계약과 맞지 않는 도구 호출을 생성하는 것입니다.
예를 들어 예약 API가 다음 요청을 기대한다고 가정합니다.
{
"guests": 2,
"date": "2025-06-15"
}
하지만 에이전트가 다음처럼 호출할 수 있습니다.
{
"guests": "two",
"date": "next Friday"
}
또는 필수 필드를 누락할 수도 있습니다.
{
"guests": 2
}
이런 요청은 이상적으로 400 Bad Request를 반환해야 합니다. 하지만 일부 API는 오류 정보를 본문에 담은 채 200 OK를 반환할 수 있고, 에이전트는 이를 성공으로 오해할 수 있습니다.
구현 방법: 도구 호출을 API 계약으로 검증하기
각 도구의 요청 스키마를 명시적으로 정의하고, 에이전트가 생성한 요청을 그 스키마에 대해 검증하세요.
예를 들어 POST /reservations의 계약은 다음 조건을 포함해야 합니다.
-
guests는 정수다. -
guests는 1 이상이다. -
date는 지정한 날짜 형식이다. - 필수 필드가 모두 존재한다.
- 허용하지 않는 추가 필드는 거부한다.
테스트는 “에이전트가 실행되었다”가 아니라 “에이전트가 유효한 요청을 보냈다”를 검증해야 합니다.
expect(request.body).toMatchObject({
guests: expect.any(Number),
date: expect.any(String),
});
expect(request.body.guests).toBeGreaterThanOrEqual(1);
실무에서는 도구 스키마를 Apidog에 저장하고, 에이전트가 실제로 보낸 요청을 해당 정의와 비교하는 방식이 효과적입니다. 계약 위반은 어떤 필드가 잘못되었는지 포함한 검증 실패로 표시되어야 합니다.
더 자세한 내용은 AI 에이전트의 도구 호출 테스트와 API를 호출하는 에이전트 테스트를 참고하세요.
실패 모드 2: 업스트림 오류 및 속도 제한
외부 API는 언제든 실패할 수 있습니다.
429 Too Many Requests500 Internal Server Error- 네트워크 오류
- 응답 시간 초과
- 일시적인 잘못된 응답 본문
취약한 에이전트는 첫 번째 오류에서 중단하거나, 반대로 무한 재시도로 더 많은 스로틀링과 비용을 유발합니다. Anthropic SDK의 에이전트 오류 복구 패턴 토론에서도 이 문제가 반복적으로 다뤄집니다.
구현 방법: 실패 순서를 모의하기
정상 API만 대상으로 테스트하면 복구 로직을 검증할 수 없습니다. 다음과 같은 응답 시퀀스를 가진 모의를 만드세요.
-
429와Retry-After헤더 반환 -
500반환 - 마지막 요청에서 성공 응답 반환
예시는 다음과 같습니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 3
Content-Type: application/json
{
"error": "rate_limit_exceeded"
}
테스트에서 확인할 항목은 다음과 같습니다.
-
Retry-After를 준수하는가? - 지수 백오프와 지터(jitter)를 적용하는가?
- 최대 재시도 횟수를 넘으면 중단하는가?
- 서비스가 계속 실패할 때 서킷 브레이커를 여는가?
- 재시도 시 중복 이메일, 중복 결제, 중복 주문이 발생하지 않는가?
재시도 대상 작업은 반드시 멱등성을 고려해야 합니다. 예를 들어 요청마다 멱등성 키를 포함할 수 있습니다.
POST /payments
Idempotency-Key: 8c7eeecb-8b8a-4c32-bd0a-0f8fbf3c1f2a
멱등성 키를 사용하면 네트워크 실패 후 재시도하더라도 동일한 작업이 중복 실행되는 위험을 줄일 수 있습니다.
속도 제한 자체도 별도 시나리오로 테스트하세요. 속도 제한 초과 응답이 의미하는 것과 AI 에이전트 오류 복구 가이드에서 재시도, 시간 초과, 백오프, 서킷 브레이커 패턴을 확인할 수 있습니다.
실패 모드 3: 비결정적 출력
온도를 0으로 설정해도 모델 출력이 실행마다 바이트 단위로 동일하다고 보장할 수는 없습니다. 하드웨어, 배치 처리, 공급자 측 변경 등은 출력 변동을 만들 수 있습니다. vLLM의 재현성 관련 토론도 이 문제를 보여줍니다.
따라서 다음과 같은 테스트는 취약합니다.
expect(agentResponse).toBe(
"고객님의 주문이 정상적으로 취소되었습니다."
);
문구가 조금만 바뀌어도 실패하기 때문입니다. 이런 불안정한 테스트는 결국 무시되며, 사실상 테스트가 없는 상태와 비슷해집니다. 불안정한 테스트의 원인도 함께 확인하세요.
구현 방법: 텍스트가 아닌 구조와 의미를 검증하기
정확한 문자열 대신 다음을 어설션하세요.
- 응답이 JSON 스키마에 맞는가?
- 필수 키가 존재하는가?
- 금지된 필드가 없는가?
- 호출한 도구와 대상이 올바른가?
- 숫자가 합리적인 범위에 있는가?
- 상태 값이 허용된 열거형인가?
예를 들어 장바구니 요약 응답은 다음처럼 테스트할 수 있습니다.
expect(response).toMatchObject({
currency: "KRW",
total: expect.any(Number),
items: expect.any(Array),
});
expect(response.total).toBeGreaterThanOrEqual(0);
expect(response.total).toBeLessThanOrEqual(cartValue);
이 테스트는 자연스러운 문장 변동을 허용하면서도, total 누락이나 음수 금액 같은 실제 회귀를 포착합니다.
비결정적 AI 에이전트 테스트에서 더 구체적인 전략을 확인할 수 있습니다. 상태를 가진 에이전트라면 에이전트 메모리가 작동하는 방식도 함께 고려해야 합니다.
실패 모드 4: 폭주하는 비용
에이전트는 도구 호출과 모델 호출을 반복합니다. 루프는 비용으로 이어집니다.
실패한 요청을 수천 번 재시도하는 에이전트 하나만으로도 작은 비용이 하룻밤 사이 큰 청구서가 될 수 있습니다. 비용 문제는 재정 문제이면서 동시에 신뢰성 문제입니다. 과도한 호출, 중복 요청, 불필요하게 큰 컨텍스트는 에이전트를 느리고 예측 불가능하게 만듭니다.
구현 방법: 호출 수와 예산을 테스트에 포함하기
복구 시나리오에서 성공 여부만 확인하지 마세요. 호출 수와 토큰 사용량도 검증해야 합니다.
expect(mockApi.calls()).toBeLessThanOrEqual(3);
expect(run.tokenUsage).toBeLessThanOrEqual(TOKEN_BUDGET);
프로덕션 구현에서는 다음 제한을 두세요.
- 실행당 토큰 예산
- 작업당 최대 도구 호출 수
- 도구별 최대 재시도 횟수
- 전체 실행 시간 제한
- 반복 가능한 조회 결과의 캐시
- 같은 요청의 중복 실행 방지
테스트가 성공했더라도 성공까지 40번의 API 호출이 필요했다면, 이는 잠재적인 비용 장애입니다.
에이전트 토큰 비용 절감 가이드에서 비용 제어에 사용할 수 있는 구체적인 방법을 확인할 수 있습니다.
실패 모드 5: 가드레일 누락
가장 큰 피해는 에이전트가 지시를 정확히 수행했지만, 수행해도 안 되는 행동을 실행했을 때 발생합니다.
예를 들면 다음과 같습니다.
- 확인 없이 고객에게 이메일을 보낸다.
- 데이터베이스 레코드를 삭제한다.
- 주문을 취소하거나 환불한다.
- 관리자나 상사에게 메시지를 보낸다.
모델의 결정과 실제 부작용 사이에 검증 단계가 없다면, 올바른 도구 호출도 잘못된 결과를 만들 수 있습니다.
구현 방법: 부작용 앞에 승인 게이트 두기
파괴적이거나 되돌리기 어려운 도구는 허용 목록과 승인 절차 뒤에 배치하세요.
const destructiveActions = new Set([
"send_email",
"delete_record",
"cancel_order",
]);
if (destructiveActions.has(toolCall.name) && !userApproved) {
return {
status: "requires_approval",
proposedAction: toolCall,
};
}
드라이런 모드도 유용합니다. 드라이런에서는 실제 API를 호출하지 않고, 에이전트가 수행할 작업만 반환합니다.
{
"mode": "dry_run",
"proposed_actions": [
{
"tool": "send_email",
"recipient": "customer@example.com",
"subject": "주문 상태 안내"
}
]
}
테스트에서는 실제 부작용 엔드포인트를 모의하고 다음을 검증하세요.
- 승인 없는 호출이 차단되는가?
- 실제 API 대신 승인 요청 경로를 따르는가?
- 드라이런에서 외부 변경이 발생하지 않는가?
- 허용 목록에 없는 도구가 실행되지 않는가?
LLM 애플리케이션을 위한 OWASP Top 10은 검토할 보안 위험의 좋은 체크리스트입니다. AI 에이전트 가드레일에서 승인 게이트와 폭발 반경 제어를 더 자세히 다룹니다.
에이전트 테스트를 구성하는 방법
다섯 가지 실패 모드는 모두 같은 테스트 루프를 사용합니다.
- 에이전트가 호출할 수 있는 도구의 요청·응답 스키마를 정의합니다.
- 각 외부 종속성을 모의하여 상태 코드, 지연 시간, 헤더, 응답 본문을 제어합니다.
- 정상 경로뿐 아니라
429,500, 시간 초과, 잘못된 JSON 같은 실패 경로를 실행합니다. - 요청 형태, 복구 동작, 호출 횟수, 비용 제한, 가드레일 작동 여부를 어설션합니다.
한 도구부터 시작하세요. 예를 들어 send_email 도구에 대해 다음 시나리오를 만듭니다.
| 시나리오 | 모의 응답 | 검증 항목 |
|---|---|---|
| 정상 전송 | 202 Accepted |
올바른 수신자와 본문 |
| 속도 제한 |
429 + Retry-After
|
백오프와 최대 재시도 |
| 일시적 장애 |
500 후 202
|
제한된 횟수 내 복구 |
| 승인 누락 | 실제 호출 차단 | 승인 경로 반환 |
| 잘못된 요청 | 스키마 검증 실패 | 프로덕션 전 테스트 실패 |
첫 번째 도구에서 이 루프가 안정화되면 다음 도구를 추가하세요. 사용자가 발견하기 전에 깨진 도구 호출을 포착하는 순간, 이 테스트 체계의 가치를 확인할 수 있습니다.
에이전트 신뢰성 체크리스트
프로덕션 배포 전에 다음 항목을 확인하세요.
- [ ] 모든 도구 호출이 스키마에 대해 검증되며, 계약 위반 시 테스트가 실패한다.
- [ ] 업스트림
429,500, 시간 초과 응답을 시뮬레이션했다. - [ ] 에이전트가 백오프와 제한된 재시도로 복구한다.
- [ ] 재시도 작업이 멱등적이어서 이중 청구나 이중 전송이 발생하지 않는다.
- [ ] 테스트가 정확한 문구가 아니라 구조와 의미를 검증한다.
- [ ] 실행당 토큰 사용량과 도구 호출 수를 측정한다.
- [ ] 예산 상한선이 폭주하는 루프를 중지한다.
- [ ] 파괴적 작업이 허용 목록 또는 인간 승인 게이트 뒤에 있다.
- [ ] 가드레일 경로를 모의 테스트로 실제 검증했다.
이 항목을 모두 확인했다면, 에이전트가 프로덕션에서 실패하는 주요 경로를 상당 부분 테스트한 것입니다.
Apidog의 역할과 역할이 아닌 것
Apidog는 에이전트 프레임워크, 모델 호스트, 평가 하네스가 아닙니다. 에이전트를 구축하거나 실행하지도 않습니다.
Apidog가 담당하는 영역은 에이전트가 의존하는 API 계층입니다. 실제로 다음 작업에 사용할 수 있습니다.
- 에이전트 도구의 API 계약을 설계하고 저장하기
- 나가는 요청을 계약에 대해 검증하기
-
429,500, 시간 초과, 잘못된 형식의 본문을 모의하기 - 스키마, 필수 키, 값 범위, 응답 형태를 어설션하기
- 실제 부작용 없이 복구 및 가드레일 경로를 검증하기
즉, Apidog는 에이전트가 호출하는 API를 테스트하고, 에이전트가 처리해야 할 실패를 모의하며, 반환되는 응답을 검증하는 데 적합합니다. 에이전트 AI 테스트에서 이 흐름을 더 넓은 QA 관점으로 살펴볼 수 있습니다.
자주 묻는 질문
에이전트 신뢰성은 모델 문제인가요, 엔지니어링 문제인가요?
대부분 엔지니어링 문제입니다. 모델 선택도 중요하지만, 잘못된 도구 호출, 처리되지 않은 속도 제한, 재시도 루프, 가드레일 누락은 모델을 바꾸지 않고도 통합과 테스트로 개선할 수 있습니다.
실제 API를 호출하지 않고 에이전트를 테스트할 수 있나요?
네. 그리고 복구 경로와 가드레일 경로를 테스트하려면 그렇게 해야 합니다. 종속 API를 모의하면 오류 응답을 강제하고, 지연 시간을 제어하며, 실제 부작용을 방지할 수 있습니다.
실행마다 출력이 달라질 때 테스트는 어떻게 작성하나요?
정확한 텍스트 대신 구조와 의미를 검증하세요. JSON 스키마, 도구 호출 형태, 필수 키, 값 범위, 허용된 상태 값을 사용하면 자연스러운 출력 변동을 견디면서도 실제 오류를 잡을 수 있습니다. 비결정적 AI 에이전트 테스트를 참고하세요.
무엇부터 테스트해야 하나요?
가장 먼저 파괴적 행동에 대한 가드레일과 오류 복구를 테스트하세요. 이 두 영역은 해로운 작업 실행과 예산 소진 루프라는 가장 비용이 큰 실패를 막습니다.
하나의 실패 모드부터 시작하세요
다섯 가지를 한 번에 구현할 필요는 없습니다. 이번 주에는 가장 위험한 실패 모드 하나를 선택하세요. 대부분의 팀에서는 가드레일 또는 오류 복구가 좋은 시작점입니다.
다음 순서로 진행하면 됩니다.
- 실패 응답 하나를 정의합니다. 예:
429와Retry-After. - 해당 응답을 반환하는 모의를 만듭니다.
- 에이전트를 실행합니다.
- 재시도 횟수, 대기 시간, 최종 결과, 토큰 사용량을 검증합니다.
- 결과를 CI 테스트에 추가합니다.
시뮬레이션된 429를 에이전트가 예산 소진 루프 대신 제한된 재시도와 깔끔한 백오프로 처리하는 것을 확인하면, 데모가 성공하는 것보다 훨씬 강한 근거로 에이전트를 신뢰할 수 있습니다.
계약을 설계하고, 실패를 모의하며, 에이전트가 의존하는 응답을 검증하려면 Apidog를 다운로드하세요.
Top comments (0)