메모리 월 (The Memory Wall)
요약
AI 에이전트가 소프트웨어 개발 작업에서 낮은 성공률(2.5%)을 보이는 근본 원인인 '맥락(Context)의 부재' 문제를 분석합니다. 단순한 코드 생성을 넘어, 시스템의 의존성과 제약 조건을 이해하는 명세 및 스킬 레이어의 중요성을 강조합니다.
핵심 포인트
- AI 에이전트의 실제 작업 완료율은 약 2.5%에 불과함
- 실패의 주된 원인은 시스템 충돌이 아닌 맥락(Context) 결여로 인한 요구사항 미충족
- 성공적인 에이전트는 작업에 필요한 모든 컨텍스트가 자기 완결적으로 존재함
- 단순 생성을 넘어 명세(Specification)와 스킬(Skill) 레이어 구축이 필수적임
서문 (Preface)
본론으로 들어가기에 앞서 솔직하게 밝히고 싶은 점이 있습니다. 이 글에서 언급되는 프레임워크 중 그 어떤 것도 제 것이 아닙니다. 여기에 담긴 아이디어들은 저보다 훨씬 더 깊고 오래 이 문제를 고민해 온 두 분으로부터 왔으며, 제가 한 마디 더 하기 전에 그분들에게 온전한 공로를 돌려야 마땅합니다.
Dan Shapiro — Glowforge의 CEO이자 Wharton 연구원이며, 이 모든 대화에 어휘를 부여한 분입니다. 그의 블로그 포스트 "The Five Levels: from Spicy Autocomplete to the Dark Factory"는 제가 말하고자 하는 모든 것의 개념적 중추입니다. 원문을 읽어보세요. 짧고 날카로우며, 가장 좋은 방식으로 당신을 불편하게 만들 것입니다. danshapiro.com
Layer 3(레이어 3)는 여기서 시작됩니다.
빌딩의 12가지 이슈: 작동하는 API, Gherkin 테스트 스위트, Pact 계약, CI/CD 파이프라인, 그리고 3개 계층에 걸쳐 6개의 활성 기술을 가진 스킬 레이어(skill layer). 이 정도 규모의 대부분의 프로젝트가 구축하는 것보다 더 많은 인프라입니다.
하지만 이것만으로는 충분하지 않습니다.
이번 호는 '왜'에 관한 것입니다. 추상적인 의미가 아니라, 구체적이고 기계적인 의미에서의 '왜'입니다. 맥락(context)의 부재가 어떻게 생산 실패(production failure)로 이어지는지 그 정확한 사건의 연쇄는 무엇인가요? 그리고 Layer 3가 방지하기 위해 존재하는, 명세 레이어(specification layer)와 스킬 레이어(skill layer)가 놓치는 실패의 범주는 무엇인가요?
솔직한 수치들 (The honest numbers)
세 가지 데이터 포인트. 세 가지 서로 다른 방법론. 하지만 그 밑바탕에는 동일한 메커니즘이 흐르고 있습니다.
2.5%의 완료율
Remote Labor Index 연구는 Upwork에서 가져온 실제 소프트웨어 프리랜서 작업 — 실제 급여, 실제 고객, 실제 수락 기준이 존재하는 작업 — 을 바탕으로 AI 에이전트의 성능을 측정했습니다. 핵심 결과는 다음과 같습니다: 에이전트들은 약 2.5%의 작업만을 성공적으로 완료했습니다.
여기서 말하는 "실패 (failure)"는 시스템 충돌이나 예외 (exception)를 의미하는 것이 아닙니다. 이는 수락 기준 (acceptance criteria)을 충족하지 못하는 출력을 의미합니다. 즉, 일부 테스트는 통과하지만 다른 테스트는 깨뜨리는 코드, 명시된 요구사항은 해결하지만 명시되지 않은 제약 조건 (constraint)을 위반하는 구현, 고립된 상태에서는 작동하지만 에이전트가 연결되어 있음을 인지하지 못한 다운스트림 (downstream)의 무언가를 망가뜨리는 변경 사항 등을 말합니다. 에이전트들은 출력을 생성하는 데 실패한 것이 아닙니다. 인간 검토자 (human reviewer)가 수용할 수 있는 출력을 생성하는 데 실패한 것입니다.
성공한 2.5%의 작업들이 가진 공통점은 그것들이 자기 완결적 (self-contained)이라는 점입니다. 성공에 필요한 컨텍스트 (context)가 호출 시점에 이미 존재합니다. 실패한 97.5%는 작업 설명 외부에 존재하는 컨텍스트를 필요로 합니다. 예를 들어, 이전 결정이 왜 내려졌는지, 변경되는 코드에 어떤 다른 시스템들이 의존하고 있는지, 현재 구현의 어떤 속성이 불변량 (invariants)이고 어떤 것이 부수적인 것인지와 같은 정보들입니다.
95%의 기업용 파일럿 실패율
McKinsey, Gartner, 그리고 MIT Sloan의 연구 결과는 일치합니다. 기업용 AI 파일럿 (pilot)의 대다수는 프로덕션 배포 (production deployment) 단계에 도달하지 못합니다. 유용한 것을 전혀 만들어내지 못했다는 뜻이 아닙니다. 많은 파일럿이 인상적인 데모와 작동하는 프로토타입 (prototype)을 만들어냈습니다. "프로덕션에 도달하지 못한다"는 것은 해당 시스템이 실제 고객에게 서비스를 제공하기 위해 배포된 적이 없음을 의미합니다.
"파일럿에서 작동함"에서 "프로덕션에서 실행됨"으로 넘어가는 단계에서 실패가 집중됩니다. 주요 원인은 순서대로 다음과 같습니다: 데이터 및 컨텍스트 문제, 파일럿 단계에서는 접하지 못했던 보안 및 컴플라이언스 (compliance) 요구사항, 실제 외부 시스템에 대한 통합의 취약성 (integration brittleness), 그리고 파일럿 단계에서 존재를 모른 채 깨뜨려 버린 문서화되지 않은 불변량 (invariants)들입니다.
이것은 역량 (capability)의 문제가 아니라 컨텍스트 (context)의 문제입니다. 작동하는 코드를 작성하고 외부 API와 통합하는 역량은 파일럿 단계에서 이미 존재하며, 데모가 이를 보여줍니다. 파일럿에 부족한 것은 파일럿 환경 외부의 제약 조건들, 즉 법적 제약, 보안 불변량, 통합 계약 (integration contracts), 운영 이력(operational history)과 일치하는 결정을 내릴 수 있는 컨텍스트입니다.
생산성 역설 (productivity paradox)의 재고
Issue #2에서는 다음과 같은 발견을 기록했습니다: AI 도구를 사용하는 숙련된 개발자들은 스스로가 24% 더 빠르다고 믿었지만, 실제로는 작업 시간이 19% 더 오래 걸렸습니다. 이를 Layer 3의 관점에서 다시 살펴보면, 이는 일차적으로 기술(skill)의 문제나 사양(spec)의 문제가 아닙니다. 이것은 관리(stewardship)의 문제입니다.
19%의 속도 저하는 통합 작업(integration work), 리뷰 작업(review work), 그리고 초기 리뷰는 통과했으나 아무도 예상하지 못한 상황에서 실패하는 AI 생성 코드의 디버깅(debugging) 과정에 집중되어 있습니다. 에이전트(agent)는 명시된 문제에 대해 작동하는 솔루션을 생성합니다. 그러면 개발자는 해당 솔루션이 명시되지 않은 제약 조건(unstated constraints)을 위반하지 않는지 확인하는 데 시간을 소비합니다. 즉, 기존 동작에 의존하는 무언가를 망가뜨리는지, 특정 이유로 존재했던 가드(guard)를 제거하는지, 혹은 시스템의 실제 설계와 충돌하는 아키텍처적 가정(architectural assumption)을 하는지 등을 확인해야 합니다. 그 검증 작업이 시간을 잡아먹는 것입니다. 만약 에이전트가 처음부터 완전한 컨텍스트(context)를 가지고 작동했다면 이러한 작업은 필요하지 않았을 것입니다.
데이터가 실제로 말하고 있는 것
세 가지 연구가 동일한 메커니즘을 보여줍니다:
1단계 — 에이전트가 불완전한 컨텍스트와 함께 작업을 받습니다. 요구사항이 모호해서 불완전한 것이 아닙니다. 이 세션 이전에 결정이 내려졌고, 코드베이스 외부에 제약 조건이 존재하며, 문서화되지 않은 운영 경험을 통해 불변량(invariants)이 확립되었기 때문에 불완전한 것입니다. 에이전트는 컨텍스트가 불완전하다는 사실을 알지 못합니다. 에이전트의 입장에서는 작업 설명과 코드베이스가 완전한 사양(specification)입니다.
2단계 — 에이전트가 국지적으로 유효한 추론(locally-valid inferences)으로 공백을 채웁니다. 정보가 없는 상태에서 에이전트는 자신이 볼 수 있는 것만을 바탕으로 추론합니다. 에이전트는 설명할 수 없는 패턴이 의도적인 것이 아니라 부수적인 것이라고 추론합니다. 문서화된 내용을 찾을 수 없는 체크 로직이 핵심적인 것이 아니라 방어적인 것이라고 가정합니다. 임의적으로 보이는 아키텍처 결정(architectural decision)을 최적화할 수 있는 대상으로 취급합니다. 각각의 추론은 개별적으로는 타당합니다. 하지만 각 추론은 자신이 무엇을 무효화(overriding)하고 있는지 모르는 상태에서 이루어집니다.
3단계 — 에이전트는 국소적으로는 정확하지만 전역적으로는 틀린 출력을 생성합니다. 코드는 컴파일됩니다. 테스트도 통과하고, PR 리뷰도 문제가 없습니다. 구현은 명시된 요구사항을 충족합니다. 하지만 동시에 에이전트가 인바리언트(invariant)임을 알지 못했던 것을 위반하거나, 3년 전 발생한 사고 때문에 존재했던 가드를 제거하거나, 스테이징 환경에서는 사실이었지만 프로덕션 환경에서는 거짓인 외부 시스템에 대한 가정을 합니다.
4단계 — 실패는 변경 사항에서 가장 먼 지점에서 표면화됩니다. 단위 테스트(unit tests)에서는 아닙니다. 통합 테스트(integration tests)에서도 아닙니다. 운영 환경(production), 즉 테스트가 커버하지 못한 조건 하에서, 목업(mock)과 다르게 동작하는 실제 외부 시스템을 상대로, 아무도 문서로 남기지 않은 방식으로 개발 환경과 다른 운영 맥락에서 발생합니다.
이것이 메커니즘입니다. 단순히 '에이전트에게 더 많은 컨텍스트가 필요하다'는 일반적인 의미가 아닙니다. 구체적으로 말하자면, 에이전트는 수년에 걸쳐 축적된 컨텍스트를 기반으로 설계된 시스템 내에서 작동하는데, 이 컨텍스트는 기계가 읽을 수 있는 형태로 만들어지지 않았습니다.
실패 분류(Failure Taxonomy)
네 가지 실패 모드. 각각은 정교하며, 하나는 본 프로젝트의 역사적 사례로, 다른 하나는 본 프로젝트와 관련 없는 프로덕션 시스템의 사례로 설명됩니다.
실패 모드 1: 프로덕션 환경에 대한 무지(Production Blindness)
정의: 에이전트는 프로덕션 환경과 비프로덕션 환경을 구별할 수 없습니다. 컨텍스트 내에서 달리 알려주는 것이 없기 때문에, 환경별 리소스를 동일한 유형의 교환 가능한 인스턴스로 취급합니다.
메커니즘: 환경 식별은 코드베이스에 거의 기록되어 있지 않습니다. 설정 값들은 다른 환경의 다른 엔드포인트를 가리키지만, 에이전트는 그 정보가 명시적으로 제공되지 않는 한 어떤 엔드포인트가 프로덕션인지 알지 못합니다. 네이밍 컨벤션(Naming conventions) — 버킷 이름, 데이터베이스 이름, 서비스 식별자 등 — 은 종종 접두사나 접미사만으로 환경 간에 공유됩니다.
프로젝트 예시 — Issue #6: CI/CD 파이프라인의 초기 YAML 단계는 pytest 세션 피스처 (fixtures)와 독립적으로 모의 서버 (mock servers)를 시작하도록 설정되어 있었습니다. YAML을 작성하는 에이전트는 8091 포트가 이미 테스트 피스처에 의해 점유되어 있다는 사실을 알 방법이 없었습니다. 에이전트는 "8091 포트에서 서버 시작"이라는 명령을 보고 서버를 실행했습니다. 그런데 피스처 코드 또한 서버를 실행했습니다. 두 계층 모두 자신이 동일한 리소스를 소유하고 있다고 믿었습니다. OSError: [Errno 98] Address already in use. 각각의 계층은 개별적으로는 틀리지 않았습니다. 그들에게는 서버 생명주기 (lifecycle)에 대해 누가 권한을 갖는지에 대한 공유된 모델이 없었습니다. 인프라 수준에서의 운영 환경에 대한 무지 (Production blindness)입니다.
운영 시스템 예시: 한 회사가 notifications-staging과 notifications-prod라는 두 개의 환경에서 알림 서비스 (notification service)를 운영하고 있습니다. 두 설정은 구조적으로 동일합니다. "알림 전달 시스템의 신뢰성 개선"이라는 과업을 맡은 에이전트는 자신이 스테이징 환경이라고 믿는 곳에 재시도 로직 (retry logic)을 추가합니다. 설정이 동일하고 컨텍스트 내에 특정 환경을 건드려서는 안 된다는 표시가 없었기 때문입니다. 이 변경 사항은 운영 환경 (production)에 적용됩니다. 마케팅 이메일 수신 거부를 선택한 고객들이 프로모션 알림을 세 번이나 받게 됩니다. 에이전트는 수신 거부 상태를 수정하지 않았습니다. 대신, 이전에는 종료되었어야 할 전달을 재시도 가능한 것으로 취급하게 된 서비스의 재시도 동작을 수정했습니다. 실패의 원인은 에이전트가 무엇을 했느냐가 아니라, 무엇을 알지 못했느냐에 있습니다.
이를 방지하는 산출물 (Artifact): 환경 구분 문서 (Environment discrimination documents) — CLAUDE.md 내의 명시적인 섹션이나 별도의 ENVIRONMENTS.md 파일로, 운영 리소스를 명시하고, 운영 리소스를 비운영 리소스와 구분하는 특징을 설명하며, 에이전트가 각 환경에서 수정할 수 있는 것과 수정할 수 없는 것을 명시합니다. 에이전트는 코드 구조만으로는 환경의 경계를 추론할 수 없습니다. 이 문서는 경계를 명시적이고 기계가 읽을 수 있는 (machine-readable) 형태로 만들어 줍니다.
실패 모드 2: 역사적 망각 (Historical Amnesia)
정의 (Definition): 에이전트가 현재 세션 이전에 내려진 결정에 접근하거나 그에 대해 추론할 수 없는 상태를 의미합니다. 에이전트는 자신이 이미 내려진 결정을 다시 내리고 있다는 사실을 인지하지 못한 채, 제1원리 (first principles)로부터 패턴을 재도출합니다. 이 과정은 때로는 올바르게, 때로는 원래 결정되었던 방식과는 다르게 이루어집니다.
메커니즘 (The mechanism): 결정사항들은 쿼리가 불가능한 (non-queryable) 형태로 시스템에 축적됩니다. 결정을 내린 사람들의 머릿속에 있거나, 왜 X가 잘못되었는지 설명 없이 "X를 되돌림 (revert X)"라고만 적힌 커밋 메시지에 있거나, 아무도 열어보지 않는 공유 드라이브에 놓인 사후 분석 (post-mortem) 문서에 존재합니다. 에이전트에게 코드베이스의 현재 상태가 주어지면, 에이전트는 결정 사항 자체는 보지만 결정의 흔적 (decision trails)은 보지 못합니다. 에이전트는 가드 절 (guard clause)이 존재한다는 것은 알지만, 그것을 추가하게 만든 운영 환경에서의 장애 (production incident)는 보지 못합니다.
프로젝트 예시 — 이슈(Issues) #2, #3, #8: And the response is returned within 12 seconds라는 단계는 이슈 #2에서 도입되었습니다. 통과하기에는 충분히 구체적입니다. 하지만 기준점 (anchor)이 없습니다. 12초는 언제부터 측정된 것일까요? 클라이언트가 요청을 보낼 때부터인가요? 마지막 재시도 (retry)가 실행될 때부터인가요? 이슈 #3의 에이전트는 이 단계를 읽고 타임아웃/재시도 로직을 올바르게 도출하였고 (2회 시도 × 5초 = 10초, 12초 이내), 다음 단계로 넘어갔습니다. 이 모호함은 이슈 #8의 사양 감사 (spec audit)에서 발견되기 전까지 세 번의 세션 동안 조용히 상속되었습니다. 이 사양은 두 명의 서로 다른 에이전트가 다르게 구현했을 수도 있는, 기준점이 없는 측정값을 포함한 채 세 번의 이슈 동안 테스트를 통과해 왔던 것입니다.
운영 시스템 사례 (Production-system example): 한 결제 서비스는 결제 요청에 대해 30초의 멱등성 윈도우 (idempotency window)를 가지고 있습니다. 이는 3년 전, TCP 연결 끊김으로 인해 고객에게 중복 결제가 발생했던 운영 사고 (production incident) 이후 추가된 것입니다. 이 윈도우는 결제 핸들러 (charge handler) 내의 체크 로직을 통해 강제됩니다. "결제 흐름의 지연 시간 (latency) 단축"이라는 과제를 맡은 에이전트는, 처음 발생하는 결제 요청의 99%에 대해 이 멱등성 체크가 오버헤드 (overhead)라고 판단합니다. 에이전트는 응답 이후에 비동기적 (asynchronously)으로 실행되도록 해당 체크 로직을 리팩터링 (refactor)합니다. 처음 발생하는 결제에 대해서는 지연 시간이 줄어듭니다. 하지만 윈도우 내에서 재시도되는 결제의 경우, 비동기 체크가 완료되기 전에 이미 두 번째 결제가 처리되어 버립니다. 결국 사고가 재발합니다. 멱등성 윈도우는 최적화 대상처럼 보였지만, 사실은 시스템을 지탱하는 보호 장치 (load-bearing guard)였습니다. 에이전트는 해당 기능이 왜 그런 방식으로 구축되었는지를 설명하는 사고 보고서 (incident report)에 접근할 수 없었습니다.
이를 방지하는 산출물 (Artifact): 에이전트가 읽을 수 있는 불변성 섹션 (invariant sections)이 포함된 아키텍처 결정 기록 (Architecture Decision Records, ADR) — 무엇이 결정되었는지뿐만 아니라 '왜' 결정되었는지를 기록하며, 해당 결정을 뒤집었을 때의 결과(consequences)를 명시적으로 기술하는 문서입니다. ADR이 저장소 (repository)에 존재하고 에이전트가 관련 코드 경로를 수정하기 전에 읽도록 지시할 수 있는 일관된 형식을 따른다면, 이는 기계 판독 가능 (machine-readable)해집니다.
실패 모드 3: 의존성 무지 (Dependency Ignorance)
정의: 에이전트가 자신이 어떤 외부 시스템에 영향을 미치고 있는지, 그 시스템들의 실패 모드 (failure modes)는 무엇인지, 또는 어떤 다운스트림 소비자 (downstream consumers)가 자신의 출력값에 의존하고 있는지를 알지 못하는 상태를 의미합니다. 에이전트는 눈앞에 보이는 시스템에 대해서만 동작하며, 그와 연결된 시스템들에 대해서는 눈이 멀어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기