
에이전트가 통제 불능 상태일 때의 사고 대응: 런북 (Runbook)
요약
AI 에이전트가 정상적인 시스템 지표(Latency, Error rate)를 유지하면서도 논리적으로 잘못된 행동을 하는 '조용한 실패' 상황에 대한 대응 가이드를 제공합니다. 사고 발생 시 디버깅보다 즉각적인 킬 스위치 작동을 통한 피해 확산 방지의 중요성을 강조합니다.
핵심 포인트
- 에이전트 사고는 지표상 정상임에도 논리적 오류로 발생할 수 있음
- 사고 발생 시 디버깅보다 즉각적인 킬 스위치 작동이 우선되어야 함
- 폭발 반경을 줄이기 위해 30초 이내에 작동 가능한 킬 스위치 설계 필요
- 플래그 설정 시 기본값을 False로 두는 등 안전한 폴백 전략 수립
- 도서: Agents in Production — Building, Tracing, and Shipping Multi-Step AI You Can Trust
- 저자의 다른 저서: Observability for LLM Applications — The AI Engineer's Library (2권 시리즈)의 동반 도서
- 나의 프로젝트: Hermes IDE | GitHub — Claude Code 및 기타 AI 코딩 도구를 사용하여 작업하는 개발자들을 위한 IDE
- 나: xgabriel.com | GitHub
대시보드는 초록색입니다. 지연 시간 (Latency)은 평탄합니다. 에러율은 6시간 동안 변동이 없습니다. 그때 고객 지원 팀에서 이메일을 전달해 옵니다. 한 고객이 왜 에이전트가 변호사가 아닌 집주인에게 이메일을 보냈는지 묻고 있습니다. 당신이 이 글을 다 읽을 때쯤이면, 에이전트는 또 다른 누군가에게 똑같은 행동을 저질렀을 것입니다.
이것이 바로 에이전트 사고 (agent incident)의 모습입니다. 급격한 수치 상승이나 경고 알람이 아닙니다. 이메일 한 통입니다. 플랫폼은 정상이며 제품이 고장 난 상태입니다. 에이전트는 조용히 실패합니다. HTTP 호출은 성공하고, 모델은 잘 구성된 JSON을 반환하며, 모든 스팬 (span)은 200 상태 코드를 나타냅니다. 무엇이 고장 났는지는 스팬 (span)들 '사이'에 존재합니다. 에이전트가 잘못된 도구 (tool)를 선택했거나, 올바른 도구에서 루프 (loop)를 돌았거나, 신뢰할 수 없는 입력값에서 읽어온 문자열에 의해 유도되었을 수 있습니다.
이것은 처음 5분간의 상황입니다. 사후 분석 (postmortem)이나 수정 단계가 아닙니다. 상처가 왜 생겼는지 이해하기 전에 출혈을 막아야 하는 5분입니다. 이것을 인쇄해서 벽에 붙여두세요.
1분: 킬 스위치 (kill switch)를 작동시키세요
먼저 디버깅을 해서는 안 됩니다. 먼저 에이전트를 중단시켜야 합니다. 코드 동결 (code freeze) 기간 중 운영 데이터베이스 (production database)에 대해 파괴적인 명령을 실행하여 라이브 레코드를 삭제한 Replit 에이전트가 참고 사례입니다. 이 사건은 Fortune에 의해 보고되었으며, AI Incident Database 항목 1152로 기록되었습니다. "무언가 잘못되었다"와 "모든 것이 꺼졌다" 사이의 시간적 간극이 바로 폭발 반경 (blast radius)이 커지는 지점입니다.
킬 스위치 (kill switch)는 모든 에이전트 호출을 제어하는 불리언 (boolean) 게이팅 장치로, 온콜 (on-call) 엔지니어가 30초 이내에 찾을 수 있도록 가장 바깥쪽 엔트리포인트 (entrypoint)에 연결되어 있어야 합니다.
# agent/service.py
import ldclient
...
플래그 (flag)의 기본값은 False입니다. 잘못 설정된 플래그 클라이언트 (flag client)는 에이전트를 켜는 것이 아니라 끄게 만듭니다. 이것이 고객에게 이메일을 보낼 수 있는 도구에 있어 올바른 실패 방향입니다. 폴백 (fallback)은 영리할 필요가 없습니다. 미리 작성되어 법무팀의 승인을 받은 메시지를 사용하세요: "현재 이 기능과 관련된 문제를 조사 중입니다. 귀하의 요청은 기록되었으며, 영업일 기준 1일 이내에 담당자가 후속 조치를 취할 것입니다." 사고가 발생하기 전에 미리 작성해 두십시오. 사고가 진행되는 동안 비용이 계속 발생하는 상황에서 홍보(PR) 팀과 문구에 대해 논쟁하고 싶지는 않을 것입니다.
2분: 예산을 동결하세요
어떤 사고들은 에이전트 전체를 끌 필요는 없습니다. 폭주하는 루프 (runaway loop)의 경우 지출액을 제한해야 합니다. Claude Code Issue #44726 (2025년 4월)이 그 사례입니다. 사용자들은 평소 5:1로 유지되던 세션에서 입력 대비 출력 토큰 비율이 74:1 및 175:1로 나타나는 결제 버그를 보고했습니다. 보고된 증상은 도구 호출 (tool calls) 전반에 걸쳐 컨텍스트 (context)가 무제한으로 커지는 복합 루프였습니다. sanj.dev를 통해 알려진 해당 유형의 사고에 대한 슬로건은 다음과 같습니다: "AI 에이전트는 충돌(crash)하지 않습니다, 돈을 씁니다(spend)."
예산 동결(budget freeze)은 무제한적인 재무적 사건을 제한된 사건으로 전환합니다. 에이전트는 여전히 실패하지만, 청구서가 전이(metastasising)되는 것은 멈춥니다. 만약 이미 궤적당 예산(per-trajectory budgets)을 적용하고 있다면, 사고 발생 시 동결은 동일한 조절 노브(knob)를 전역적으로 조이는 것과 같습니다.
# agent/budget.py
def check_budget(spent_usd: float, caps: dict) -> str | None:
if spent_usd >= caps["hard"]:
...
사고가 발생하는 동안, caps["freeze"] 값을 현재 p50 값에 가깝게 낮추어, 실행 중인 궤적(in-flight trajectories)은 소진되도록 두되 새로운 궤적은 시작되지 못하게 합니다. 이 체크 로직을 모델 호출(model call) 이전에 연결하십시오. 만약 호출 이후에 체크한다면, 이미 예산을 초과하게 만든 그 호출에 대한 비용을 지불한 상태가 됩니다.
3분째: 궤적 분류 (triage the trajectory)
이제 확인을 시작합니다. Grafana도, Kibana도, 배포 로그도 아닙니다. 당신이 구축해 둔 에이전트 관측성(agent-observability) 도구(Langfuse, Arize, Braintrust, Phoenix 등)에서 궤적 뷰어(trajectory viewer)를 열고 실패한 trace_id를 가져오십시오. 알림(alert)에 해당 ID가 링크 형태로 포함되어 있어야 합니다. 만약 포함되어 있지 않았다면, 그것이 사고 대응 후 가장 먼저 수정해야 할 사항입니다.
실패 직전의 마지막 5개 도구 호출(tool calls)을 읽으십시오. 그들의 입력(inputs), 출력(outputs), 그리고 그 사이에서 모델이 작성한 내용을 읽으십시오. 당신은 궤적이 당신이 선택했을 법한 경로에서 벗어난 지점을 찾아내야 합니다. 그 지점은 거의 항상 마지막 단계가 아닙니다. 대개 3~4단계 앞선 단계이며, 에이전트가 잘못된 경로로 작은 결정을 내린 후 남은 실행 시간 동안 그 결정을 방어하는 데 시간을 보낸 지점입니다.
추론 텍스트(reasoning text)를 읽는 한 가지 규칙은 다음과 같습니다: 도구 호출은 신뢰하되, 산문(prose)은 불신하십시오. 모델은 "어떠한 변형(mutation)을 실행하기 전에 대상 환경을 신중하게 검증하겠습니다"라고 작성하지만, 바로 다음 도구 호출은 검증 과정 없이 db://prod에 대해 execute_sql을 실행하는 식입니다. 산문은 사후 합리화(post-hoc rationalisation)일 뿐입니다. 실제로 일어난 일은 도구 호출입니다.
1. 알림에서 trace_id를 확보합니다.
2. 궤적 뷰어를 열고 전체 실행(full run)을 로드합니다.
3. 끝까지 스크롤한 다음, 역순으로 따라갑니다.
...
모든 주요 입력 클래스에 대해 골든 궤적 (golden trajectory) — 즉, 개발 중에 캡처된 검증된 정상 실행 기록 — 을 유지하십시오. 실패한 실행(failing run)과 비교(diff)하는 것이 각각을 따로 읽는 것보다 더 빠릅니다.
4분째: 영향 범위 (blast radius) 확인
가설이 세워졌습니다. 무언가를 수정하기 전에 한 가지 질문에 답하십시오. 이 문제가 이미 얼마나 많은 사용자에게 도달했는지, 그리고 여전히 확산 중인지 확인해야 합니다.
에이전트 사고의 유입 형태는 이를 숨깁니다. 하나의 지원 티켓(support ticket)을 통해 발생하기 때문에 마치 한 명의 사용자 문제처럼 느껴질 수 있습니다. 하지만 대개 한 명의 사용자 문제가 아닙니다. 트레이스 저장소(trace store)를 쿼리하여 사고 발생 시간대(incident window) 내에서 궤적 이탈 시그니처(off-rails signature)와 일치하는 모든 궤적을 찾아내십시오.
# scripts/blast_radius.py
def blast_radius(traces, signature, window):
hit = [
...
두 가지 분기가 있습니다. 만약 still_open이 참(true)이라면, 당신의 킬 스위치(kill switch)나 동결(freeze) 조치가 완전히 작동하지 않은 것입니다. 1분 단계로 돌아가 모든 리전(region)에서 플래그(flag)가 실제로 전환되었는지 확인하십시오. 만약 거짓(false)이지만 distinct_users가 크다면, 이는 단순한 코드 문제가 아니라 고객 커뮤니케이션(customer-comms) 문제입니다. 캐나다의 한 법정은 에이전트가 고객에게 말한 내용에 대해 Air Canada의 책임을 물었습니다 (CBC): 기업은 에이전트가 말하는 내용에 대한 책임을 집니다. 만약 에이전트가 200명에게 잘못된 약속을 했다면, 법무팀(legal)에는 요약본이 아닌 명단이 필요합니다.
5분째: 수정하기 전에 평가 (eval)를 캡처하십시오
수정은 쉬운 부분이면서 동시에 함정입니다. 가드레일(guardrail)을 패치하면 에이전트는 오작동을 멈추고 모두가 다시 잠자리에 들겠지만, 다음 분기에 다른 트레이스 ID(trace ID)를 가진 동일한 유형의 실패가 다시 배포될 것입니다. 반복을 막아주는 것은 평가(eval)이며, 실패한 입력이 아직 눈앞에 있는 지금 바로 캡처해야 합니다. 나중이 아니라 지금 말입니다.
에이전트를 궤도에서 이탈하게 만든 정확한 입력을 추출하여, 회귀 테스트 케이스(regression case)로서 동결된 평가 세트(frozen eval set)에 추가하십시오.
# evals/regressions.py
def add_regression(trace, verdict, eval_path):
case = {
...
이것은 대부분의 에이전트 사후 분석 (postmortem)에서 생략되지만, 결정적인 차이를 만드는 분야입니다. 전통적인 사후 분석은 "배포 (deploy)가 포함되었는가?"를 묻습니다. 에이전트의 경우 그 대답은 종종 '아니오'입니다. 배포는 2주 전에 이루어졌고, 모델은 지난 화요일부터 드리프트 (drifting)를 시작했으며, 아무도 아무것도 건드리지 않았기 때문입니다. 일반화할 수 있는 질문은 "어떤 입력 (input)이 이를 망가뜨렸는가, 그리고 그것이 현재 평가 세트 (eval set)에 포함되어 있는가"입니다. 해당 케이스가 병합 (merge)되고, 수정 사항에 대해 평가 스위트 (suite)가 통과 (green)될 때까지 그 사고는 종료되지 않습니다.
화재가 발생하기 전에 길러야 하는 근육
이 다섯 가지 조치 중 어느 것도 실제 사고가 발생했을 때 처음 시도한다면 효과가 없습니다. 분기에 한 번씩 게임 데이 (gameday)를 실행하세요. 스테이징 (staging) 환경에 실패 모드 (failure mode)를 주입하고, 실제 온콜 (on-call) 엔지니어에게 페이지 (page)를 보냅니다. 엔지니어가 킬 스위치 (kill switch)를 작동시키고, 예산 (budget)을 동결하며, 경로를 벗어난 단계 (off-rails step)를 찾아내고, 영향 범위 (blast radius)를 확인하는 데 시간이 얼마나 걸리는지 측정하세요. 첫 번째 게임 데이는 항상 엉망으로 끝납니다. 그것이 핵심입니다. 고객이 대신 알아내기 전에, 어떤 런북 (runbook) 항목이 핵심적인 지지대 역할을 하는지, 그리고 어떤 것이 허구인지 알려주기 때문입니다.
AI Engineer's Library는 이 페이지의 확장 버전입니다. _Agents in Production_은 킬 스위치, 예산, 게임 데이가 이론에 그치지 않고 실제가 될 수 있도록 구축하고 출시하는 방법을 다루며, _Observability for LLM Applications_는 새벽 3시에도 궤적 (trajectory)을 읽을 수 있게 해주는 트레이싱 (tracing), 판사 점수 (judge scores), 평가 세트 (eval sets)를 다룹니다. 런북은 화재가 시작되기 전에 트레이스 (trace)가 이미 존재할 때에만 작동합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기