Split View: AI 에이전트는 프로덕션에서 어떻게 실패하는가 — 14가지 실패 모드, 그리고 재시도가 안전하지 않은 이유
AI 에이전트는 프로덕션에서 어떻게 실패하는가 — 14가지 실패 모드, 그리고 재시도가 안전하지 않은 이유
- 들어가며 — 프로덕션 에이전트의 진짜 질문 세 가지
- 에이전트는 실제로 어디서 실패하는가 — MAST의 14가지 모드
- 각 실패 범주는 무엇을 청구하는가
- 왜 재시도가 안전하지 않은가
- MCP는 멱등성을 보장해 주는가 — 힌트는 있고 메커니즘은 없다
- 그럼 업계 표준은 어디까지 왔나 — 5년 반째 드래프트
- 에이전트 비용을 어떻게 재는가 — OTel에는 비용 지표가 없다
- 토큰을 그냥 더하면 왜 틀리는가
- 무엇을 계측해야 하는가 — 그리고 옵트인 함정
- 고치면 실제로 나아지는가 — 정직한 개입 결과
- 그래서, 무엇을 해야 하나 — 결정 규칙
- 언제 이 모든 걸 하지 말아야 하나
- 마치며
- 참고 자료
들어가며 — 프로덕션 에이전트의 진짜 질문 세 가지
데모에서 잘 도는 에이전트를 프로덕션에 올리면 질문이 바뀝니다. "이게 되나?"에서 "이게 왜 안 됐고, 얼마를 태웠고, 다시 시도해도 되나?"로요. 이 글은 그 세 질문에 순서대로 답합니다.
먼저 결론부터. 실패는 대부분 모델이 아니라 시스템 설계에서 납니다. UC 버클리 연구진이 멀티 에이전트 시스템 실행 트레이스 1642건을 분류한 결과, 실패의 44.2%가 시스템 설계 이슈였고 32.3%가 에이전트 간 어긋남, 23.5%가 태스크 검증 실패였습니다. 그리고 가장 흔한 실패 모드 두 개는 단계 반복(15.7%)과 종료 조건 미인지(12.4%) — 합치면 전체의 28.1%입니다. 이 둘은 그냥 "실패"가 아닙니다. 에이전트가 같은 일을 반복하고 멈출 줄 모른다는 뜻이고, 그건 토큰 청구서인 동시에 부작용이 두 번 일어난다는 뜻입니다.
여기서 세 번째 질문이 나옵니다. 비결정적 호출자(LLM)가 진짜 부작용(결제, 메일 발송, 티켓 생성)을 굴릴 때, 재시도는 안전한가? 답은 "당신이 직접 안전하게 만들지 않으면 아니다"입니다. 에이전트가 도구를 부르는 표준 프로토콜인 MCP에는 멱등성을 선언하는 힌트는 있지만 멱등성을 보장하는 메커니즘이 없습니다. 그리고 이 문제의 업계 표준이라 할 IETF의 Idempotency-Key 헤더 드래프트는 2020년에 시작해서 아직 RFC가 아닙니다 — 2026년 4월에 만료된 상태입니다.
에이전트는 실제로 어디서 실패하는가 — MAST의 14가지 모드
가장 인용할 만한 자료는 MAST(Multi-Agent System Failure Taxonomy)입니다. UC 버클리와 Intesa Sanpaolo 연구진(Cemri, Pan, Yang 등, arXiv:2503.13657)이 NeurIPS 2025 Datasets & Benchmarks 트랙에 낸 논문으로, 멀티 에이전트 시스템 실패를 경험적으로 분류한 첫 시도입니다.
방법론부터 봅시다. 연구진은 5개 오픈소스 프레임워크의 트레이스 150여 건(한 건당 평균 1만 5천 줄이 넘습니다)을 근거 이론(Grounded Theory)으로 분석해 분류 체계를 만들었고, 전문 어노테이터 6명이 참여했습니다. 어노테이터 3명이 트레이스 15건을 독립적으로 라벨링해 어노테이터 간 일치도 카파 0.88을 확보한 뒤, 그 정의를 기준으로 OpenAI o1 기반 LLM-as-a-judge 파이프라인을 만들어 전체 1642건을 라벨링했습니다.
분류 결과는 이렇습니다. 괄호 안은 전체 1642건 대비 각 모드의 출현 비율입니다.
[범주 1] 시스템 설계 이슈 (System Design Issues) 44.2%
1.1 태스크 명세 위반 (Disobey Task Specification) 11.8%
1.2 역할 명세 위반 (Disobey Role Specification) 1.50%
1.3 단계 반복 (Step Repetition) 15.7% <- 최다
1.4 대화 히스토리 상실 (Loss of Conversation History) 2.80%
1.5 종료 조건 미인지 (Unaware of Termination Conditions) 12.4%
[범주 2] 에이전트 간 어긋남 (Inter-Agent Misalignment) 32.3%
2.1 대화 리셋 (Conversation Reset) 2.20%
2.2 확인 질문 실패 (Fail to Ask for Clarification) 6.80%
2.3 태스크 탈선 (Task Derailment) 7.40%
2.4 정보 은닉 (Information Withholding) 0.80%
2.5 타 에이전트 입력 무시 (Ignored Other Agent's Input) 1.90%
2.6 추론-행동 불일치 (Reasoning-Action Mismatch) 13.2%
[범주 3] 태스크 검증 (Task Verification) 23.5%
3.1 조기 종료 (Premature Termination) 6.20%
3.2 검증 없음/불완전 (No or Incomplete Verification) 8.20%
3.3 잘못된 검증 (Incorrect Verification) 9.10%
숫자가 맞아떨어지는지 확인해 두면 좋습니다. 11.8 + 1.5 + 15.7 + 2.8 + 12.4 = 44.2, 2.2 + 6.8 + 7.4 + 0.8 + 1.9 + 13.2 = 32.3, 6.2 + 8.2 + 9.1 = 23.5. 세 범주를 더하면 정확히 100%입니다.
여기서 한 가지 경고. 이 논문을 요약한 2차 자료 중에는 "42%가 명세 문제, 37%가 조율 붕괴, 21%가 검증 부실"이라고 적은 것들이 돌아다니는데, 세 숫자 모두 논문에 없습니다. 범주 이름도 다릅니다 — 논문의 첫 범주는 "명세 이슈"가 아니라 "시스템 설계 이슈"입니다(초기 버전에서 이름이 바뀐 흔적으로 보입니다). 인용하실 거면 논문 Figure 1을 직접 보시길 권합니다.
기저 실패율도 새겨둘 만합니다. 논문이 평가한 7개 SOTA 오픈소스 시스템의 실패율은 41%에서 86.7% 사이였습니다. 가장 나빴던 건 AppWorld의 Test-C(86.7% 실패), 가장 나았던 건 AG2의 OlympiadBench(41% 실패)였습니다. 프로덕션에 올리는 물건의 기준선이 이렇다는 뜻입니다.
각 실패 범주는 무엇을 청구하는가
먼저 정직하게 말해 둘 것이 있습니다. 모드별로 "이 실패는 얼마"라고 적은 공개 수치는 없습니다. MAST는 출현 빈도를 셌지 비용을 재지 않았고, 제가 찾은 어떤 1차 자료도 실패 모드별 금액을 공개하지 않았습니다. 그러니 여기서 숫자를 지어내는 대신, 각 범주가 청구하는 비용의 종류를 구분하겠습니다. 이 구분이 실무에서는 금액보다 쓸모 있습니다 — 종류가 다르면 대응이 다르기 때문입니다.
범주 1(시스템 설계, 44.2%)은 토큰으로 청구됩니다. 단계 반복(15.7%)과 종료 조건 미인지(12.4%)가 이 범주의 절반 이상을 차지하는데, 둘 다 증상이 "에이전트가 계속 돈다"입니다. 루프 한 바퀴마다 컨텍스트 전체가 다시 모델에 들어가므로 비용은 선형이 아니라 그보다 가파르게 붙습니다. 이 범주의 좋은 점은 가장 싸게 잡힌다는 것입니다 — 턴 수 상한, 예산 상한, 반복 감지는 모델을 건드리지 않고도 넣을 수 있습니다.
범주 2(에이전트 간 어긋남, 32.3%)는 잘못된 부작용으로 청구됩니다. 이 범주의 최다 모드는 추론-행동 불일치(13.2%)로, 논문의 정의대로라면 에이전트의 추론과 실제 행동이 어긋나는 경우입니다. 이건 토큰 문제가 아닙니다. 에이전트가 "A를 하겠다"고 말하고 B를 실행하면, 청구서는 B가 건드린 시스템에 남습니다. 태스크 탈선(7.40%)도 같은 성격입니다.
범주 3(태스크 검증, 23.5%)은 조용히 틀린 산출물로 청구됩니다. 검증 없음/불완전(8.20%)과 잘못된 검증(9.10%)을 합치면 17.3%인데, 이 둘의 공통점은 실패가 실패로 보이지 않는다는 것입니다. 논문이 든 예가 인상적입니다 — ChatDev가 만든 체스 프로그램이 피상적인 테스트는 통과했다는 것이죠. 이 범주가 가장 늦게 발견되고, 그래서 대체로 가장 비쌉니다.
정리하면 이렇습니다.
범주 1 (44.2%) -> 토큰 청구서 -> 가장 싸게 잡힘 (상한·감지)
범주 2 (32.3%) -> 잘못된 부작용 -> 멱등성·권한으로 방어
범주 3 (23.5%) -> 조용히 틀린 결과 -> 가장 늦게 발견, 가장 비쌈
왜 재시도가 안전하지 않은가
이제 핵심입니다. 범주 1의 두 최다 모드(반복·미종료)와 범주 2의 최다 모드(추론-행동 불일치)를 겹쳐 놓으면, 하나의 문장이 나옵니다. 비결정적 호출자가 실제 부작용을 일으키는 도구를 반복해서 부른다.
전통적인 분산 시스템에서 이건 낯선 문제가 아닙니다. 네트워크가 끊기면 클라이언트는 요청이 처리됐는지 모르고, 재시도하면 중복이 생깁니다. 그래서 우리는 멱등성 키를 씁니다. 그런데 에이전트에는 문제가 하나 더 있습니다 — 전통적 클라이언트는 재시도할지를 코드가 결정하지만, 에이전트는 모델이 결정합니다. 모델은 도구가 실패했다고 판단하면(혹은 실패했다고 오판하면) 그냥 다시 부릅니다. 재시도 정책이 프롬프트 안에 있는 셈이고, 그건 정책이 아니라 확률입니다.
여기서 아주 중요한 구분이 하나 있습니다. 재시도가 위험한 건 도구가 실패했을 때만이 아닙니다. 도구가 성공했는데 응답이 유실됐을 때가 더 위험합니다. 이때 모델이 보는 건 "결과 없음"이고, 모델의 합리적 행동은 재시도입니다. 그리고 부작용은 이미 일어났습니다.
MCP는 멱등성을 보장해 주는가 — 힌트는 있고 메커니즘은 없다
그럼 에이전트가 도구를 부르는 표준 프로토콜인 MCP는 이 문제를 어떻게 다룰까요. 현재 최신 스펙 리비전(2025-11-25)의 스키마를 직접 열어 보면 답이 명확합니다.
MCP에는 도구 애노테이션이 있고, 그중 하나가 멱등성 힌트입니다. 스키마의 정의를 그대로 옮기면, 참이면 "같은 인자로 도구를 반복 호출해도 환경에 추가적인 영향이 없다"는 뜻입니다. 네 가지 힌트의 기본값은 이렇습니다.
readOnlyHint 기본값 false (도구가 환경을 수정한다고 가정)
destructiveHint 기본값 true (수정이 파괴적이라고 가정)
idempotentHint 기본값 false (반복 호출에 추가 영향이 있다고 가정)
openWorldHint 기본값 true (외부 세계와 상호작용한다고 가정)
기본값이 전부 비관적인 쪽으로 잡혀 있다는 데 주목할 만합니다. 애노테이션이 없으면 모든 도구는 파괴적이고 멱등하지 않은 쓰기 작업으로 취급해야 한다는 뜻입니다. 이건 좋은 설계입니다.
그런데 스펙은 이 힌트들에 대해 이렇게 못 박습니다(스키마 주석 원문의 번역입니다).
ToolAnnotations의 모든 속성은 힌트다. 이것들이 도구 동작을 충실하게 기술한다는 보장은 없다(
title같은 서술적 속성을 포함해서). 클라이언트는 신뢰할 수 없는 서버로부터 받은 ToolAnnotations에 근거해 도구 사용을 결정해서는 안 된다.
여기서 진짜 문제가 드러납니다. 2573줄짜리 스키마 전체에서 idempotentHint는 선언되는 그 한 곳에만 등장합니다. 그리고 "retry"라는 단어는 스키마에 한 번도 나오지 않습니다. 멱등성 키도, 요청 중복 제거 메커니즘도, 재시도 시맨틱도 없습니다.
즉 MCP는 멱등성에 대해 어휘는 주지만 도구는 주지 않습니다. 서버가 "나는 멱등해요"라고 주장할 수는 있는데, 그 주장은 (a) 강제되지 않고 (b) 신뢰할 수 없는 서버에서 오면 믿지 말라고 스펙이 직접 경고하며 (c) 멱등하지 않은 도구를 재시도 안전하게 만들 방법은 프로토콜에 아예 없습니다.
트랜스포트 계층에 재전송이 있지 않냐고 물을 수 있습니다. Streamable HTTP 스펙에는 재개(resumability)와 재전송(redelivery)이 있습니다 — 서버가 SSE 이벤트에 id를 붙이고, 클라이언트가 끊긴 뒤 Last-Event-ID 헤더로 GET을 보내면 서버가 그 지점 이후 메시지를 재생할 수 있습니다. 하지만 이건 서버에서 클라이언트로 가는 메시지를 끊어진 그 스트림에 한해 재생하는 것이고(스펙은 다른 스트림의 메시지를 재생해서는 안 된다고 명시합니다), 스트림 내 커서일 뿐입니다. 클라이언트가 보낸 도구 호출을 중복 제거해 주는 장치가 아닙니다. MCP 스펙 자체의 방향성에 대해서는 MCP가 세션을 걷어낸다 — 스테이트리스 코어 읽기 편에서 따로 다룹니다.
그럼 업계 표준은 어디까지 왔나 — 5년 반째 드래프트
"그냥 멱등성 키 쓰면 되잖아"가 자연스러운 반응입니다. 맞습니다. 그런데 놀랍게도 멱등성 키에는 표준이 없습니다.
IETF HTTPAPI 워킹그룹의 Idempotency-Key HTTP 헤더 드래프트 이력을 데이터트래커에서 직접 확인하면 이렇습니다.
draft-idempotency-header-00 2020-11-17 (개인 드래프트)
draft-ietf-httpapi-idempotency-key-header-00 2021-07-01 (WG 채택)
-01 2022-05-08
-02 2022-11-09
-03 2023-07-04
-04 2023-11-16
-05 2024-05-27
-06 2025-02-24
-07 2025-10-15 (최신)
현재 상태: Expired (2026-04-18 만료)
IESG 상태: "I-D Exists" — IESG가 처리를 시작한 적 없음
2020년 11월에 시작해 8번 개정됐고, 최신 개정판은 2025년 10월에 올라와 인터넷 드래프트의 6개월 수명 규칙대로 2026년 4월 18일에 만료됐습니다. IESG 상태는 "I-D Exists" — 데이터트래커의 설명 그대로 "IESG가 이 드래프트 처리를 시작하지 않았거나, 출판 없이 처리를 중단했음"입니다. 5년 반 넘게 RFC가 되지 못했습니다.
그래서 현실에서 "멱등성 키"는 표준이 아니라 관행입니다. 그 관행을 가장 잘 문서화한 곳이 Stripe이고, 실제로 위 IETF 드래프트가 성문화하려던 것도 대체로 이 관행입니다. Stripe 문서에서 확인되는 동작은 이렇습니다.
- 주어진 멱등성 키에 대해 첫 요청의 상태 코드와 응답 본문을 저장하고, 성공이든 실패든 저장한다.
- 같은 키로 오는 후속 요청은 같은 결과를 돌려준다 — 500 에러까지 그대로 재생한다.
- 멱등성 계층은 들어온 파라미터를 원 요청과 비교해서, 다르면 에러를 낸다.
- 키는 최소 24시간이 지나면 정리(prune)될 수 있고, 정리된 뒤 같은 키를 재사용하면 새 요청이 생성된다.
- 결과는 엔드포인트 실행이 시작된 뒤에만 저장된다. 파라미터 검증 실패나 동시 실행 충돌은 저장되지 않으므로 재시도할 수 있다.
에이전트 관점에서 두 줄이 특히 아픕니다. 하나는 500까지 재생한다는 것 — 멱등 재시도는 "다시 시도"가 아니라 "원래 결과를 다시 보여주기"입니다. 모델이 실패를 보고 재시도하면 실패를 다시 봅니다. 이건 버그가 아니라 설계이고, 모델이 이걸 이해하지 못하면 무한 루프에 빠집니다(그리고 그게 바로 실패 모드 1.3 단계 반복입니다). 다른 하나는 24시간입니다. 며칠에 걸쳐 도는 장기 실행 에이전트가 어제의 키로 재시도하면, 그건 재시도가 아니라 새 부작용입니다.
에이전트 비용을 어떻게 재는가 — OTel에는 비용 지표가 없다
이제 관측 가능성입니다. 표준을 따르고 싶다면 OpenTelemetry의 GenAI 시맨틱 컨벤션을 보게 되는데, 현재 상태를 정확히 알고 시작하는 게 좋습니다.
첫째, GenAI 컨벤션은 이사했습니다. 원래 open-telemetry/semantic-conventions 저장소의 docs/gen-ai에 있었지만, 지금 그 경로의 파일들은 "이 페이지는 이동했으며 더 이상 이 저장소에서 관리되지 않습니다"라는 안내만 남아 있습니다. 새 집은 open-telemetry/semantic-conventions-genai입니다.
둘째, 그 새 저장소는 2026년 5월 5일에 만들어졌고, 아직 릴리스가 하나도 없습니다. (본체 시맨틱 컨벤션의 최신 릴리스는 v1.43.0, 2026년 7월 3일입니다.) 그리고 에이전트 스팬 문서와 MCP 문서 모두 문서 상태가 "Development"입니다. 개발이 멈춘 건 아닙니다 — 마지막 푸시가 이틀 전입니다. 다만 당신이 "표준"이라 부르며 대시보드를 세우는 그 물건은 아직 릴리스된 적 없는 개발 중 스펙이라는 뜻입니다.
셋째, 그리고 이게 실무에서 제일 중요한데 — 비용 지표가 없습니다. GenAI 시맨틱 컨벤션의 메트릭 문서와 속성 레지스트리 전체를 cost, price, usd, dollar로 검색하면 결과가 0건입니다. 정의된 메트릭은 이게 전부입니다.
gen_ai.client.token.usage
gen_ai.client.operation.duration
gen_ai.client.operation.time_to_first_chunk
gen_ai.client.operation.time_per_output_chunk
gen_ai.server.request.duration
gen_ai.server.time_to_first_token
gen_ai.server.time_per_output_token
표준은 토큰을 줍니다. 돈은 당신 몫입니다. 토큰을 금액으로 바꾸려면 모델별 단가표가 필요하고, 그 표는 벤더가 바꿉니다. 벤더들의 비용 대시보드가 저마다 독자 규격이고 서로 숫자가 안 맞는 이유가 여기 있습니다.
토큰을 그냥 더하면 왜 틀리는가
비용을 직접 계산하기로 했다면, 반드시 알아야 할 함정이 하나 있습니다. 속성 레지스트리에 정의된 토큰 속성은 다섯 개입니다.
gen_ai.usage.input_tokens
gen_ai.usage.output_tokens
gen_ai.usage.cache_creation.input_tokens
gen_ai.usage.cache_read.input_tokens
gen_ai.usage.reasoning.output_tokens
문제는 이것들이 평평하지 않고 중첩되어 있다는 점입니다. 레지스트리의 주석이 명시합니다 — 캐시 생성 토큰과 캐시 읽기 토큰은 gen_ai.usage.input_tokens에 포함되어야 하고(SHOULD), 입력 토큰은 캐시된 토큰을 포함해 모든 종류의 입력 토큰을 포함해야 합니다. 추론 토큰도 마찬가지로 gen_ai.usage.output_tokens에 포함되어야 합니다.
그래서 이런 계산은 틀립니다.
잘못된 계산:
비용 = input_tokens x 입력단가 + output_tokens x 출력단가
왜 틀리나:
input_tokens 안에 cache_read.input_tokens 가 이미 들어 있음
캐시 읽기는 신규 입력보다 싸게 과금됨 (그게 캐시를 쓰는 이유)
-> 캐시가 잘 들을수록, 당신의 계산은 실제보다 비싸게 나옴
올바른 방향:
신규입력 = input_tokens - cache_read - cache_creation
비용 = 신규입력 x 입력단가
+ cache_read x 캐시읽기단가
+ cache_creation x 캐시쓰기단가
+ output_tokens x 출력단가
(단가는 벤더·모델마다 다르므로 여기에 숫자를 적지 않습니다)
에이전트에서 이 오차는 작지 않습니다. 에이전트 루프는 매 턴 같은 시스템 프롬프트와 도구 정의를 다시 보내므로 캐시 적중률이 구조적으로 높고, 그래서 input_tokens 중 캐시 읽기 비중이 큽니다. 순진하게 더하면 캐싱이 잘 될수록 장부상 비용이 부풀려집니다. 캐싱의 비용 효과를 대시보드에서 확인하려다 정반대 결론에 도달할 수 있습니다.
한 겹 더 있습니다. 위 다섯 속성은 스팬 속성이고, 메트릭 gen_ai.client.token.usage의 차원인 gen_ai.token.type의 well-known 값은 input과 output 둘뿐입니다. 즉 집계된 메트릭만 보고서는 캐시된 토큰과 신규 토큰을 구분할 수 없습니다. 정확한 비용을 내려면 스팬 단위로 내려가야 합니다. (커스텀 값을 쓸 수는 있지만, 그 순간 표준이 아니게 됩니다.)
무엇을 계측해야 하는가 — 그리고 옵트인 함정
도구 호출 계측은 execute_tool 스팬으로 합니다. 스팬 이름은 execute_tool 뒤에 도구 이름을 붙이는 형태이고, 속성은 이렇습니다.
gen_ai.operation.name Required (Development)
gen_ai.tool.name Required (Development)
error.type Recommended (Stable)
gen_ai.agent.name Conditionally Req. (Development)
gen_ai.tool.call.id Recommended (Development)
gen_ai.tool.description Recommended (Development)
gen_ai.tool.type Recommended (Development)
gen_ai.tool.call.arguments Opt-In (Development) <- 기본 미수집
gen_ai.tool.call.result Opt-In (Development) <- 기본 미수집
마지막 두 줄이 함정입니다. 에이전트가 무슨 인자로 도구를 불렀고 무엇을 돌려받았는지는 "Opt-In" — 즉 기본적으로 수집되지 않습니다. 그럴 만한 이유가 있습니다(개인정보와 저장 비용). 하지만 결과적으로, 에이전트가 왜 엉뚱한 짓을 했는지 디버깅하는 데 정확히 필요한 그 두 필드가 기본으로 꺼져 있습니다. 범주 2(추론-행동 불일치)를 추적하려면 이걸 켜야 하고, 켜는 순간 트레이스에 민감정보가 들어올 수 있습니다. 공짜 점심이 아닙니다 — 샘플링·마스킹과 함께 의식적으로 결정해야 하는 트레이드오프입니다.
MCP를 쓴다면 하나 더 알아야 합니다. MCP 시맨틱 컨벤션 문서는 HTTP 트레이스 컨텍스트 전파가 HTTP 요청만 덮고 요청/응답 스트림 안에서 오가는 개별 메시지는 덮지 못한다고 지적합니다. 그래서 계측은 MCP 요청의 params._meta 주머니에 컨텍스트를 주입해야 하고(SEP-414가 traceparent, tracestate, baggage 키의 처리를 정의합니다), 수신 측이 그걸 원격 부모로 씁니다. 이걸 안 하면 에이전트 트레이스가 MCP 경계에서 끊깁니다. 로깅·트레이싱 일반론은 LLM 로그와 트레이싱 편을 참고하세요.
고치면 실제로 나아지는가 — 정직한 개입 결과
여기가 이 글에서 가장 정직해야 할 부분입니다. MAST 논문은 분류만 하고 끝내지 않고, 분류를 근거로 실제 개입을 해 봤습니다. 결과(논문 Table 5)는 이렇습니다.
AG2 / GSM-Plus (GPT-4) 기준선 84.75 +- 1.94 | 프롬프트 개선 89.75 +- 1.44 | 토폴로지 변경 85.50 +- 1.18
AG2 / GSM-Plus (GPT-4o) 기준선 84.25 +- 1.86 | 프롬프트 개선 89.00 +- 1.38 | 토폴로지 변경 88.83 +- 1.51
ChatDev / ProgramDev-v0 기준선 25.0 | 프롬프트 개선 34.4 | 토폴로지 변경 40.6
ChatDev / HumanEval 기준선 89.6 | 프롬프트 개선 90.3 | 토폴로지 변경 91.5
널리 인용되는 "+9.4%"와 "+15.6%"가 여기서 나옵니다. ChatDev의 ProgramDev-v0에서 25.0 → 34.4이 +9.4, 25.0 → 40.6이 +15.6입니다. 퍼센트가 아니라 퍼센트 포인트입니다.
그런데 이 숫자를 인용할 거라면 반드시 함께 말해야 할 것이 있습니다.
첫째, ProgramDev-v0는 32개 태스크입니다. 32개에 대한 25.0%는 8개, 34.4%는 11개, 40.6%는 13개입니다(계산이 정확히 맞아떨어집니다). 즉 화제의 "+9.4%p"는 태스크 3개, "+15.6%p"는 태스크 5개를 더 통과했다는 뜻입니다. 작은 표본입니다.
둘째, 최선의 개입 이후에도 성공률은 40.6%입니다. 다시 말해 여전히 약 59%가 실패합니다. 논문의 표현을 그대로 옮기면 "초기 단계의 개입이 성능 향상으로 이어지긴 하지만, 모든 실패 모드가 해소되지는 않으며 태스크 완료율은 여전히 낮아, 더 실질적인 개선이 필요함을 시사한다"입니다.
셋째, 효과가 보편적이지 않습니다. AG2에서 토폴로지 변경은 GPT-4에서 윌콕슨 검정 p값 0.4로 통계적으로 유의하지 않았고, 같은 변경이 GPT-4o에서는 p값 0.03으로 유의했습니다. 논문의 결론은 "이 전략들은 보편적이지 않으며, 그 효과는 기저 LLM 같은 요인에 따라 달라진다"입니다.
넷째, 방향성은 있습니다. 논문은 두 시스템 모두에서 토폴로지 기반 변경이 프롬프트 기반 변경보다 효과적이었다고 보고합니다. 그리고 저자들은 견고한 신뢰성에는 "고립된 수정 이상"이 필요하며 "근본적인 MAS 재설계"를 향해야 한다고 못 박습니다.
마지막으로 이 자료 자체의 한계. 1642건 중 대부분은 저자들이 만든 o1 기반 LLM 어노테이터가 라벨링했습니다. 사람이 검증한 건 훨씬 적은 부분집합입니다(카파 0.88은 트레이스 15건에 대한 어노테이터 3명의 일치도입니다). 저자들 스스로 "MAST가 모든 잠재적 실패 패턴을 포괄한다고 주장하지 않는다"고 적었습니다. 이건 결정적 지도가 아니라, 지금까지 나온 것 중 가장 경험적인 지도입니다.
그래서, 무엇을 해야 하나 — 결정 규칙
위 자료들에서 곧장 따라 나오는 규칙입니다.
1. 도구를 부작용 기준으로 먼저 분류하라. MCP 기본값이 이미 정답을 알려 줍니다 — 애노테이션이 없으면 파괴적·비멱등으로 취급합니다. 읽기 전용 도구와 쓰기 도구를 섞어 놓고 재시도 정책을 하나로 가져가지 마세요.
2. 멱등성은 당신 계층에서 구현하라. MCP의 idempotentHint는 힌트지 보장이 아니고, 신뢰할 수 없는 서버의 애노테이션은 믿지 말라고 스펙이 직접 말합니다. IETF 표준도 없습니다. 그러니 부작용을 일으키는 도구는 당신의 서버 쪽에서 키를 받아 첫 결과를 저장하고 재생하십시오. Stripe의 관행이 검증된 참고 구현입니다 — 결과(에러 포함)를 저장하고, 파라미터 지문을 비교하고, 보존 기간을 명시하는 것까지.
3. 키를 모델이 만들게 하지 마라. 재시도를 결정하는 주체가 비결정적이므로, 멱등성 키는 결정적인 곳 — 오케스트레이터의 스텝 ID나 태스크 ID — 에서 파생시켜야 합니다. 모델이 매번 새 키를 지어내면 멱등성 키는 장식입니다.
4. 상한을 예산으로 걸어라. 실패의 28.1%가 반복과 미종료라면, 턴 상한과 토큰 예산은 최적화가 아니라 차단기입니다. 가장 흔한 실패 범주를 가장 싸게 막는 방법입니다.
5. 비용은 스팬에서 계산하라. 메트릭만으로는 캐시 토큰을 분리할 수 없습니다. 그리고 토큰을 그냥 더하지 마세요 — 캐시 읽기는 이미 input_tokens 안에 있습니다.
6. 인자와 결과 수집을 의식적으로 결정하라. 기본은 꺼짐입니다. 켜지 않으면 범주 2를 디버깅할 수 없고, 켜면 민감정보가 트레이스에 들어옵니다. 샘플링과 마스킹을 함께 설계하십시오.
언제 이 모든 걸 하지 말아야 하나
정직한 반대편입니다. 위의 대부분은 에이전트가 진짜 부작용을 굴릴 때만 값을 합니다.
- 읽기 전용 에이전트라면 멱등성 인프라는 과잉입니다. 검색·요약·분석만 하는 에이전트에서 중복 호출은 돈만 더 쓸 뿐 상태를 망가뜨리지 않습니다. 상한만 걸고 넘어가세요.
- 사람이 모든 쓰기를 승인한다면 재시도 위험은 대부분 사라집니다. 승인 게이트는 멱등성 계층보다 싸게 구현되고, 초기 단계에서는 대체로 더 낫습니다.
- 워크플로로 충분하다면 에이전트를 쓰지 마세요. MAST의 실패 대부분은 시스템 설계 이슈이고, 그건 "결정을 모델에게 넘긴 만큼" 생깁니다. 경로가 정해져 있다면 코드로 짜세요 — 효과적인 AI 에이전트 만들기 편에서 다룬 워크플로/에이전트 구분이 여기서 그대로 유효합니다.
- 단일 에이전트로 되면 멀티 에이전트로 가지 마세요. MAST 실패의 32.3%는 에이전트 사이에서 났습니다. 에이전트를 하나 더 붙이는 순간 그 32.3% 표면적을 새로 삽니다.
마치며
정리하면 이렇습니다. 에이전트 실패는 모델의 실패가 아니라 대체로 시스템 설계의 실패입니다(MAST 기준 44.2%). 가장 흔한 두 모드는 반복과 미종료이고(합쳐서 28.1%), 이 둘은 토큰을 태우는 동시에 부작용을 중복시킵니다. 그리고 그 중복을 막아 줄 프로토콜 차원의 장치는 없습니다 — MCP는 멱등성 힌트만 주고, IETF의 멱등성 키 표준은 5년 반째 드래프트이며 지금 만료 상태입니다. 관측 가능성 쪽도 비슷합니다 — OTel GenAI 컨벤션은 릴리스도 없는 개발 중 스펙이고, 비용 지표가 아예 없으며, 디버깅에 필요한 인자·결과는 기본으로 꺼져 있습니다.
이 목록은 비관하라는 뜻이 아니라, 어디를 당신이 직접 메워야 하는지의 지도입니다. 표준이 채워 줄 거라 기대하고 비워 둔 칸이 바로 프로덕션에서 청구서가 날아오는 칸입니다.
그리고 마지막으로, 개입 결과를 다시 봅시다. 최선의 구조적 개입 뒤에도 코드 생성 성공률은 25.0%에서 40.6%였습니다 — 여전히 절반 넘게 실패합니다. 프로덕션 에이전트를 운영한다는 건 실패를 없애는 일이 아니라, 실패의 비용을 아는 일에 가깝습니다. 어떤 실패가 토큰만 태우고, 어떤 실패가 상태를 망가뜨리고, 어떤 실패가 조용히 틀린 답을 내보내는지. 그 셋을 구분하는 순간부터가 운영입니다. 에이전트를 무엇으로 어떻게 측정할지의 문제는 시뮬레이션된 고객은 절대 떠나지 않는다 편에서, 에이전트를 격리해 굴리는 문제는 Lambda 마이크로VM 에이전트 샌드박스 편에서 이어집니다.
참고 자료
- Why Do Multi-Agent LLM Systems Fail? (Cemri 등, arXiv:2503.13657, NeurIPS 2025 D&B) — MAST 분류 체계, 14가지 실패 모드, Figure 1과 Table 5
- MAST 공식 저장소 / MAST-Data 데이터셋
- MCP 스펙 2025-11-25 스키마 — ToolAnnotations 정의
- MCP 스펙 — 트랜스포트(재개와 재전송)
- The Idempotency-Key HTTP Header Field — IETF 데이터트래커(현재 Expired)
- Stripe API — Idempotent requests
- OpenTelemetry GenAI 시맨틱 컨벤션 저장소(이전한 새 저장소)
- GenAI 에이전트 스팬 컨벤션(Status: Development)
- GenAI 속성 레지스트리 — 토큰 사용량 속성과 중첩 규칙
- MCP 시맨틱 컨벤션 — params._meta 컨텍스트 전파
- 효과적인 AI 에이전트 만들기 — 워크플로와 에이전트의 구분(관련 글)
- MCP가 세션을 걷어낸다 — 스테이트리스 코어(관련 글)
How AI Agents Fail in Production — 14 Failure Modes, and Why Retrying Is Not Safe
- Introduction — The Three Real Questions About Production Agents
- Where Agents Actually Fail — MAST's 14 Modes
- What Each Failure Category Bills You For
- Why Retrying Isn't Safe
- Does MCP Guarantee Idempotency? — A Hint, No Mechanism
- So How Far Has the Industry Standard Come? — A Draft for Five and a Half Years
- How Do You Measure Agent Cost? — OTel Has No Cost Metric
- Why Naively Summing Tokens Is Wrong
- What to Instrument — and the Opt-In Trap
- Does Fixing It Actually Help? — Honest Intervention Results
- So What Should You Do? — Decision Rules
- When Not to Do Any of This
- Closing
- References
Introduction — The Three Real Questions About Production Agents
An agent that runs fine in a demo changes the questions the moment it goes to production. From "does this work?" to "why didn't this work, how much did it burn, and is it safe to try again?" This post answers those three questions in order.
The conclusion first. Failures come mostly from system design, not the model. When UC Berkeley researchers classified 1642 multi-agent-system execution traces, 44.2% of failures were system-design issues, 32.3% were inter-agent misalignment, and 23.5% were task-verification failures. And the two most common failure modes are step repetition (15.7%) and unaware of termination conditions (12.4%) — together 28.1% of the whole. These two are not just "failure." They mean the agent repeats the same work and doesn't know when to stop, and that is both a token bill and a side effect happening twice.
That is where the third question comes in. When a nondeterministic caller (the LLM) drives real side effects (a payment, an email, a ticket), is a retry safe? The answer is "not unless you make it safe yourself." MCP, the standard protocol by which agents call tools, has a hint that declares idempotency but no mechanism that guarantees it. And the closest thing to an industry standard for this problem — IETF's Idempotency-Key header draft — began in 2020 and still isn't an RFC; as of April 2026 it is expired.
Where Agents Actually Fail — MAST's 14 Modes
The most citable source is MAST (Multi-Agent System Failure Taxonomy). It is a paper by researchers at UC Berkeley and Intesa Sanpaolo (Cemri, Pan, Yang et al., arXiv:2503.13657), presented in the NeurIPS 2025 Datasets & Benchmarks track — the first attempt to empirically classify multi-agent-system failures.
Start with the methodology. The researchers analyzed some 150 traces from 5 open-source frameworks (each averaging more than 15,000 lines) with Grounded Theory to build the taxonomy, with 6 expert annotators involved. Three annotators independently labeled 15 traces to reach an inter-annotator agreement of kappa 0.88, and then, using those definitions, built an OpenAI o1-based LLM-as-a-judge pipeline to label all 1642 traces.
Here is the classification. The figures are each mode's occurrence rate out of all 1642 traces.
[Category 1] System Design Issues 44.2%
1.1 Disobey Task Specification 11.8%
1.2 Disobey Role Specification 1.50%
1.3 Step Repetition 15.7% <- most common
1.4 Loss of Conversation History 2.80%
1.5 Unaware of Termination Conditions 12.4%
[Category 2] Inter-Agent Misalignment 32.3%
2.1 Conversation Reset 2.20%
2.2 Fail to Ask for Clarification 6.80%
2.3 Task Derailment 7.40%
2.4 Information Withholding 0.80%
2.5 Ignored Other Agent's Input 1.90%
2.6 Reasoning-Action Mismatch 13.2%
[Category 3] Task Verification 23.5%
3.1 Premature Termination 6.20%
3.2 No or Incomplete Verification 8.20%
3.3 Incorrect Verification 9.10%
It is worth checking that the numbers add up. 11.8 + 1.5 + 15.7 + 2.8 + 12.4 = 44.2, 2.2 + 6.8 + 7.4 + 0.8 + 1.9 + 13.2 = 32.3, 6.2 + 8.2 + 9.1 = 23.5. The three categories sum to exactly 100%.
One warning here. Some secondary summaries of this paper circulate figures like "42% specification problems, 37% coordination breakdown, 21% weak verification" — none of those three numbers are in the paper. The category names differ too: the paper's first category is not "specification issues" but "system design issues" (it looks like the name changed from an earlier version). If you are going to cite it, go straight to Figure 1 of the paper.
The baseline failure rates are worth noting too. The failure rates of the 7 SOTA open-source systems the paper evaluated ranged from 41% to 86.7%. The worst was AppWorld's Test-C (86.7% failure), the best AG2's OlympiadBench (41% failure). That is the baseline for the kind of thing you put in production.
What Each Failure Category Bills You For
One honest thing up front. There are no public figures that say "this failure costs X" per mode. MAST counted occurrence frequency, not cost, and no primary source I found publishes a dollar amount per failure mode. So instead of inventing numbers here, I will distinguish the kind of cost each category bills. In practice that distinction is more useful than an amount — a different kind of cost calls for a different response.
Category 1 (system design, 44.2%) is billed in tokens. Step repetition (15.7%) and unaware of termination conditions (12.4%) make up more than half of this category, and both have the same symptom: "the agent keeps going." Each turn around the loop feeds the whole context back into the model, so cost piles up not linearly but steeper. The good thing about this category is that it is the cheapest to catch — a turn cap, a budget cap, and repetition detection can all go in without touching the model.
Category 2 (inter-agent misalignment, 32.3%) is billed in wrong side effects. The most common mode here is reasoning-action mismatch (13.2%), which by the paper's definition is when an agent's reasoning and its actual action diverge. This is not a token problem. When an agent says "I'll do A" and executes B, the bill lands on whatever system B touched. Task derailment (7.40%) is the same in character.
Category 3 (task verification, 23.5%) is billed in silently wrong output. No or incomplete verification (8.20%) and incorrect verification (9.10%) sum to 17.3%, and what they share is that the failure doesn't look like a failure. The paper's example is striking — a chess program ChatDev built passed superficial tests. This category is found latest, and so is usually the most expensive.
To sum up:
Category 1 (44.2%) -> token bill -> cheapest to catch (caps / detection)
Category 2 (32.3%) -> wrong side effects -> defend with idempotency / permissions
Category 3 (23.5%) -> silently wrong output -> found latest, costs the most
Why Retrying Isn't Safe
Now the crux. Overlay the two most common modes of category 1 (repetition, non-termination) with the most common mode of category 2 (reasoning-action mismatch) and one sentence falls out: a nondeterministic caller repeatedly calls a tool that causes real side effects.
In traditional distributed systems this is not a new problem. When the network drops, the client doesn't know whether its request was processed, and retrying creates duplicates. So we use idempotency keys. But agents have one more problem — in a traditional client, code decides whether to retry; in an agent, the model decides. When the model judges that a tool failed (or misjudges that it did), it simply calls again. The retry policy lives inside the prompt, and that is not a policy but a probability.
There is one crucial distinction here. A retry is dangerous not only when the tool failed. It is more dangerous when the tool succeeded but the response was lost. What the model sees then is "no result," and the model's rational move is to retry. And the side effect has already happened.
Does MCP Guarantee Idempotency? — A Hint, No Mechanism
So how does MCP — the standard protocol by which agents call tools — handle this? Open the schema of the current latest spec revision (2025-11-25) and the answer is clear.
MCP has tool annotations, and one of them is the idempotency hint. Quoting the schema's definition directly: if true, it means that "calling the tool repeatedly with the same arguments has no additional effect on its environment." The four hints' defaults are these:
readOnlyHint default false (assume the tool modifies its environment)
destructiveHint default true (assume the modification is destructive)
idempotentHint default false (assume repeated calls have additional effects)
openWorldHint default true (assume interaction with an external world)
It is worth noting that every default leans pessimistic. It means without annotations, every tool should be treated as a destructive, non-idempotent write. That is good design.
But the spec nails these hints down as follows (the schema's own comment):
Every property in ToolAnnotations is a hint. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like
title). Clients should never make tool use decisions based on ToolAnnotations received from untrusted servers.
Here the real problem shows itself. In the entire 2573-line schema, idempotentHint appears only in that one place where it is declared. And the word "retry" never appears in the schema at all. No idempotency key, no request-deduplication mechanism, no retry semantics.
In other words, on idempotency MCP gives you vocabulary but not tools. A server can claim "I am idempotent," but that claim (a) is not enforced, (b) comes with the spec's own warning not to trust it from an untrusted server, and (c) offers no way in the protocol to make a tool that is not idempotent safe to retry.
You might ask whether the transport layer has redelivery. The Streamable HTTP spec does have resumability and redelivery — the server attaches an id to each SSE event, and after a disconnect the client sends a GET with a Last-Event-ID header, letting the server replay messages after that point. But this replays server-to-client messages, and only on that broken stream (the spec states it MUST NOT replay messages from a different stream); it is just a cursor within a stream. It is not a mechanism that deduplicates the tool calls a client sent. The direction of the MCP spec itself is covered separately in MCP Sheds Its Sessions — Reading the Stateless Core.
So How Far Has the Industry Standard Come? — A Draft for Five and a Half Years
"Just use an idempotency key" is the natural reaction. Right. But surprisingly, there is no standard for idempotency keys.
Checking the history of the IETF HTTPAPI working group's Idempotency-Key HTTP header draft directly in the Datatracker, here it is:
draft-idempotency-header-00 2020-11-17 (individual draft)
draft-ietf-httpapi-idempotency-key-header-00 2021-07-01 (WG adoption)
-01 2022-05-08
-02 2022-11-09
-03 2023-07-04
-04 2023-11-16
-05 2024-05-27
-06 2025-02-24
-07 2025-10-15 (latest)
Current status: Expired (expired 2026-04-18)
IESG state: "I-D Exists" — the IESG has never begun processing it
It began in November 2020, was revised 8 times, and the latest revision, posted in October 2025, expired on 18 April 2026 under the six-month lifetime rule for Internet-Drafts. Its IESG state is "I-D Exists" — in the Datatracker's own words, "the IESG has not started processing this document, or has stopped processing it without publication." For more than five and a half years it has failed to become an RFC.
So in practice an "idempotency key" is not a standard but a convention. The place that documents that convention best is Stripe, and what the IETF draft above was trying to codify is largely this convention. The behavior documented by Stripe is this:
- For a given idempotency key, it stores the first request's status code and response body, whether that was a success or a failure.
- A subsequent request with the same key returns the same result — it replays even a 500 error verbatim.
- The idempotency layer compares the incoming parameters against the original request and errors out if they differ.
- A key may be pruned after at least 24 hours, and reusing the same key after it is pruned creates a new request.
- A result is only stored once the endpoint has started executing. Parameter-validation failures and concurrent-execution conflicts are not stored, so they can be retried.
From an agent's point of view, two lines especially sting. One is that it replays even a 500 — an idempotent retry is not "try again" but "show the original result again." If the model sees a failure and retries, it sees the failure again. This is design, not a bug, and if the model doesn't understand it, it falls into an infinite loop (and that is exactly failure mode 1.3, step repetition). The other is 24 hours. When a long-running agent that churns over several days retries with yesterday's key, that is not a retry but a new side effect.
How Do You Measure Agent Cost? — OTel Has No Cost Metric
Now observability. If you want to follow a standard you end up looking at OpenTelemetry's GenAI semantic conventions, and it is best to start knowing exactly what state they are in.
First, the GenAI conventions have moved. They used to live in docs/gen-ai in the open-telemetry/semantic-conventions repo, but the files at that path now carry only a note: "this page has moved and is no longer maintained in this repository." Their new home is open-telemetry/semantic-conventions-genai.
Second, that new repo was created on 5 May 2026 and has no release yet at all. (The latest release of the main semantic conventions is v1.43.0, dated 3 July 2026.) And both the agent-spans doc and the MCP doc carry the document status "Development." Development hasn't stalled — the last push was two days ago. It just means that the thing you call a "standard" and build dashboards on is a never-released, in-development spec.
Third, and this matters most in practice — there is no cost metric. Search the GenAI semantic conventions' metrics doc and the entire attribute registry for cost, price, usd, dollar and you get zero hits. The defined metrics are all of these:
gen_ai.client.token.usage
gen_ai.client.operation.duration
gen_ai.client.operation.time_to_first_chunk
gen_ai.client.operation.time_per_output_chunk
gen_ai.server.request.duration
gen_ai.server.time_to_first_token
gen_ai.server.time_per_output_token
The standard gives you tokens. The money is on you. To turn tokens into an amount you need a per-model price table, and vendors change that table. This is why vendors' cost dashboards each have their own proprietary format and their numbers don't agree.
Why Naively Summing Tokens Is Wrong
If you have decided to compute cost yourself, there is one trap you must know. The attribute registry defines five token attributes:
gen_ai.usage.input_tokens
gen_ai.usage.output_tokens
gen_ai.usage.cache_creation.input_tokens
gen_ai.usage.cache_read.input_tokens
gen_ai.usage.reasoning.output_tokens
The problem is that these are not flat but nested. The registry's comment is explicit — cache-creation tokens and cache-read tokens SHOULD be included in gen_ai.usage.input_tokens, and input tokens should include every kind of input token, cached tokens included. Reasoning tokens likewise should be included in gen_ai.usage.output_tokens.
So this calculation is wrong:
Wrong calculation:
cost = input_tokens x input_price + output_tokens x output_price
Why it's wrong:
input_tokens already contains cache_read.input_tokens
cache reads are billed cheaper than new input (that's the point of caching)
-> the better your cache hits, the more your math overstates the real cost
Right direction:
new_input = input_tokens - cache_read - cache_creation
cost = new_input x input_price
+ cache_read x cache_read_price
+ cache_creation x cache_write_price
+ output_tokens x output_price
(prices vary by vendor and model, so no numbers here)
In an agent this error is not small. Because an agent loop resends the same system prompt and tool definitions every turn, the cache hit rate is structurally high, so cache reads make up a large share of input_tokens. Sum them naively and the better your caching works, the more your books overstate cost. Trying to see caching's cost benefit on a dashboard, you can reach the exact opposite conclusion.
There is one more layer. Those five attributes are span attributes, and the well-known values of gen_ai.token.type — the dimension of the metric gen_ai.client.token.usage — are only input and output. That is, from the aggregated metric alone you cannot separate cached tokens from new ones. To get an accurate cost you have to drop down to the span level. (You can use a custom value, but the moment you do it is no longer standard.)
What to Instrument — and the Opt-In Trap
Tool-call instrumentation uses the execute_tool span. The span name is execute_tool followed by the tool name, and the attributes are these:
gen_ai.operation.name Required (Development)
gen_ai.tool.name Required (Development)
error.type Recommended (Stable)
gen_ai.agent.name Conditionally Req. (Development)
gen_ai.tool.call.id Recommended (Development)
gen_ai.tool.description Recommended (Development)
gen_ai.tool.type Recommended (Development)
gen_ai.tool.call.arguments Opt-In (Development) <- not collected by default
gen_ai.tool.call.result Opt-In (Development) <- not collected by default
The last two lines are the trap. What arguments the agent called the tool with and what it got back are "Opt-In" — that is, not collected by default. There is a good reason (privacy and storage cost). But the upshot is that exactly the two fields you need to debug why the agent did something wrong are off by default. To track category 2 (reasoning-action mismatch) you have to turn them on, and the moment you do, sensitive data can enter your traces. It is not a free lunch — it is a tradeoff you decide deliberately, together with sampling and masking.
If you use MCP there is one more thing to know. The MCP semantic-conventions doc points out that HTTP trace-context propagation covers only the HTTP request and not the individual messages exchanged inside the request/response stream. So instrumentation has to inject context into the params._meta pocket of the MCP request (SEP-414 defines how the traceparent, tracestate, and baggage keys are handled), and the receiver uses it as the remote parent. Without this, the agent trace breaks at the MCP boundary. For logging and tracing in general, see LLM Logs and Tracing.
Does Fixing It Actually Help? — Honest Intervention Results
This is the part of the post that has to be most honest. The MAST paper did not stop at classifying; it tried actual interventions based on the taxonomy. The results (paper Table 5) are these:
AG2 / GSM-Plus (GPT-4) baseline 84.75 +- 1.94 | prompt fix 89.75 +- 1.44 | topology change 85.50 +- 1.18
AG2 / GSM-Plus (GPT-4o) baseline 84.25 +- 1.86 | prompt fix 89.00 +- 1.38 | topology change 88.83 +- 1.51
ChatDev / ProgramDev-v0 baseline 25.0 | prompt fix 34.4 | topology change 40.6
ChatDev / HumanEval baseline 89.6 | prompt fix 90.3 | topology change 91.5
The widely cited "+9.4%" and "+15.6%" come from here. On ChatDev's ProgramDev-v0, 25.0 → 34.4 is +9.4 and 25.0 → 40.6 is +15.6. These are not percentages but percentage points.
But if you are going to cite these numbers, there are things you must say alongside them.
First, ProgramDev-v0 is 32 tasks. Out of 32, 25.0% is 8, 34.4% is 11, and 40.6% is 13 (the arithmetic lands exactly). So the celebrated "+9.4%p" means 3 more tasks passed and "+15.6%p" means 5 more tasks. It is a small sample.
Second, even after the best intervention the success rate is 40.6%. In other words, about 59% still fail. In the paper's own words: "while these initial interventions improve performance, they do not resolve all failure modes and task completion rates remain low, suggesting that more substantial improvements are needed."
Third, the effect is not universal. On AG2, the topology change was not statistically significant on GPT-4 (Wilcoxon test p-value 0.4), while the same change was significant on GPT-4o (p-value 0.03). The paper's conclusion: "these strategies are not universal, and their effectiveness depends on factors such as the underlying LLM."
Fourth, there is a direction. The paper reports that on both systems, topology-based changes were more effective than prompt-based ones. And the authors insist that robust reliability needs "more than isolated fixes" and should aim at a "fundamental MAS redesign."
Finally, the limits of the source itself. Most of the 1642 traces were labeled by the authors' own o1-based LLM annotator. Only a much smaller subset was human-verified (the kappa 0.88 is the agreement of 3 annotators on 15 traces). The authors themselves write that they do not claim MAST covers every potential failure pattern. This is not a definitive map, but the most empirical one produced so far.
So What Should You Do? — Decision Rules
Rules that follow directly from the sources above.
1. Classify tools by side effect first. The MCP defaults already tell you the answer — without annotations, treat a tool as destructive and non-idempotent. Don't mix read-only tools and write tools under one retry policy.
2. Implement idempotency in your own layer. MCP's idempotentHint is a hint, not a guarantee, and the spec itself says not to trust an untrusted server's annotations. There is no IETF standard either. So for tools that cause side effects, take a key on your server side, store the first result, and replay it. Stripe's convention is a proven reference implementation — down to storing the result (errors included), comparing the parameter fingerprint, and stating a retention period.
3. Don't let the model make the key. Because the thing deciding on a retry is nondeterministic, the idempotency key has to be derived from somewhere deterministic — the orchestrator's step ID or task ID. If the model invents a new key every time, the idempotency key is decoration.
4. Set caps as a budget. If 28.1% of failures are repetition and non-termination, then a turn cap and a token budget are not an optimization but a circuit breaker. It is the cheapest way to block the most common failure category.
5. Compute cost from spans. Metrics alone can't separate cache tokens. And don't just sum tokens — cache reads are already inside input_tokens.
6. Decide argument and result collection deliberately. The default is off. Leave it off and you can't debug category 2; turn it on and sensitive data enters your traces. Design sampling and masking alongside it.
When Not to Do Any of This
The honest other side. Most of the above earns its keep only when the agent drives real side effects.
- For a read-only agent, idempotency infrastructure is overkill. In an agent that only searches, summarizes, and analyzes, a duplicate call just costs more money without corrupting state. Just set caps and move on.
- If a human approves every write, most of the retry risk goes away. An approval gate is cheaper to build than an idempotency layer, and in early stages it is usually better.
- If a workflow is enough, don't use an agent. Most MAST failures are system-design issues, and those arise "in proportion to how much you handed the decision to the model." If the path is fixed, write it in code — the workflow-versus-agent distinction covered in Building Effective AI Agents applies directly here.
- If a single agent works, don't go multi-agent. 32.3% of MAST failures happened between agents. The moment you add one more agent, you buy that 32.3% of surface area anew.
Closing
To sum up. Agent failure is not the model's failure but mostly a failure of system design (44.2% by MAST). The two most common modes are repetition and non-termination (28.1% together), and these two burn tokens while duplicating side effects. And there is no protocol-level device to prevent that duplication — MCP gives only an idempotency hint, and IETF's idempotency-key standard has been a draft for five and a half years and is expired right now. Observability is similar — the OTel GenAI conventions are a never-released, in-development spec, have no cost metric at all, and leave the arguments and results you need for debugging off by default.
This list is not a reason to be pessimistic but a map of where you have to fill the gaps yourself. The box you left empty expecting a standard to fill it is exactly the box the bill comes from in production.
And finally, look at the intervention results again. Even after the best structural intervention, code-generation success ran from 25.0% to 40.6% — more than half still fail. Running a production agent is less about eliminating failure than about knowing the cost of failure — which failures only burn tokens, which corrupt state, and which quietly emit a wrong answer. Operations begins the moment you tell those three apart. The question of what to measure an agent by, and how, continues in The Simulated Customer Never Leaves, and the problem of running agents in isolation in Lambda microVM Agent Sandboxes.
References
- Why Do Multi-Agent LLM Systems Fail? (Cemri et al., arXiv:2503.13657, NeurIPS 2025 D&B) — the MAST taxonomy, 14 failure modes, Figure 1 and Table 5
- The official MAST repository / the MAST-Data dataset
- MCP spec 2025-11-25 schema — the ToolAnnotations definition
- MCP spec — transports (resumability and redelivery)
- The Idempotency-Key HTTP Header Field — IETF Datatracker (currently Expired)
- Stripe API — Idempotent requests
- The OpenTelemetry GenAI semantic-conventions repository (the new, relocated repo)
- GenAI agent-spans conventions (Status: Development)
- The GenAI attribute registry — token-usage attributes and nesting rules
- MCP semantic conventions — params._meta context propagation
- Building Effective AI Agents — the workflow/agent distinction (related post)
- MCP Sheds Its Sessions — the Stateless Core (related post)