Alejandro Rioja.
AI Agents Operations

프로덕션에서 AI 에이전트를 디버깅하는 방법 (현장 가이드)

Alejandro Rioja
Alejandro Rioja
5 분 읽기
TL;DR

프로덕션 AI 에이전트를 디버깅하는 것은 대부분 어느 레이어가 실패했는지 — 프롬프트, 도구, 모델, 오케스트레이션 — 분리하는 일이다. 나는 모든 단계를 트레이스 ID로 로깅하고, 정확히 같은 입력을 리플레이하며, 이분 탐색한다. 내 에이전트에서는 'AI 버그'의 약 70%가 모델 버그가 아니라 배관 버그로 드러난다.

무료 뉴스레터

매주 수요일. 28,400명+ 구독자. 핵심만.

목차

2026년 6월 업데이트.

TL;DR: 프로덕션 AI 에이전트를 디버깅하는 것은 대부분 어느 레이어가 실패했는지 — 프롬프트, 도구 호출, 모델 출력, 오케스트레이션 — 분리하는 일이다. 나는 모든 단계를 트레이스 ID로 로깅하고, 정확히 같은 입력을 리플레이하며, 거기서부터 이분 탐색한다. 내 에이전트에서는 “AI 버그”처럼 보이는 것의 약 70%가 배관으로 드러난다 — 잘못된 형식의 도구 결과, 잘린 입력, 조용히 삼켜진 예외다.

운영자의 시각: 나는 100개가 넘는 프로덕션 에이전트를 운영한다 — Pickleland의 예약 플로우, 콘텐츠 파이프라인, 받은편지함 분류기다. 그것들은 모든 소프트웨어가 망가지는 방식으로 망가지고, 거기에 몇 가지 새로운 방식이 더해진다. 이것은 내가 가지고 있었으면 했던 현장 가이드다. 토큰의 벽을 응시하지 않고 실패한 레이어를 찾는 방법이다.

에이전트가 프로덕션에서 오작동하면 본능적으로 모델을 탓하게 된다. “Claude가 환각을 일으켰다.” 가끔은 사실이다. 대개는 아니다. 모델은 다섯 개나 여섯 개로 된 스택의 한 레이어이며, 버그는 Anthropic이 출시한 레이어보다 당신이 작성한 레이어에 있는 경우가 훨씬 많다. 이 글은 내가 그것을 찾는 체계적인 방법이다.

무엇이든 디버깅하기 전에 모든 실행을 추적 가능하게 만들어라

볼 수 없는 것은 디버깅할 수 없다. 가장 큰 레버리지가 되는 일은 — 특정 버그가 나타나기 전에 — 모든 에이전트 실행에 트레이스 ID를 붙이고 그것이 밟는 모든 단계를 로깅하는 것이다.

“단계”는 경계를 넘는 모든 것이다. 들어오는 트리거, 각 모델 호출(전체 메시지 배열 포함), 각 도구 호출(인자 포함), 각 도구 결과, 그리고 최종 출력. 그것들을 트레이스 ID로 키를 매긴 구조화된 JSON으로 로깅하라.

typescript
function logStep(traceId: string, step: string, payload: unknown) {
  console.log(JSON.stringify({
    traceId,
    step,            // "trigger" | "model_call" | "tool_call" | "tool_result" | "output"
    ts: Date.now(),
    payload,
  }));
}

Cloudflare Workers에서는 이것들을 큐와 테이블로 보내고, 로컬에서는 stdout으로 보낸다. 규칙은 절대적이다. 단계가 로깅되지 않았다면, 디버깅에 관한 한 그것은 일어나지 않은 것이다. 이것은 내가 사용하는 에이전트 스택에서 설명한 계측을 반영한다 — 트레이스 ID는 다른 모든 것이 매달려 있는 척추다.

레이어를 분리하라: 프롬프트, 도구, 모델, 오케스트레이션

트레이스가 있으면 디버깅은 이분 탐색이 된다. 레이어는 네 개이고, 버그는 대부분의 경우 그중 정확히 하나에 산다.

1. 입력 레이어 (가장 흔한 범인)

실패한 모델 호출에 들어간 정확히 같은 messages 배열을 꺼내라. 재구성이 아니라 — 로그의 문자 그대로의 페이로드다. 그런 다음 낯선 사람이 읽듯이 읽어라. “모델이 지시를 무시했다”는 내 버그의 절반은 실제로 다음과 같다.

입력이 잘못되었다면, 모델은 쓰레기에 대해 완벽하게 제 일을 한 것이다. 배관을 고쳐라.

2. 도구 레이어

입력이 깨끗해 보이면, 도구가 에이전트가 성공으로 취급한 오류를 반환했는지 확인하라. 전형적인 예: API가 { "error": "rate limited" } 본문과 함께 200 을 반환하고, 당신의 도구 래퍼가 본문을 확인하지 않으며, 에이전트가 오류 메시지에 자신만만하게 행동한다. 도구 결과를 원본 그대로 로깅하고 그 형태를 검증하라.

3. 모델 레이어

1과 2를 배제한 뒤에야 나는 모델을 의심한다. 그때조차 “모델 버그”는 대개 “내 프롬프트가 모호하다”를 뜻한다. 실패한 정확히 같은 입력을 가져다 같은 모델과 온도에 대해 일회성 스크립트에 넣고 재현되는지 보라. 재현된다면 해결책은 프롬프트 작업이나 더 엄격한 eval이지, 다급한 모델 교체가 아니다.

4. 오케스트레이션 레이어

단일 단계가 격리 상태에서는 괜찮은데 다단계 실행이 실패한다면, 버그는 인계에 있다 — 단계 사이에서 잃어버린 상태, 경쟁 조건, 멱등하지 않은 작업을 재실행한 재시도다. 이것들이 가장 고약하며, 나는 멀티 에이전트 오케스트레이션 패턴에서 그 패턴들을 다룬다.

비결정성과 싸우지 말고 재현하라

에이전트를 디버깅 불가능하게 느끼게 하는 것은 비결정성이다. 같은 입력이 실행마다 다른 출력을 낸다. 그것은 길들일 수 있다.

첫째, 고정할 수 있는 것을 고정하라. 디버깅하는 동안 temperature: 0 으로 설정하라. Claude를 완전히 결정론적으로 만들지는 않지만, 분산을 크게 좁혀서 진짜 버그를 샘플링 노이즈와 구분할 수 있게 한다.

둘째, N번 실행하라. 실패가 20번에 1번 재현된다면, 정확히 같은 입력을 50번 반복하고 모든 출력을 캡처하라. 이제 일화가 아니라 샘플을 갖게 된다. 5%의 확률로 터지는 버그는 진짜 버그다 — 그것을 보려면 양이 필요할 뿐이다.

bash
for i in $(seq 1 50); do
  node replay.mjs --trace=abc123 >> runs.jsonl
done
# 그런 다음 실패를 센다
grep -c '"status":"fail"' runs.jsonl

셋째, 성공한 실행과 실패한 실행을 비교하라. 온도를 고정하고 입력이 같다면, 출력의 차이는 당신이 아직 발견하지 못한 입력의 차이를 의미한다 — 프롬프트 안의 타임스탬프, 변하는 도구 결과, 바뀐 검색 문서다.

리플레이 하니스를 만들어 프로덕션에서 디버깅하는 것을 멈춰라

라이브 에이전트를 다시 트리거하며 디버깅하는 것은 느리고 위험하다 — 실제 이메일을 보내고, 실제 코트를 예약한다. 대신 트레이스를 캡처해 오프라인에서 리플레이하라.

리플레이 하니스는 로깅된 트레이스를 불러와, 어떤 단계든 그에 대한 정확히 같은 입력을 재구성하고, 그 단계만 모델에 대해 재실행한다. 전체 messages 배열을 로깅해 두었으므로 상류 시스템은 전혀 필요 없다. 이것은 프로덕션에서의 10분 왕복을 2초짜리 로컬 루프로 바꾸며, 내 디버깅 워크플로에서 가장 큰 속도 향상이다.

좋은 리플레이 하니스는 또한 변형하고 재실행하게 해준다. 시스템 프롬프트의 한 줄을 바꾸고, 같은 50개의 실패 트레이스를 리플레이하여 이제 몇 개가 통과하는지 보라. 이것이 디버깅에서 eval로 가는 다리다 — 실패 트레이스의 코퍼스가 생기면 회귀 스위트의 시작이 손에 들어온다.

실제로 고장을 예측하는 지표를 관찰하라

어떤 실패는 결코 예외를 던지지 않는다. 에이전트는 실행되고, 그럴듯한 무언가를 반환하며, 조용히 잘못된 일을 한다. 그것들을 잡으려면 오류율만이 아니라 행동 지표를 관찰하라.

나는 이것들을 다른 모든 것과 같은 방식으로 추적한다 — AI 에이전트가 실제로 작동하는지 어떻게 측정하는가를 보라. 조용한 실패를 잡는 지표는 시끄러운 실패를 잡는 지표 열 개의 가치가 있다.

5분 분류 체크리스트

에이전트가 망가지고 시간에 쫓길 때, 나는 이것을 순서대로 실행한다.

  1. 실패한 실행의 트레이스 ID를 확보하라.
  2. 실패한 단계에 대한 정확히 같은 입력을 읽어라. 잘 구성되어 있는가? (여기서 약 50%의 경우가 해결된다.)
  3. 그 트레이스에서 도구 결과를 확인하고 성공으로 위장한 오류를 찾아라.
  4. temperature: 0 에서 단계를 오프라인으로 리플레이하라. 재현되는가?
  5. 재현되면, 프롬프트/모델 문제다 — 고치고 트레이스 코퍼스를 재실행하라. 아니면, 비결정성이거나 상태/오케스트레이션 버그다 — 50번 반복해 특성을 파악하라.

규율 있는 분리는 매번 영리한 프롬프팅을 이긴다. 모델이 문제인 경우는 드물다. 대개 문제는 그 주변의 시스템이다.

자주 묻는 질문

가끔씩만 실패하는 AI 에이전트는 어떻게 디버깅하나요?

로깅된 트레이스에서 정확히 같은 입력을 캡처해 온도 0에서 50번 이상 리플레이하라. 간헐적 실패는 발화율이 낮은 진짜 버그다 — 양이 일화를 비교하고 고칠 수 있는 재현 가능한 샘플로 바꾼다.

버그는 보통 모델에 있나요, 아니면 제 코드에 있나요?

내 프로덕션 에이전트에서는 겉보기 “AI 버그”의 약 70%가 배관이다. 잘못된 형식의 도구 결과, 잘린 입력, 삼켜진 예외, 단계 사이에서 잃어버린 상태다. 모델을 의심하기 전에 입력 레이어와 도구 레이어를 배제하라.

에이전트를 디버깅하는 데 필요한 최소한의 로깅은 무엇인가요?

모든 실행의 트레이스 ID, 더해서 트리거, 각 모델 호출(전체 메시지 배열), 각 도구 호출과 그 원본 결과, 그리고 최종 출력의 구조화된 로그. 단계가 로깅되지 않으면 그것을 디버깅할 수 없다.

라이브 프로덕션에 대고 디버깅하는 것을 어떻게 멈추나요?

로깅된 트레이스를 불러와 캡처한 입력을 사용해 어떤 단일 단계든 오프라인으로 재실행하는 리플레이 하니스를 만들어라. 그것은 느리고 위험한 프로덕션 왕복을 빠른 로컬 루프로 바꾸며 회귀 스위트의 씨앗이 된다.

계속 읽기

AI 플레이북을 받아보세요

매주 수요일. 28,400명+ 구독자. 핵심만.

↵ 전체 결과 보기 esc esc 닫기