본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 19. 04:35

에이전트 실행 실패 후 대부분의 토큰이 낭비됩니다 — 40줄의 코드로 이를 측정하세요

요약

에이전트 실행 중 첫 번째 실패 신호가 발생한 이후에도 토큰이 과도하게 낭비되는 문제를 다룹니다. 40줄의 파이썬 코드를 통해 실패 시점과 실제 종료 시점 사이의 토큰 낭비율을 측정하는 방법을 제시합니다.

핵심 포인트

  • 실패한 에이전트 실행은 첫 경고 발생 후에도 상당량의 토큰을 추가로 소비함
  • 논문 데이터에 따르면 경고 발생 후 평균 58.1%의 토큰이 낭비됨
  • 40줄의 스크립트로 JSON 트레이스 기반의 토큰 낭비율 측정 가능
  • 낭비된 토큰을 측정하여 CI 게이트의 종료 코드로 활용 가능

**에이전트 실패 후 낭비되는 토큰 (Wasted tokens after agent failure)**은 아무도 측정하지 않는 부분입니다. 깨끗하게 성공한 에이전트 실행과 실패한 실행은 시작 비용이 거의 비슷하지만, 비용 차이는 실행이 이미 실패한 _이후_에 발생합니다. 이 포스트는 40줄짜리 오프라인 측정기(offline meter)를 통해, 첫 번째 실패 신호가 발생한 이후에도 실행이 계속해서 소모하는 토큰 비율인 '꼬리(tail)' 부분을 측정합니다.

제가 이 글을 쓰게 만든 수치는 다음과 같습니다. 멀티 에이전트 관측성 (multi-agent observability)에 관한 2026년 논문에서 연구진은 165개의 GAIA 트레이스 (traces)를 측정하였고, 경고가 발생한 실패한 실행 중 평균 58.1%의 토큰이 첫 번째 경고 신호 이후에 소비된다는 것을 발견했습니다. 먼저 경고(도구 오류, 루프, 예산 압박 플래그 등)가 발생하고, 그 후 에이전트는 멈추기 전까지 전체 실행 토큰의 절반 이상을 계속 사용합니다. 인용문을 주의 깊게 읽어주세요. 그 58.1%는 _그들_의 수치이며, 모든 실행이 아닌 _경고가 발생한 실패한 실행_에 국한된 것입니다. 제 측정값과는 다릅니다. 저는 이 둘을 끝까지 구분하여 설명하겠습니다.

제가 강조하고 싶은 점은 다음과 같습니다: 낭비는 실패가 아닙니다. 실패 자체는 저렴한 부분입니다. 진짜 비용이 많이 드는 것은 "이 실행은 명백히 경로를 벗어났다"라는 시점과 "에이전트가 실제로 멈춘" 시점 사이의 거리입니다. 그 간극은 토큰으로 계산되며, 여러분의 로그를 통해 약 1분 만에 직접 측정할 수 있습니다.

요약 (TL;DR)

  • 실패한 에이전트 실행은 첫 번째 감지 가능한 실패 신호 _이후_에 대부분의 토큰을 소비합니다. 발표된 수치에 따르면 경고가 발생한 실패한 실행에서 58.1%가 소모됩니다 (arXiv 2606.01365, 165 GAIA traces).
  • waste_probe.py는 40줄로 구성된 오프라인, 키리스 (keyless), 읽기 전용 스크립트입니다. JSON 트레이스를 입력하면 첫 번째 신호를 찾아 그 시점과 그 이후에 소모된 토큰 점유율을 출력합니다.
  • 제가 직접 만든 루프 고정 장치 (loopy fixture)에서 측정했을 때는 82.9%의 낭비 (707/853 토큰)가 발생했습니다. 이는 고정 장치에서 실행한 결과이며, 논문의 수치와는 별개입니다.
  • 종료 코드 (Exit code)는 CI 게이트 (CI gate)로 사용할 수 있습니다: 낭비가 임계값 이하이면 0, 초과하면 1, 사용 오류 시 2를 반환합니다. 수집된 트레이스에 연결하여 사용하세요.
  • 이것은 사후 분석용 측정기 (post-mortem meter)이지, 런타임 제한 장치 (runtime cap)가 아닙니다. 어디에서 토큰이 낭비되었는지를 알려줄 뿐, 무엇인가를 차단하지는 않습니다.

에이전트 실패 후 낭비되는 토큰이 실제로 발생하는 곳: 첫 번째 신호가 곧 첫 번째 중단 지점은 아니다

로그에서 실패한 실행이 실제로 어떻게 보이는지 생각해 보세요. 단 한 번의 깔끔한 폭발인 경우는 드뭅니다. 약간 다른 JSON 스키마(schema)를 반환하는 200 OK 응답을 주는 도구, KeyError: 'close'를 던지는 파서(parser), 그리고 일시적인 오류(flakiness)라고 가정하고 재시도(retry) 분기를 실행하는 에이전트가 나타납니다. 그래서 에이전트는 _정확히 동일한 요청_을 다시 보냅니다. 그리고 또 보냅니다. 페이로드(payload)는 전혀 변하지 않습니다. 에이전트는 다시 파싱(reparse)하지도 않습니다. 그저 루프를 돌며 매 턴마다 비용을 온전히 지불하고, 점점 더 확신에 찬 문체로 자신의 혼란을 서술할 뿐입니다.

그 실행을 무언가가 중단할 때쯤이면, 진단 정보는 이미 첫 번째 에러 시점에 가용했습니다. 그 이후의 모든 과정은 트레이스(trace)에 이미 포함되어 있던 결론을 다시 도출하는 과정일 뿐입니다. 그것이 바로 낭비입니다. 실패 그 자체가 아니라, 실패 _이후의 지속(persistence past)_이 문제입니다.

이것이 무엇이고 무엇이 아닌지에 대해 정확히 짚고 넘어가고 싶습니다. 왜냐하면 서로 인접한 두 가지 개념에는 이미 도구들이 존재하기 때문입니다:

이 도구가 제어하는 게이트는 에이전트의 다음 호출이 아니라, CI 파이프라인의 종료 코드 (exit code)입니다.

따라서 이 도구가 답하는 질문은 좁고 검증 가능합니다: 주어진 트레이스 (trace) 내에서, 첫 번째 실패 신호가 발생한 시점 또는 그 이후에 사용된 토큰의 비율은 얼마인가? 만약 실제 로그에서 이 비율이 일관되게 작게 나타난다면, 이 가설은 귀하에게 틀린 것이며 도구는 종료 코드 0 (exit 0)과 함께 이를 알려줄 것입니다. 제 말을 믿기보다는 직접 확인해 보시는 편이 낫습니다.

한 가지 더 짚고 넘어갈 점이 있습니다. 이는 실패의 꼬리 (tail)를 줄이는 것이 아니라 오히려 키우기 때문입니다: 바로 팬아웃 (fan-out)입니다. Anthropic의 Dynamic Workflows (2026년 5월 말 출시된 리서치 프리뷰)는 하나의 실행이 수십에서 수백 개의 병렬 서브 에이전트 (subagents)를 생성할 수 있게 하며, 실행당 최대 16개의 동시 실행 및 총 1,000개로 제한됩니다. InfoQ의 기사는 당연한 점을 지적합니다 — 이러한 방식은

  1. status == "error" — 명시적인 도구 오류 (tool error). 가장 비용이 적게 드는 신호이며, 대부분의 로그에 이미 포함되어 있습니다.
  2. 반복되는 동일한 (tool, tool_args) — 에이전트가 이미 시도했던 것과 바이트 단위로 동일한 인자 (arguments)를 사용하여 동일한 도구를 호출했습니다. 이는 루프 (loop)이거나 정보 이득 (information-gain)이 낮은 재시도이며, 오류가 발생하지 않더라도 유효한 신호입니다.

두 번째 체크 항목에는 제가 첫 실행에서 맞닥뜨렸던 버그가 있는데, 이는 실제 함정이 될 수 있으므로 솔직하게 밝힐 가치가 있습니다. 제 첫 번째 버전은 tool 필드가 있는 모든 단계 (step)를 기준으로 루프를 식별했습니다. 하지만 도구 결과 (role: "tool") 또한 tool 필드를 포함하고 있으며, tool_args가 없는 동일한 도구의 두 결과는 동일한 빈 키 (empty key)를 생성하여, 완벽하게 정상적인 추적 (trace)에서도 잘못된 루프 신호를 발생시켰습니다. 해결책은 if "tool_args" in s 가드 (guard)를 추가하는 것이었습니다. 오직 도구 '호출' (call)만이 루프가 될 수 있으며, '결과' (result)는 결코 루프가 될 수 없습니다. 이 한 줄을 추가한 후, 제 클린 피스처 (clean fixture)는 잘못된 45.4%에서 정확한 0.0%로 바뀌었습니다. 탐지 로직 (detection logic)은 이러한 도구들이 당신에게 조용히 거짓말을 하는 바로 그 지점이기에, 저는 피스처를 적대적 (adversarial)으로 유지합니다.

첫 번째 신호 인덱스 (signal index)를 확보하고 나면, 나머지는 산술 연산입니다. 해당 인덱스부터 그 이후의 토큰 (tokens)을 모두 더하고, 전체 토큰으로 나눈 뒤, 설정 가능한 비율로 달러로 변환합니다 (기본값인 $5/1M은 실제 업체 견적이 아니라 --price-per-1m으로 덮어쓸 수 있는 자리 표시자입니다). 마지막으로 임계값 (threshold, 기본값 0.30)에 따라 종료 코드 (exit code)를 설정합니다.

실제 실행

저는 이를 Python 3.13.5 환경에서 실제 tiktoken o200k_base를 사용하여 라이브로 실행했습니다. 두 가지 피스처를 사용했습니다: 클린 선형 실행 (clean linear run)과, 에이전트가 실패하는 동일한 호출을 7번 재시도하는 루프 실행 (loopy run)입니다. 편집 없이 출력된 결과 그대로를 보여드립니다:

$ python3 waste_probe.py trace_clean.json
trace: trace_clean.json  (tiktoken o200k_base (exact))
steps: 8   total tokens: 326
...

왼쪽에서 오른쪽으로 그 루프 형태의 곡선을 읽어보세요. 실행은 정상적으로 상승하다가 (37 58 51 77), 3단계에서 첫 번째 tool-error가 발생합니다. 그 이후의 리듬을 살펴보세요: 51 74 51 72 51 78 51 79 51 72. 에이전트는 동일한 요청을 다시 보내고, 파싱할 수 없는 동일한 200-OK 페이로드(payload)를 돌려받으며, 다음번에는 왜 성공할 것인지에 대해 새로운 이론을 서술하고, 이를 반복합니다. 순수하게 재유도(re-derivation)만 수행하는 5번의 왕복이 이어집니다. 853개 토큰 중 707개, 즉 82.9%가 첫 번째 신호 이후에 발생했습니다.

그 82.9%는 제가 만든 테스트 환경(fixture)에서의 수치입니다. 논문에 나온 58.1%와는 다릅니다. 논문은 여러 실행에 걸친 실제 GAIA 트레이스(traces)를 측정하여 _평균_을 보고했습니다. 저는 메커니즘을 명확하게 보여주기 위해 의도적으로 루프가 발생하는 트레이스를 하나 구축했으며, 단일하게 조작된 트레이스는 집단 평균보다 높게 나타날 수밖에 없습니다. 현상은 동일하지만, 분모가 완전히 다른 두 가지 사례입니다. 만약 제가 하나를 다른 것으로 인용한다면 바로 지적해 주십시오.

금액에 대한 참고 사항: $0.003535가 매우 작은 이유는 테스트 환경이 작기 때문입니다. 이는 토큰 수와 사용자가 입력하는 실제 요율에 따라 선형적으로 확장됩니다. 중요한 신호는 비율(ratio)이며, 달러 수치는 재무 담당자가 읽을 수 있는 단위로 변환해 줄 뿐입니다. 본인의 요율로 직접 실행해 보세요:

$ python3 waste_probe.py trace_loopy.json --price-per-1m=15.0
$ wasted after signal: $0.010605  (at $15.0/1M tok)

그리고 게이트(gate)는 실제로 작동합니다. 인자가 없으면 종료 코드(exit code) 2를 반환하고, clean은 0을, loopy는 1을 반환하므로 CI 작업에서 이를 기준으로 분기(branch)할 수 있습니다. 출력 또한 결정론적(deterministic)입니다. loopy 실행을 두 번 해시(hash)해 보았는데 두 번 모두 동일한 sha256 값이 나왔습니다. 시계, 무작위성, 네트워크가 개입되지 않습니다. 동일한 트레이스가 입력되면 매번 동일한 숫자가 출력됩니다. 이것만이 CI 게이트를 가치 있게 만드는 유일한 방법입니다.

월요일에 이를 어떻게 활용할 것인가

노력의 정도에 따라 대략적인 순서대로 세 가지 용도를 제안합니다.

1. 먼저 자신의 비율을 측정하세요. 이미 보유하고 있는 실제 실패한 트레이스(trace) 하나를 가져와서 [{role, content, tool, tool_args, status}] 형태로 변환한 뒤, 프로브(probe)를 실행하고 백분율을 읽어보세요. 그것이 58%일 것이라거나 83%일 것이라고 가정하지 마세요. 당신의 것을 직접 측정하세요. 이 도구가 키(key)가 필요 없고 오프라인 방식인 이유는, 누군가의 허락을 구할 필요 없이 실제 운영 로그(production log)에서 바로 그렇게 할 수 있도록 하기 위함입니다. 이는 무언가를 결정하기 전에 MCP 서버의 도구별 토큰 세금(token tax)을 측정하는 것과 같은 '선(先) 측정' 습관과 맥을 같이 합니다.

2. 수집된 트레이스에 대한 CI 게이트(gate)로 연결하세요. 에이전트 트레이스를 저장하고 있다면(운영 환경의 모든 것은 저장해야 합니다), 이를 수집하는 파이프라인에 waste_probe.py를 집어넣으세요. exit 1은 "이 실행은 이미 실패한 후 토큰의 30% 이상을 낭비했다"는 것을 의미하며, 이는 빌드를 실패(red build)로 처리할 만한 회귀(regression) 사항입니다. --threshold 값을 실제 상황에 맞게 조정하세요. 0.30은 시작점일 뿐 법이 아닙니다. 저는 의도적으로 이 값을 선택했으며, 그 이유는 다음과 같습니다.

3. 조기 종료(early stopping)를 향한 루프를 완성하세요. 58.1%라는 수치를 제시한 동일한 논문에서는 작은 파일럿 테스트를 진행했는데, 조기 경고에 따라 조치했을 때 경고 이후의 토큰 비율이 0.638에서 0.304로 감소했습니다. 이것이 바로 전/후 비교를 통해 보여주는 게임의 핵심입니다. 신호를 감지하고, 그 근처에서 멈추면, 꼬리 부분(tail)이 무너집니다. 프로브는 측정의 절반을 담당합니다. 나머지 절반은 에이전트의 재시도 로직(retry logic)이 반복되는 동일한 호출을 일시적인(transient) 오류가 아닌 종료(terminal) 상황으로 실제로 처리하는 것입니다. 이는 앞서 언급한 루프형 피스처(loopy fixture)로 돌아가 보면, 에이전트가 스스로 해결하지 못한 바로 그 버그와 정확히 일치합니다.

이것이 아닌 것

이 도구는 의도적으로 작게 만들어졌으므로, 한계점에 대해 솔직하게 밝힙니다:

  • 런타임 차단 도구(Runtime blocker)가 아닙니다. 이 도구는 완료된 트레이스(Traces)를 읽습니다. 실행 중인 비용 과다 발생 상황을 중간에 멈추지는 않습니다. 그 역할은 지출 한도(Spend cap)의 몫입니다.
  • 모든 낭비를 탐지하는 도구는 아닙니다. 이 도구는 두 가지 신호, 즉 명시적인 도구 오류(Tool errors)와 바이트 단위로 동일한 반복 호출(Byte-identical repeated calls)을 포착합니다. 의미상으로는 무의미하지만 텍스트상으로 다른 재시도(Retry)는 이 도구를 그냥 지나칩니다. 오류가 전혀 없는, 틀렸지만 확신에 찬 답변도 마찬가지입니다. 반복 호출 체크는 설계상 완전 일치(Exact-match)를 기준으로 하므로, 정밀도(Precision)는 높지만 의도적으로 재현율(Recall)은 낮게 설정되었습니다.
  • 평가(Eval)의 대체재가 아닙니다. 낭비 비율(Waste ratio)은 신호가 발생한 후 얼마나 많은 비용이 소모되었는지를 알려줄 뿐, 최종 답변이 정답이었는지는 결코 알려주지 않습니다. 두 가지 모두 여전히 필요합니다.
  • 임계값(Threshold)은 보정(Calibrate)하기 전까지는 추측일 뿐입니다. 0.30이라는 수치는 논문에서 개입 후 나타난 0.304를 반영한 것이며, 이는 기분 좋은 우연일 뿐 그 이상은 아닙니다. 당신의 수치는 당신만의 수치입니다.

다시 한번, 검증 가능한(Falsifiable) 논지를 제시하자면: 실패한 실행에서 값비싼 토큰은 첫 번째 감지 가능한 신호 '이후'에 발생하며, 그 이전에는 발생하지 않습니다. 경고가 발생한 실패한 실행의 발표된 평균은 58.1%입니다. 제가 만든 루프 고정 장치(Loopy fixture)는 82.9%를 기록했습니다. 이제 여러분의 수치를 확인해 보세요. 만약 결과가 지속적으로 30% 미만으로 나온다면, 여러분은 대부분의 경우보다 더 건강한 중단 조건(Stop condition)을 갖춘 것이며, 저는 여러분이 그것을 어떻게 구축했는지 진심으로 듣고 싶습니다.

여러분의 트레이스에서는 어떤 신호가 먼저 트리거되나요 — 오류인가요, 아니면 루프인가요? 그리고 무엇이 결국 여러분의 에이전트로 하여금 동일한 반복 호출을 일시적인 것이 아닌 종료(Terminal) 상태로 취급하게 만드나요? 댓글로 남겨주세요. '재시도가 종료 조건이 되는가'라는 질문은 저 또한 여전히 고민하고 있는 문제입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0