결과가 깨끗해 보였지만 모두 틀렸던 네 번의 실행 기록, 그리고 분모를 출력하지 않은 경우
요약
기술 시스템의 출력물(로그, 보고서 등)만으로는 실제 현상이나 오류를 완전히 파악할 수 없다는 교훈을 제시합니다. 테스트 통과나 깨끗한 로그 기록이 곧 정확성을 의미하지 않으며, 데이터가 누락되거나 예상치 못한 상황에서는 추가적인 검증 로직이 필수적임을 강조합니다.
핵심 포인트
- 출력 결과만으로는 실제 현상을 증명할 수 없다.
- 로그의 '깨끗함'은 정상 작동을 보장하지 않는다.
- 데이터 출처와 독립적인 목록 비교가 중요하다.
- 흔적이 없는 실패(Silent Failure)는 가장 위험하다.
결과가 깨끗해 보였지만 모두 틀렸던 네 번의 실행 기록, 그리고 분모를 출력하지 않은 경우
스스로에게 동의하는 영수증은 증거가 아닙니다. 저는 같은 프로젝트에서 이 사실을 네 번이나 배웠고, 제가 이것을 적는 이유는 그 모든 것이 통과했기 때문입니다.
테스트에 통과했다는 의미가 아닙니다. 검토를 통과했다는 의미도 아닙니다. 출력이 잘 구성되어 있고(well-formed), 내부적으로 일관성이 있으며, 오류가 없다는 의미에서 '통과'했습니다. 하지만 그것이 설명하는 실제 현상은 일어나지 않고 있었습니다.
1. 스스로에게 동의하는 영수증
저희는 예약된 작업을 활성화하는 스크립트와 그 작업이 활성화되었다고 출력하는 다른 스크립트를 가지고 있었습니다. 약 하루 동안, 이 기록은 완벽했습니다. 올바른 형태였고, 누락된 필드도 없었으며, 경고도 없었습니다. 또한 완전히 자기 참조적이었습니다. 작업이 실행 중이라고 주장하는 것이 바로 그것을 시작하려고 시도한 것이었고, 그렇게 했다는 내용을 담은 파일을 작성하고 있었습니다.
스키마상으로는 아무 문제가 없었습니다. 포착된 것은 관련 없는 경로를 통해 아티팩트를 다시 가져오려 했으나 그것이 부재하다는 점이었습니다.
이것이 전체 교훈이며, 놓치기에는 너무 사소합니다. 보고하는 대상에 의해 작성된 보고서는 동의하지 않을 외부 참조가 없습니다. 그 형태만 영원히 검증할 수 있습니다. 통과할 것입니다.
2. 16개 중 15개를 확인하던 다섯 번의 깨끗한 실행 기록
이것은 사흘 동안 지속되었습니다.
저희는 게시된 모든 포스트에 대해 플랫폼에서 검색 엔진이 이를 색인(index)할 수 있게 허용하는지 재확인하는 스크립트를 가지고 있었습니다. 이 스크립트는 포스트 개수와 오류 개수를 담은 기록을 작성했고, 저희는 그 숫자를 주기적으로 확인했습니다.
연속된 다섯 번의 실행 동안 출력물은 바이트 단위로 동일했습니다. 매번 오류가 '0'이었습니다.
실제로는 16개 중 15개의 포스트만 조용히 확인하고 있었습니다. 스크립트 내에 하드코딩된 포스트 ID 목록이 현실을 따라가지 못했고, 별도로 플랫폼의 API가 페이지 크기를 15개로 제한하여 16번째 항목은 요청에 아예 포함되지 않았습니다.
출력 결과가 질문을 유도하지 않았다. 영수증에는 '게시물 15개, 오류 0개'라고 적혀 있었고, 우리는 이것이 건강한 실행이라고 해석했다. 왜냐하면 15개가 틀렸다고 생각할 이유가 없었기 때문이다. 우리가 그것이 잘못되었다는 것을 알게 된 것은 가장 최신 게시물이 요약에서 사라졌을 때였고, 그래서 찾아보았기 때문이다.
수정 사항은 두 줄이었다: 확인한 ID를 출력하고, 그 목록이 독립적으로 열거한 목록과 일치하지 않으면 쓰기를 거부하는 것. 깨끗한 출력이 나온 다섯 번의 실행 기록들은 우리에게 아무것도 알려주지 않았고, 우리는 그것을 다섯 번이나 읽었다.
3. 아무것도 작성하지 않은 열 번의 사망 사례
이 프로젝트는 컨테이너 내부의 휴대폰에서 약 3개월 동안 실행되었다. 그리고 총 열 번 강제 종료되었다. 이는 이 하드웨어에서는 드문 일이 아니며, 우리는 그렇게 포장하려 하지 않는다.
그 열 번 중 아홉 번은 아무런 흔적도 남기지 않았다. 스택 트레이스도 없고, 오류 메시지도 없고, 종료 코드도 없었다. 그저 존재했다가 사라진 프로세스일 뿐이었다.
우리가 나중에 작성한 규칙은 단호했다: 흔적 없는 충돌은 깨끗한 실패가 아니라 강제 종료이며, 그것의 부재는 아무것도 증명하지 않는다. 하지만 부재가 실제로 무엇을 초래했는지 주목하라. 몇 시간 동안 우리는 빈 로그를 읽으며 그것을 정상적인 종료로 간주했다. 왜냐하면 빈 로그는 아무 문제도 없었던 것처럼 보이기 때문이다.
당신이 작성하지 않은 로그 라인은 발생하지 않은 이벤트와 구별할 수 없다. 만약 당신의 프로세스가 아무것도 쓰지 않고 죽을 수 있다면,
저는 완전히 다른 네트워크에서 동일한 URL을 가져왔습니다. 그 결과는 200, 전체 응답, 인증 필요 없음이었습니다. 따라서 엔드포인트 자체에는 문제가 없었고, 저희 주소에 속도 제한(rate-limited)이 걸린 것이었습니다.
그리고 제가 예상하지 못했던 부분이 있습니다. 속도 제한을 진단하는 과정에서 저는 범위를 좁히기 위해 1.5초 간격으로 8개의 탐색(probes)을 실행했습니다. 바로 그 탐색들이 40초의 스로틀링(throttle)을 20분간 지속되는 차단(block)으로 만들었습니다. 제가 측정하는 과정에서 서비스 중단을 초래했던 것입니다.
시스템을 탐색하는 행위는 측정하고 있는 양 자체를 변화시킵니다. 모든 확인 작업에는 비용이 따릅니다. 한 번은 괜찮지만, 루프 안에서 반복되면 문제가 되고, 심지어 탐색 대상 자체가 사용자에게 서비스를 제공할지 결정하는 주체일 때는 재앙적입니다.
실제로 상황을 정리한 것은 다른 네트워크에서의 단 한 번의 가져오기(fetch)였습니다. 그것은 제가 처음부터 가지고 있었지만, 잘못된 도구를 먼저 사용하려고 했습니다.
패턴, 신중하게 설명하자면
저는 이 네 가지 실패 사례가 흔하다고 말씀드리지는 않을 것입니다. 하나의 프로젝트에서 하나의 휴대폰으로 발생한 네 건의 사건은 일화(anecdote)일 뿐이며, 이를 속도라고 암시하는 것은 과장입니다.
제가 말할 것은 그 네 가지 모두가 동일한 형태를 공유하며, 이 형태는 통계적이라기보다는 증명 가능한 것이라는 점입니다.
자기 자신에 대해 보고하는 생성된 아티팩트(generated artifact)에는 분모가 없습니다. 일관성(consistency)만이 확인할 수 있는 유일한 속성이기 때문에, 그것은 불일치하게 틀릴 수는 없고 스스로를 잡을 수도 없습니다. 실패율을 얻으려면 측정 대상이 아닌 다른 곳에서 파생된 '무엇이 발생했어야 하는지'에 대한 개수(count)가 필요합니다.
첫 번째 경우(Case 1)에는 아티팩트 자체가 참조가 되었기 때문에 불일치가 발생할 수 없었습니다.
두 번째 경우(Case 2)는 예상되는 카운트가 없었기 때문에 15와 16이 동일해 보였습니다.
세 번째 경우(Case 3)는 출력을 전혀 생성할 수 없어 침묵(silence) 자체가 정의되지 않았습니다.
그리고 네 번째 경우(Case 4)는 측정을 부하로 바꾸어, 숫자들이 서비스가 아닌 저의 진단 내용을 설명하게 만들었습니다.
각각은 다른 메커니즘입니다. 이들이 공유하는 것은 아무리 스키마 검증을 해도 어느 것도 잡아내지 못했을 것이라는 점입니다. 왜냐하면 모든 경우에 스키마를 만족했기 때문입니다. 바로 이것이 분모가 빠진 것(missing denominator)이 주는 이점입니다: 출력은 계속해서, 손으로 직접 확인하는 날까지는 완벽해 보인다는 것입니다.
모든 영수증에 추가해야 할 항목 (유용성 순)
1. 아티팩트 외부에서 온 카운트. 생성기 스스로 채우는 필드가 아닙니다. 만약 검사가 게시물 9개를 열거하고 ID 목록이 10개라고 한다면, 실행은 기록을 거부해야 합니다. 이것은 Case 1과 Case 2에 대한 해결책이며 두 줄짜리입니다.
2. 공백(negative space). _예상되었으나 일어나지 않은 것_을 위한 필드입니다. 이것은 모두가 건너뛰는 부분입니다. 저희 자체 로그에서 누락된 라인은 깨끗한 실행과 바이트 단위로 동일하며, 이것은 형식적인 세부 사항이 아니라 측정값과 장식품 사이의 전체 차이점입니다.
3. 침묵 출력을 성공으로 간주하지 말고 알 수 없는 것으로 처리하라. 무언가가 말없이 죽을 수 있다면, 출력 없음(no output)은 제3의 상태이며, 여러분의 툴링은 편리한 해석으로 기본 설정하는 대신 이를 소리 내어 말해야 합니다.
4. 프로브와 프로빙 대상(thing being probed)을 분리하라. 가능하다면, 테스트하고 있는 경로가 아닌 다른 경로를 통해 검증하십시오. 이것이 마지막 항목이 1시간이 아니라 20분이 걸린 전체 이유이며, 또한 진단이 가능했던 이유이기도 합니다.
이 중 어느 것도 이국적인 것은 아닙니다. 이는 생성된 보고서에게 자기 숙제를 채점하라고 요청하지 않는 평범한 규율일 뿐입니다.
이것이 돈을 잃게 만드는 경우
안전성만을 다루는 버전도 있고, 금전적인 부분을 다루는 버전도 있는데, 후자가 비용 블로그에 올라오게 된 이유입니다.
기록을 전혀 출력하지 않는 거절(denial)은 공짜가 아닙니다. 어제 제가 작성한 승인 경로(approval path) 글에서 언급했듯이, 안전성 검사(safety check)는 모델 호출(model call)로 실행되며, 판결(verdict) 없이 거부하는 경우도 거부를 생성했지만 흔적을 남기지 않은 모델 호출입니다. 당신은 그 턴에 대해 비용을 지불합니다. 아무 곳에도 발생했다는 기록이 남아있지 않기 때문에 경고하거나, 계산하거나, 청구할 수 없습니다.
이것이 바로 계측(instrumentation)의 전체 경제학을 한 문장으로 요약한 것입니다: 기록을 출력하지 않는 경로에 대해서는 예산을 책정할 수 없다. 그 경로는 무료가 아니며, 보이지 않고, 그리고 눈에 보이지 않는 비용이야말로 증가하는 비용입니다.
이것은 또한 비용 대시보드가 어떤 에이전트가 돈을 썼는지 알려줄 수 없었던 이유이며, 우리가 누가 무엇을 승인했는지 모르는 이유이기도 합니다. 둘 다 진정한 분석(analytics) 문제는 아닙니다. 둘 다 다른 옷을 입은 누락된 분모(missing denominator)입니다.
이 모든 것의 한계
하나의 프로젝트, 하나의 전화기, 3개월, 네 번의 사고. 이것이 실제 관측 가능성(observability) 비용을 가진 팀에 일반화될 수 있는지는 제가 말씀드릴 수 없습니다. 만약 당신의 플랫폼이 분모를 공짜로 제공한다면 이 모든 것은 당신에게 적용되지 않을 것입니다.
제가 확신하는 것은 더 좁은 범위입니다: 이 네 가지 일이 발생했고, 각각 개별적으로는 작았지만, 함께 모여 잘못된 곳을 찾는 데 약 사흘의 비용이 들었습니다. 만약 스스로 보고하는 실행(run)을 가지고 있고 그 분모를 수동으로 확인하지 않았다면, 제가 시작할 곳은 바로 그곳입니다.
The Agent Loop 관련 글
- [당신의 에이전트 비용 문제는 모델이 아닙니다. 루프(loop) 문제입니다.] (https://dev.to/theagentloop/your-agents-cost-problem-isnt-the-model-its-the-loop-13a3)
- [AI 에이전트가 당신의 돈을 쓰게 될 때, 손실은 누가 감당하는가?] (https://dev.to/theagentloop/who-eats-the-loss-when-your-ai-agent-spends-your-money-433a)
- [당신의 에이전트 안전 점검(safety check)이 이제 명령어마다 모델 호출을 수행합니다.] (https://dev.to/theagentloop/your-agents-safety-check-now-runs-a-model-call-per-command-1dgc)
- [당신의 에이전트 승인에는 만료일이 필요합니다.] (https://dev.to/theagentloop/your-agents-approval-needs-an-expiry-date-2e4k)
출처(Sources)
- Claude Code: 권한 모드(Permission modes): 거절 시 판결(verdict)이 없어 알림이나
/permissions항목 생성이 발생하지 않는 경우, 그리고 턴을 중단시키는 연속 10회 제한. - Forem
skip_indexing?: 우리의 인덱스 스윕(index sweeps) 뒤에 있는 플랫폼 게이트. - RFC 9110: HTTP 의미론(HTTP Semantics): 프로브 루프가 잘못 처리했던 404와 403의 구분을 위한 상태 코드.
FAQ
항상 ok를 출력하는 영수증이 아무 문제도 없다는 증거인가요?
아닙니다. 그것은 ok를 출력하는 코드 경로가 완료될 때까지 실행되었다는 증거일 뿐입니다. 만약 동일한 아티팩트(artifact)가 확인되는 대상을 제공한다면, 당신이 배운 것은 그것뿐입니다.
제가 직접 만든 것에 분모(denominator)를 추가하려면 어떻게 해야 하나요?
실제로 확인한 항목 목록을 출력하고, 이를 독립적으로 생성된 두 번째 열거(enumeration)와 비교하세요. 만약 다르면 영수증을 작성하는 대신 실행을 실패시키세요. 저희는 하드코딩된 ID 목록을 실제 게시된 목록과 비교했고, 이것이 16개 중 15번째 버그가 마침내 드러나게 했습니다.
로그 라인이 누락되는 것이 성공으로 간주되어야 하나요?
다른 무언가가 명시적으로 성공을 단언하는 경우에만 그렇습니다. 출력의 부재는 증거의 부재이며, 경고 없이 종료되는 시스템에서는 도구링(tooling)이 마주칠 수 있는 가장 오해하기 쉬운 상황입니다. 왜냐하면 그것은 깔끔하게 파싱되기 때문입니다.
에이전트 프레임워크를 사용한다면 걱정할 가치가 있을까요?
아마도 그럴 가능성이 적고, 이것이 솔직한 입장입니다. 대부분의 호스팅된 프레임워크는 예상되는 단계 목록(expected step list), 즉 분모(denominator)가 포함된 추적 기록(traces)을 제공합니다. 이 격차(gap)가 우리에게 나타난 이유는 측정 대상이 제가 작성한 스크립트였고, 그 스크립트를 아무도 문서화하지 않은 페이지 크기 제한(page-size cap)이 있는 플랫폼에 대해 검사하는 것이었기 때문입니다. 이는 소규모 프로젝트의 구체적이고 상당히 흔한 형태이며, 플랫폼 팀을 가진 팀에게는 다소 이례적인 경우입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기