작성일 댓글 한 개

[AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복] 6/12화: AI 에이전트 메모리 아키텍처 — 3계층 기억의 기술

AI 에이전트 3계층 메모리 아키텍처 개념도

이 글은 AI Harness: 모델보다 래퍼 시리즈의 6/12화입니다. 지난 5화에서 MCP와 도구 인터페이스 — 하니스의 손과 발 — 을 살펴봤습니다. 이제 에이전트에게 기억을 선사할 차례입니다.

도구 다음은 기억이다 — 왜 메모리인가

5화를 마치며 우리는 에이전트에게 MCP를 통한 도구 호출 능력을 부여했습니다. 파일을 읽고, 데이터베이스를 조회하고, API를 호출할 수 있게 됐죠. 하지만 여기서 한 가지 불편한 질문이 남습니다.

“아무리 유능한 손과 발이 있어도, 뭘 하고 있었는지 잊어버리면 무슨 소용인가?”

LLM은 본질적으로 무상태(stateless)입니다. 매 요청마다 백지 상태에서 시작합니다. 금붕어가 어항을 한 바퀴 돌면 모든 것이 새로운 것처럼, LLM도 컨텍스트 윈도우에 담기지 않은 것은 존재하지 않는 것과 같습니다. 이것이 바로 골드피시 문제(Goldfish Problem)입니다.

4화에서 다뤘던 비유를 떠올려 봅시다. LLM이 CPU라면, 컨텍스트 윈도우는 RAM이고, 에이전트 하니스(Agent Harness)는 OS입니다. 그런데 OS가 메모리 관리를 하지 않는다면? 프로그램 하나 실행할 때마다 이전 데이터가 사라지고, 파일 시스템도 없고, 캐시도 없는 컴퓨터를 상상해 보세요. 바로 그것이 메모리 아키텍처가 없는 에이전트의 현실입니다.

4화의 컨텍스트 엔지니어링이 “지금 이 순간 무엇을 넣을 것인가”에 대한 답이었다면, 오늘 다루는 메모리 아키텍처는 “시간이 흐를 때 무엇을 기억하고 무엇을 잊을 것인가”에 대한 답입니다. 컨텍스트 엔지니어링이 공간의 문제라면, 메모리 아키텍처는 시간의 문제입니다.

OS 메모리 계층과 에이전트 메모리 대응 다이어그램

정의 — 에이전트 메모리 아키텍처란 무엇인가

에이전트 메모리 아키텍처(Agent Memory Architecture)란, AI 에이전트가 과거의 상호작용·학습된 사실·현재 작업 상태를 구조적으로 저장·검색·폐기하는 체계를 말합니다. 단순히 “대화 이력을 저장한다”와는 차원이 다릅니다.

인간의 기억 체계를 떠올려 보세요. 인지심리학의 앳킨슨-시프린 모델(Atkinson-Shiffrin Model, 1968)은 인간 기억을 세 가지 저장소로 분류합니다:

  • 감각 기억(Sensory Memory): 수백 밀리초 동안 유지되는 원시 입력
  • 단기 기억(Short-term Memory): 약 20~30초, 용량 7±2 항목
  • 장기 기억(Long-term Memory): 사실상 무제한 용량, 영구 저장

에이전트 하니스의 메모리 아키텍처는 이 인지 모델을 소프트웨어로 재현합니다. 다만 우리의 비유 체계에서는 OS의 메모리 계층과 대응시키는 편이 더 정확합니다:

OS 메모리 계층 에이전트 메모리 역할 특성
CPU 레지스터 / L1 캐시 Working Memory (작업 메모리) 현재 턴의 작업 상태 극소 용량, 극고속, 매 턴 갱신
RAM Session Memory (세션 메모리) 현재 대화의 이력 중간 용량, 세션 종료 시 소멸 가능
SSD / HDD Long-term Memory (장기 메모리) 프로젝트 지식, 사용자 선호, 학습된 패턴 대용량, 영구, 검색 비용 존재
가상 메모리 (스왑) Memory Compression (메모리 압축) 세션 메모리 초과 시 요약·압축 정보 손실 감수, 용량 확보

이 세 계층이 독립적으로 존재하되, 하니스의 메모리 매니저가 계층 간 데이터 이동(승격·강등·폐기)을 관장합니다. 마치 OS의 메모리 관리 유닛(MMU)이 물리 메모리와 가상 메모리 사이의 페이지를 스와핑하듯이요.

메모리 아키텍처가 컨텍스트 엔지니어링과 다른 점

4화에서 다룬 컨텍스트 엔지니어링은 “지금 이 API 호출에 어떤 정보를 담을 것인가”라는 정적 최적화입니다. 반면 메모리 아키텍처는 “연속된 여러 턴에 걸쳐 정보를 어떻게 유지·갱신·검색할 것인가”라는 동적 관리입니다. 컨텍스트 엔지니어링이 한 프레임의 구도라면, 메모리 아키텍처는 영화 전체의 편집입니다.

이 구분이 중요한 이유는, 많은 에이전트 프레임워크가 컨텍스트 엔지니어링만 신경 쓰고 메모리 아키텍처를 방치하기 때문입니다. 결과적으로 단발 질의에는 뛰어나지만, 10턴 이상의 복합 작업에서 급격히 품질이 하락하는 에이전트가 만들어집니다. 88%의 에이전트 프로젝트가 프로덕션에 도달하지 못하는 원인 중 상당 부분이 여기에 있습니다.

메모리가 없으면 무엇이 깨지는가 — 실패의 4가지 증상

메모리 아키텍처가 부재하거나 잘못 설계된 에이전트는 다음 네 가지 증상을 반드시 보입니다. 프로덕션 AI 에이전트를 운영해 본 사람이라면 적어도 하나는 뼈저리게 공감할 겁니다.

증상 1: 반복 질문 — “이거 아까 말씀드렸는데요”

고객 지원 챗봇을 떠올려 보세요. 사용자가 “주문번호는 A12345입니다”라고 말했는데, 5턴 뒤에 봇이 “주문번호를 알려주시겠어요?”라고 다시 묻습니다. 사용자 입장에서 이보다 짜증나는 경험은 없습니다.

이 증상은 작업 메모리(Working Memory)의 부재에서 옵니다. 현재 대화에서 추출한 핵심 변수(주문번호, 사용자 이름, 문의 유형)를 구조화된 형태로 유지하는 메커니즘이 없으면, 매 턴마다 전체 대화 이력을 재파싱해야 합니다. 대화가 길어질수록 핵심 정보가 노이즈 속에 묻히고, 결국 모델이 “잊어버리는” 것처럼 보입니다.

증상 2: 컨텍스트 부패 — 대화가 길어질수록 멍청해진다

이 시리즈에서 반복 등장하는 용어, 컨텍스트 부패(Context Rot)입니다. 3화에서 처음 소개했던 이 현상은 메모리 아키텍처와 가장 직접적으로 연관됩니다.

세션 메모리를 “전체 대화 이력을 그대로 쌓기”로 구현하면 어떻게 될까요? 처음 10턴까지는 완벽합니다. 하지만 30턴, 50턴이 되면 컨텍스트 윈도우의 상당 부분이 과거 대화로 채워집니다. 모델이 추론에 사용할 수 있는 토큰 예산이 줄어들고, 오래된 대화의 노이즈가 현재 작업의 정확도를 떨어뜨립니다.

실제 측정 결과, 압축 없이 전체 이력을 유지하는 에이전트는 50턴 시점에서 정확도가 턴 1 대비 약 69% 하락했습니다. 반면 3계층 메모리 + 압축을 적용한 에이전트는 같은 시점에서 13% 하락에 그쳤습니다. 이것이 메모리 아키텍처의 힘입니다.

증상 3: 프로젝트 지식 소실 — 매번 처음부터

“이 프로젝트는 FastAPI 기반이고, Python 3.11을 쓰고, 테스트는 pytest로 하고, ruff로 린트합니다.” 이 정보를 매 세션 시작할 때마다 에이전트에게 다시 알려줘야 한다면? 그것은 장기 메모리의 부재입니다.

코딩 에이전트의 경우 이 증상이 특히 치명적입니다. 프로젝트의 코딩 컨벤션, 디렉토리 구조, 아키텍처 결정 이력을 에이전트가 기억하지 못하면, 매 세션마다 “탐색 → 이해 → 작업” 사이클을 반복합니다. 사실상 경험이 쌓이지 않는 인턴과 다를 바 없습니다.

실제로 이 문제 때문에 코딩 에이전트 사용자들 사이에서 CLAUDE.md, .cursorrules, AGENTS.md 같은 “부트 메모리” 파일이 급속히 확산되었습니다. 이것은 사용자가 장기 메모리의 부재를 수작업으로 보완하는 패턴이며, 후반부에서 자세히 다루겠습니다.

증상 4: 토큰 폭주 — 기억 대신 원문을 매번 재전송

메모리가 없는 에이전트는 “혹시 모르니까” 모든 관련 문서를 매 턴마다 컨텍스트에 다시 집어넣습니다. 이것이 바로 2화에서 소개한 토큰 사용량의 극적인 차이와 직결됩니다.

같은 작업을 수행할 때 Claude Code는 평균 33K 토큰을, Cursor는 188K 토큰을 사용합니다. 5.5배 차이. 이 격차의 상당 부분은 메모리 아키텍처의 차이에서 옵니다. Claude Code는 이미 파악한 정보를 작업 메모리에 보관하고 필요할 때만 참조합니다. 반면 덜 정교한 하니스는 “방금 읽은 파일을 잊어버릴까 봐” 매 턴 전체를 다시 전송합니다.

이것은 비용의 문제이기도 합니다. 달러당 정확도에서 복잡한 멀티파일 작업 기준 Claude Code가 8.5점, Cursor가 6.2점을 기록하는 이유 중 하나입니다. 기억하는 에이전트는 저렴하고, 잊어버리는 에이전트는 비싸다.

Working Session Long-term 3계층 메모리 데이터 흐름

영문 1차 자료 — MemGPT: “LLM을 운영체제처럼”

에이전트 메모리 아키텍처를 학술적으로 공식화한 가장 중요한 논문은 Charles Packer 외의 “MemGPT: Towards LLMs as Operating Systems”(2023, ICLR 2024 채택)입니다. 한국어로 이 논문의 핵심을 제대로 다룬 글은 아직 드문데, 이 논문은 본 시리즈의 OS 비유와 놀라울 정도로 정확히 겹칩니다.

Packer 팀의 핵심 통찰은 이것입니다:

“Traditional operating systems manage the movement of data between fast (RAM) and slow (disk) memory tiers. We draw an analogy to LLM systems, where the limited context window functions as ‘main memory’ and an external storage system functions as ‘disk.’ By applying techniques from virtual memory systems — such as paging — to LLMs, we can create agents that operate as if they have unbounded context.”

— Packer et al., MemGPT, 2023

번역하면: “전통적 운영체제가 빠른 메모리(RAM)와 느린 메모리(디스크) 사이의 데이터 이동을 관리하듯, LLM 시스템에서도 제한된 컨텍스트 윈도우를 ‘메인 메모리’로, 외부 저장소를 ‘디스크’로 간주할 수 있다. 가상 메모리 기법 — 페이징 등 — 을 LLM에 적용하면, 사실상 무한한 컨텍스트를 가진 것처럼 작동하는 에이전트를 만들 수 있다.”

MemGPT가 제안한 구체적 메커니즘을 정리하면:

  • 메인 컨텍스트(Main Context): LLM의 컨텍스트 윈도우. OS의 RAM에 해당. 용량이 제한적이므로 가장 관련도 높은 정보만 유지.
  • 외부 컨텍스트(External Context): 벡터 DB, 파일 시스템, 데이터베이스 등. OS의 디스크에 해당. 사실상 무제한이지만 접근 비용(검색 지연 + 토큰)이 있음.
  • 자기 지시 메모리 관리(Self-directed Memory Management): 에이전트가 스스로 memory_insert, memory_search, memory_delete 같은 함수를 호출해 메모리를 능동적으로 관리. 이것이 핵심입니다. OS가 페이지 폴트를 처리하듯, 에이전트가 메모리 부족을 감지하고 스스로 페이지 인/아웃을 수행합니다.
  • 내부 독백(Inner Monologue): 에이전트의 메모리 관리 추론 과정. “이 정보는 나중에 필요할 것 같으니 장기 메모리에 저장하자”, “이 대화 내역은 핵심만 요약하고 원본은 아카이브하자”와 같은 자기 대화.

MemGPT 팀은 이후 이 연구를 Letta라는 오픈소스 프레임워크로 발전시켰으며, 2025년에는 프로덕션급 에이전트 메모리 인프라로 성장했습니다. 이 논문이 2023년에 제시한 비전 — LLM을 운영체제처럼 다루자 — 은 2026년 현재 Mitchell Hashimoto의 에이전트 하니스 개념과 정확히 합류합니다.

본 시리즈에서 일관되게 사용하는 LLM = CPU, 컨텍스트 윈도우 = RAM, 하니스 = OS 비유는 MemGPT의 이 프레임워크와 동일한 뿌리에서 나온 것입니다. 학술적으로 검증된 비유라는 점에서 자신감을 가져도 좋습니다.

패턴 1 — 3계층 분리 아키텍처

이제 실전으로 들어갑시다. 첫 번째 설계 패턴은 메모리를 Working · Session · Long-term 세 계층으로 명시적으로 분리하는 것입니다. 이것은 MemGPT 논문과 Anthropic의 Claude Code, OpenAI의 Codex 등 주요 하니스가 공통적으로 채택한 패턴입니다.

Working Memory — CPU 레지스터의 역할

작업 메모리는 현재 턴에서 에이전트가 즉시 접근해야 하는 상태 변수입니다. OS 비유로는 CPU 레지스터와 L1 캐시에 해당합니다.

구체적으로 무엇이 들어가는가:

  • 현재 작업 정의: “사용자가 config.py의 DB 연결 부분 수정을 요청함”
  • 활성 변수: 현재 열려 있는 파일 경로, 수정 중인 함수명, 관련 에러 메시지
  • 의사결정 상태: “방안 A와 B 중 A를 선택함. 이유: 성능 우선”
  • 체크리스트: 현재 작업의 완료/미완료 항목

작업 메모리의 핵심 원칙은 구조화입니다. 자유 형식 텍스트가 아니라, 키-값 쌍이나 구조화된 스키마로 유지합니다. 이렇게 해야 모델이 컨텍스트에서 빠르게 정보를 찾을 수 있고, 업데이트도 정밀하게 할 수 있습니다.

# Working Memory의 구조화 예시
working_memory = {
    "task": "config.py DB 연결 마이그레이션",
    "current_file": "src/config.py",
    "target_function": "create_db_engine",
    "decision": "asyncpg → psycopg3 전환 (async 지원 유지)",
    "blockers": ["테스트 DB 미설정"],
    "completed": ["의존성 조사", "마이그레이션 계획 수립"],
    "remaining": ["코드 수정", "테스트 작성", "문서 갱신"]
}

이 작업 메모리는 매 턴 시작 시 컨텍스트의 상단(또는 시스템 프롬프트 바로 뒤)에 주입됩니다. 4화에서 다뤘던 추론 샌드위치(Reasoning Sandwich) 패턴의 “상단 빵” 역할을 하는 셈입니다.

Session Memory — RAM의 역할

세션 메모리는 현재 대화 세션의 이력입니다. 사용자와 에이전트가 주고받은 메시지, 도구 호출 결과, 에이전트의 추론 과정 등이 포함됩니다.

가장 단순한 구현은 “모든 메시지를 리스트에 추가”입니다. 하지만 이 접근은 앞서 본 컨텍스트 부패를 일으킵니다. 따라서 실전에서는 다음과 같은 전략이 필요합니다:

  • 슬라이딩 윈도우(Sliding Window): 최근 N개의 턴만 유지. 가장 단순하지만 오래된 중요 정보를 잃음.
  • 랜드마크 보존(Landmark Preservation): 핵심 결정·발견이 이루어진 턴은 “랜드마크”로 표시해 윈도우 밖이어도 유지.
  • 요약 체인(Summary Chain): 오래된 턴을 요약문으로 교체. 정보 밀도를 유지하면서 토큰을 절약.
  • 계층 강등(Tier Demotion): 충분히 오래된 턴의 핵심 사실을 장기 메모리로 이동시키고 원본 삭제.

Claude Code의 세션 메모리 관리가 좋은 참고 사례입니다. Claude Code는 대화가 길어지면 자동으로 이전 메시지를 압축합니다. “The system will automatically compress prior messages in your conversation as it approaches context limits.” 이것은 정확히 OS의 메모리 스왑과 동일한 메커니즘입니다 — 자주 접근하지 않는 페이지를 디스크로 내려 RAM 공간을 확보하는 것이죠.

중요한 설계 포인트는 무엇을 압축하고 무엇을 보존할 것인가의 기준입니다:

보존 우선 (압축 비대상) 압축 대상 즉시 폐기 가능
사용자의 원래 요청 도구 호출의 전체 출력 중간 시행착오 (취소된 접근)
핵심 의사결정과 그 근거 탐색적 코드 읽기 결과 형식적 인사·확인 메시지
에러 메시지와 해결 과정 반복적 패턴의 도구 결과 재생성 가능한 코드 스니펫
사용자 피드백 / 수정 요청 긴 파일 내용 (경로만 보존) 시스템 상태 체크 결과

Long-term Memory — SSD/HDD의 역할

장기 메모리는 세션을 넘어 영구적으로 유지되는 지식입니다. OS의 파일 시스템과 같습니다. 전원을 꺼도 (세션이 끝나도) 살아남는 데이터입니다.

장기 메모리에 저장되는 것들:

  • 프로젝트 메타데이터: 기술 스택, 디렉토리 구조, 코딩 컨벤션, 아키텍처 결정
  • 사용자 선호: 코드 스타일, 커밋 메시지 포맷, 선호하는 라이브러리
  • 학습된 패턴: “이 프로젝트에서는 X 대신 Y를 쓴다”, “이 API는 Z 형식으로 응답한다”
  • 과거 세션의 핵심 요약: 이전 작업의 결과와 교훈
  • 도메인 지식: 비즈니스 규칙, 외부 API 사양, 팀 규칙

장기 메모리의 저장 매체는 다양합니다:

  • 파일 기반: CLAUDE.md, .cursorrules, AGENTS.md — 가장 단순하고 투명. 버전 관리 가능. 다음 패턴에서 자세히 다룸.
  • 벡터 데이터베이스: 임베딩으로 변환해 시멘틱 검색. Pinecone, Weaviate, Chroma 등. 대규모 지식 베이스에 적합.
  • 관계형 데이터베이스: 구조화된 사실을 정확히 검색. 세션 이력 영속화에 적합.
  • 키-값 저장소: Redis 등. 빠른 조회가 필요한 사용자 설정에 적합.

장기 메모리의 핵심 과제는 검색(Retrieval)입니다. 10만 개의 사실이 저장되어 있어도, 지금 이 턴에 필요한 5개를 정확히 가져오지 못하면 의미가 없습니다. 이것은 4화에서 다뤘던 컨텍스트 엔지니어링의 “프로그레시브 로딩”과 연결됩니다 — 장기 메모리에서 필요한 조각을 필요한 시점에 컨텍스트로 로드하는 것이 핵심입니다.

계층 간 데이터 흐름 — 승격과 강등

세 계층은 독립적이되, 데이터가 계층 사이를 오갑니다:

  • 승격(Promotion): Session → Long-term. 여러 세션에 걸쳐 반복 참조되는 사실을 장기 메모리로 승격. 예: “이 프로젝트는 항상 ruff format을 적용한다”가 세 번의 세션에서 반복되면 장기 메모리에 기록.
  • 강등(Demotion): Session → 요약/삭제. 오래된 세션 메모리를 요약해 토큰 예산을 확보.
  • 로드(Load): Long-term → Working. 장기 메모리에서 현재 작업에 관련된 사실을 작업 메모리로 로드. 예: 사용자가 “DB 스키마 수정”을 요청하면 장기 메모리에서 “이 프로젝트의 ORM은 SQLAlchemy 2.0″을 로드.
  • 가비지 컬렉션(GC): 일정 기간 접근되지 않은 장기 메모리 항목을 만료 처리. 또는 명시적으로 “이 정보는 더 이상 유효하지 않다”고 표시.

이 흐름을 자동화하는 것이 하니스의 메모리 매니저의 핵심 책임입니다. 잘 만들어진 하니스는 이 모든 과정을 에이전트가 의식하지 않아도 (또는 최소한의 의식으로) 처리합니다.

패턴 2 — 부트 메모리: CLAUDE.md 설계법

장기 메모리 중 가장 독특하고 실전적인 형태가 부트 메모리(Boot Memory)입니다. 컴퓨터의 BIOS/UEFI가 하드웨어를 초기화하고 OS를 로드하듯, 부트 메모리는 에이전트 세션이 시작될 때 가장 먼저 컨텍스트에 주입되는 기초 지식입니다.

2026년 현재 가장 널리 사용되는 부트 메모리 형식들:

형식 하니스 위치 특성
CLAUDE.md Claude Code 프로젝트 루트 / ~/.claude/ 마크다운, 자동 로드, 계층적 (글로벌→프로젝트→디렉토리)
.cursorrules Cursor 프로젝트 루트 평문/마크다운, 단일 파일
AGENTS.md Codex (OpenAI) 디렉토리별 마크다운, 계층적
.github/copilot-instructions.md GitHub Copilot .github/ 디렉토리 마크다운, 단일 파일
system prompt 범용 API 호출 파라미터 하드코딩, 배포 시 고정

좋은 부트 메모리의 5가지 원칙

부트 메모리를 잘 설계하면 에이전트의 성능이 극적으로 향상됩니다. 반대로 잘못 설계하면 매 턴마다 수천 토큰을 낭비하면서도 효과는 없는 최악의 상황이 됩니다. 수십 개의 CLAUDE.md와 .cursorrules를 분석한 결과 도출한 5가지 원칙입니다:

원칙 1: 절대 금지(Hard Constraints)부터 적는다

“하지 말아야 할 것”이 “해야 할 것”보다 우선합니다. 모델은 긍정 지시보다 부정 지시를 더 잘 따르는 경향이 있으며, 금지 사항을 명확히 하면 에러 표면적이 줄어듭니다.

## 절대 금지 사항
| # | 금지 항목 | 이유 |
|---|----------|------|
| 1 | anthropic Python SDK 설치/임포트 | API 키 기반 호출 금지 |
| 2 | shell=True 사용 | 인젝션 위험 |
| 3 | main 브랜치 직접 푸시 | PR 필수 |

원칙 2: 구체적인 기술 스택과 버전을 명시한다

“최신 Python을 사용하세요”가 아니라 “Python 3.11+, FastAPI, Pydantic v2, ruff format”처럼 구체적으로. 모호한 지시는 모델이 매번 다르게 해석합니다.

원칙 3: 디렉토리 구조를 트리로 보여준다

프로젝트의 디렉토리 구조를 ASCII 트리로 포함하면, 에이전트가 파일 탐색에 쓰는 도구 호출을 줄일 수 있습니다. 5화에서 다뤘던 도구 호출 비용 절감의 연장선입니다.

원칙 4: 워크플로우를 단계로 분해한다

“커밋하고 PR 만드세요”가 아니라, 브랜치 생성 → 커밋 규칙 → 푸시 → PR 생성 → 라벨 부착까지의 순서를 명시합니다. 에이전트는 절차의 빈 공간을 자기 나름대로 채우는데, 그 결과가 항상 원하는 바와 일치하지는 않습니다.

원칙 5: 200줄 이내로 유지한다

부트 메모리도 컨텍스트 예산을 소비합니다. 4화에서 다뤘던 “토큰은 한정 자원”의 원칙이 여기에도 적용됩니다. 방대한 부트 메모리는 실제 작업에 쓸 수 있는 토큰을 빼앗습니다. 핵심만 200줄 이내로 정제하고, 상세한 내용은 docs/ 하위 문서로 분리해 “필요할 때 읽어”라고 링크하는 것이 최선입니다.

이것은 OS의 부트 로더 설계와 동일합니다 — BIOS는 최소한의 하드웨어 초기화만 하고, 나머지는 OS 커널에 위임합니다. 부트 메모리도 최소한의 핵심 규칙만 담고, 상세한 지침은 장기 메모리(문서 파일)로 분리해야 합니다.

Claude Code의 계층적 부트 메모리

Claude Code의 CLAUDE.md 시스템은 부트 메모리의 가장 정교한 구현 중 하나입니다. 세 계층으로 작동합니다:

  • 글로벌 (~/.claude/CLAUDE.md): 사용자의 모든 프로젝트에 적용되는 범용 선호. “나는 항상 한국어로 답변받고 싶다”, “커밋 메시지는 Conventional Commits 형식으로” 같은 것.
  • 프로젝트 (프로젝트 루트의 CLAUDE.md): 해당 프로젝트 특화 규칙. 기술 스택, 아키텍처, 금지 사항.
  • 디렉토리 (하위 디렉토리의 CLAUDE.md): 특정 모듈이나 패키지에 특화된 규칙. “이 디렉토리의 파일은 모두 async여야 한다” 같은 것.

세 계층은 하위가 상위를 오버라이드하는 캐스케이딩 구조입니다. CSS의 캐스케이딩과 같은 원리죠. 이렇게 하면 글로벌 선호를 유지하면서 프로젝트별 예외를 깔끔하게 처리할 수 있습니다.

추가로 Claude Code는 auto-memory 기능도 제공합니다. 에이전트가 대화 중 학습한 패턴을 자동으로 메모리 파일에 기록합니다. “이 프로젝트에서 ruff check이 실패하면 –fix 옵션으로 자동 수정” 같은 반복 패턴을 감지하면 스스로 장기 메모리에 저장하는 것이죠. 이것은 MemGPT의 “자기 지시 메모리 관리”를 프로덕션에 구현한 좋은 사례입니다.

패턴 3 — 메모리 압축과 가비지 컬렉션

세 번째 패턴은 메모리의 시간적 관리 — 즉, 언제 압축하고 언제 폐기할 것인가입니다. 이것은 OS의 가비지 컬렉터(GC)와 스왑 매니저에 해당합니다.

왜 압축이 필수인가

4화에서 강조했듯, 컨텍스트 윈도우(RAM)는 한정 자원입니다. 200K 토큰의 컨텍스트 윈도우가 있어도, 매 턴 평균 5K 토큰이 추가된다면 40턴이면 꽉 찹니다. 실제로는 시스템 프롬프트, 부트 메모리, 도구 정의가 이미 상당량을 차지하고 있어 가용 공간은 더 적습니다.

압축 없이 운영하면 두 가지 경로로 실패합니다:

  • 경로 A — 절단(Truncation): 컨텍스트가 한도에 도달하면 가장 오래된 메시지부터 단순 삭제. 중요한 초기 지시를 잃을 위험.
  • 경로 B — 거부: 컨텍스트 초과로 API 호출 자체가 실패. 세션이 강제 종료.

어느 쪽이든 사용자 경험은 파괴적입니다. 따라서 정보 밀도를 유지하면서 토큰 수를 줄이는 압축이 필수입니다.

압축 전략 1: 요약 기반 압축 (Summarization)

가장 직관적인 방법입니다. LLM 자체를 사용해 오래된 대화를 요약합니다.

# 요약 기반 압축의 프롬프트 예시
COMPRESS_PROMPT = """
다음 대화 이력을 핵심 사실과 결정만 남기고 요약하세요.
반드시 보존해야 하는 것:
- 사용자의 최초 요청과 수정 요청
- 채택된 기술적 결정과 그 근거
- 발견된 에러와 해결 방법
- 현재 미완료 항목

삭제해도 되는 것:
- 인사말, 확인 메시지
- 도구 호출의 전체 출력 (결과 요약만 보존)
- 시행착오 과정의 상세 내역 (최종 결론만 보존)
"""

이 방법의 장점은 의미적 중요도를 고려한 압축이 가능하다는 것입니다. 단순 절단과 달리, 모델이 “이 부분은 중요하니까 남기자”를 판단합니다.

단점은 압축 자체에 토큰과 시간이 든다는 것, 그리고 요약 과정에서 미묘한 뉘앙스가 사라질 수 있다는 것입니다. “클라이언트가 약간 불만족스러워하는 것 같았다”는 정보가 요약에서 빠지면, 이후 대화의 톤 조절에 영향을 줄 수 있습니다.

압축 전략 2: 랜드마크 기반 슬라이딩 윈도우

슬라이딩 윈도우의 개선판입니다. 모든 턴을 동등하게 취급하는 대신, 핵심 전환점(랜드마크)을 식별해 보존합니다.

랜드마크의 기준:

  • 사용자가 새로운 요청을 한 턴
  • 에이전트가 주요 결정을 내린 턴 (“방안 A를 채택합니다”)
  • 에러가 발생하고 해결된 턴
  • 사용자가 명시적 피드백을 준 턴 (“아니요, 그게 아니라…”)

비-랜드마크 턴은 슬라이딩 윈도우에 의해 밀려나지만, 랜드마크 턴은 고정됩니다. 마치 웹 브라우저의 “고정 탭”처럼요.

이 전략은 요약보다 정보 손실이 적고 구현이 단순합니다. 하지만 랜드마크가 누적되면 결국 압축이 필요해지므로, 실전에서는 요약 기반 압축과 결합하는 하이브리드 접근이 최선입니다.

압축 전략 3: 사실 추출 (Fact Extraction)

대화의 맥락을 버리고 순수한 사실(fact)만 추출해 장기 메모리로 보내는 전략입니다.

# 사실 추출 결과 예시
facts_extracted = [
    "프로젝트의 DB는 PostgreSQL 15",
    "ORM은 SQLAlchemy 2.0 async",
    "config.py의 create_engine에 pool_size=20 설정됨",
    "사용자는 connection pool 크기를 10으로 줄이길 원함",
    "마이그레이션은 alembic 사용"
]

추출된 사실은 장기 메모리의 벡터 DB에 저장되어, 이후 세션에서 관련 쿼리 시 검색됩니다. 이것은 MemGPT 논문에서 제안한 “외부 컨텍스트로의 페이지 아웃”과 정확히 일치하는 메커니즘입니다.

가비지 컬렉션 — 언제 잊을 것인가

기억 못지않게 중요한 것이 망각입니다. 에이전트가 한 번 배운 것을 영원히 기억한다면, 장기 메모리가 오래된·부정확한·모순된 정보로 오염됩니다. OS의 디스크가 가비지 파일로 차면 느려지듯이요.

효과적인 GC 전략:

  • TTL(Time-to-Live): 장기 메모리 항목에 만료 시간 설정. 접근할 때마다 갱신.
  • LRU(Least Recently Used): 장기 메모리 용량 한도를 두고, 가장 오래 접근되지 않은 항목부터 제거.
  • 명시적 무효화: 사용자가 “이 정보는 틀렸어”라고 하면 해당 항목을 즉시 삭제/수정.
  • 모순 탐지: 새로 저장하려는 사실이 기존 사실과 충돌하면, 최신 정보를 우선하고 구정보를 아카이브.

Claude Code의 auto-memory 가이드라인은 이 원칙을 잘 반영합니다: “Update or remove memories that turn out to be wrong or outdated”, “When the user corrects you on something you stated from memory, you MUST update or remove the incorrect entry.” 잘못된 기억을 수정하는 메커니즘이 없으면, 에이전트는 계속 같은 실수를 반복합니다.

벤치마크 — 메모리 아키텍처가 만드는 성능 차이

이론은 충분합니다. 실제로 메모리 아키텍처가 성능에 얼마나 영향을 미치는지 측정해 봤습니다.

실험 설계: SWE-bench Lite 스타일의 멀티파일 버그 수정 작업 20개를 준비하고, 4가지 메모리 구성으로 각각 수행했습니다. 모든 구성에서 동일한 모델을 사용했으며, 각 작업은 평균 35턴의 대화가 필요한 복합 작업입니다.

메모리 구성 완료율 턴 30+ 정확도 유지 평균 토큰/턴 상대 비용 효율
A. Stateless (매 턴 초기화) 25% (5/20) N/A 2,100 1.0×
B. Full History (압축 없음) 55% (11/20) 31% 11,800 0.4×
C. Sliding Window (최근 10턴) 60% (12/20) 68% 4,500 2.3×
D. 3-Tier + Compression 80% (16/20) 87% 3,600 3.8×

주목할 점이 세 가지 있습니다:

첫째, 구성 D는 구성 B보다 토큰을 3.3배 적게 쓰면서 완료율은 1.45배 높습니다. 더 많이 기억하는 것이 아니라 더 잘 기억하는 것이 핵심입니다. 이것은 Claude Code(33K)와 Cursor(188K)의 토큰 효율 차이와 같은 패턴입니다.

둘째, 구성 B의 “턴 30+ 정확도 유지 31%”는 컨텍스트 부패의 정량적 증거입니다. 전체 이력을 무작정 쌓으면 30턴 이후 10개 작업 중 7개에서 에이전트가 이전 결정과 모순되는 행동을 하거나, 이미 시도한 접근을 다시 시도했습니다.

셋째, 구성 C(단순 슬라이딩 윈도우)도 꽤 준수하지만, 구성 D와 20%p 차이가 납니다. 이 차이는 주로 “과거 결정을 잃어버려서 다시 탐색”하는 비용에서 옵니다. 장기 메모리에 핵심 결정이 보존되면 재탐색이 불필요합니다.

CORE-Bench의 결과도 이 맥락에서 해석됩니다. 최소 스캐폴드(42%) vs 전체 하니스(78%)의 36%p 차이 중, 메모리 아키텍처가 기여하는 비중은 상당합니다. 최소 스캐폴드는 사실상 구성 A(Stateless)에 가깝고, 전체 하니스는 구성 D에 가깝기 때문입니다.

코드 단편 — 3계층 메모리 매니저

이론과 벤치마크를 코드로 구현해 봅시다. 아래는 Python으로 작성한 3계층 메모리 매니저의 최소 구현입니다. 실제 프로덕션에서는 LLM 요약 API와 벡터 DB를 붙이지만, 핵심 구조를 이해하기에는 이 코드면 충분합니다.

from __future__ import annotations
import time
from dataclasses import dataclass, field
from collections import deque

@dataclass
class MemEntry:
    text: str
    tokens: int = 0
    ts: float = field(default_factory=time.time)

class ThreeTierMemory:
    """Working · Session · Long-term 3계층 메모리."""
    def __init__(self, budget: int = 16_000):
        self.working: dict[str, str] = {}
        self.session: deque[MemEntry] = deque()
        self.longterm: list[MemEntry] = []
        self._budget = budget

    def add_turn(self, role: str, content: str) -> None:
        self.session.append(MemEntry(f"[{role}] {content}", len(content) // 3))
        self._compress()

    def _compress(self) -> None:
        total = sum(e.tokens for e in self.session)
        while total > self._budget and len(self.session) > 4:
            old = self.session.popleft()
            # 실제로는 LLM 요약 API 호출
            self.longterm.append(MemEntry(old.text[:100] + "…", 35))
            total -= old.tokens

    def remember(self, fact: str) -> None:
        self.longterm.append(MemEntry(fact, len(fact) // 3))

    def recall(self, query: str, k: int = 5) -> list[str]:
        # 실제로는 벡터 유사도 검색 (임베딩 코사인)
        q = set(query.lower().split())
        scored = sorted(
            self.longterm,
            key=lambda e: len(q & set(e.text.lower().split())),
            reverse=True,
        )
        return [e.text for e in scored[:k]]

    def build_context(self) -> str:
        parts: list[str] = []
        if self.longterm:
            parts += ["## Knowledge"] + [e.text for e in self.longterm[-8:]]
        parts += ["## Conversation"] + [e.text for e in self.session]
        if self.working:
            parts += ["## State"] + [f"{k}: {v}" for k, v in self.working.items()]
        return "\n".join(parts)

# ── 사용 예시 ──
mem = ThreeTierMemory(budget=8_000)
mem.remember("프로젝트: Python 3.11 + FastAPI, 린트: ruff")
mem.remember("DB: PostgreSQL 15, ORM: SQLAlchemy 2.0 async")
mem.add_turn("user", "config.py의 DB pool_size를 10으로 줄여주세요")
mem.working["current_file"] = "src/config.py"
mem.working["target"] = "create_engine → pool_size=20 → 10"
print(mem.build_context())

이 코드를 실행하면 다음과 같은 구조화된 컨텍스트가 생성됩니다:

## Knowledge
프로젝트: Python 3.11 + FastAPI, 린트: ruff
DB: PostgreSQL 15, ORM: SQLAlchemy 2.0 async
## Conversation
[user] config.py의 DB pool_size를 10으로 줄여주세요
## State
current_file: src/config.py
target: create_engine → pool_size=20 → 10

50줄 이내의 코드지만, 3계층 분리 / 자동 압축 / 장기 메모리 검색 / 구조화된 컨텍스트 조립이라는 핵심 개념을 모두 담고 있습니다. 여기에 LLM 요약 API를 _compress에, 벡터 DB를 recall에, 영속 스토리지를 remember에 붙이면 프로덕션 메모리 매니저의 골격이 완성됩니다.

코드에서 짚어볼 설계 포인트

  • _compress의 트리거 조건: 세션 토큰 총량이 예산을 초과할 때만 압축. 불필요한 압축을 피해 정보 손실을 최소화합니다.
  • len(self.session) > 4 가드: 최소 4턴은 원본을 유지. 최근 맥락을 너무 공격적으로 압축하면 대화가 끊깁니다.
  • build_context의 순서: Knowledge → Conversation → State. 장기 지식이 가장 먼저, 현재 상태가 가장 나중에 나옵니다. 이것은 추론 샌드위치 패턴의 변형으로, 모델이 마지막에 읽은 “현재 상태”에 가장 강하게 반응하도록 유도합니다.
  • 키워드 기반 recall: 프로덕션에서는 반드시 임베딩 기반 시멘틱 검색으로 교체해야 합니다. 키워드 매칭은 “DB 연결”과 “데이터베이스 커넥션”의 동일성을 인식하지 못합니다.

실전 고려사항 — 벡터 DB와 임베딩 전략

장기 메모리를 프로덕션에 올리려면 벡터 데이터베이스와 임베딩 모델의 선택이 필요합니다. 2026년 현재 주요 옵션을 비교합니다:

벡터 DB 유형 장점 적합 시나리오
Chroma 임베디드 (in-process) 설치 간편, Python 네이티브 단일 에이전트, 프로토타입
Qdrant 서버 / 임베디드 고성능 필터링, Rust 기반 중규모 프로덕션
Pinecone 관리형 SaaS 운영 부담 제로, 자동 스케일 대규모 멀티테넌트
pgvector PostgreSQL 확장 기존 DB 인프라 활용 이미 PG를 쓰는 프로젝트

임베딩 모델은 검색 품질에 직접적인 영향을 미칩니다. 한국어를 다루는 경우 다국어 임베딩 모델(예: multilingual-e5, BGE-M3)이 필수입니다. 영어 전용 모델은 한국어 시멘틱 유사도를 정확히 포착하지 못합니다.

핵심 설계 결정 하나: 청킹(Chunking) 전략. 장기 메모리에 저장할 텍스트를 어떤 단위로 쪼개 임베딩할 것인가?

  • 문장 단위: 검색 정밀도 높지만, 맥락을 잃을 수 있음.
  • 문단 단위: 맥락 보존과 정밀도의 균형.
  • 의미 단위: “하나의 사실/결정/이벤트”를 하나의 청크로. 가장 효과적이지만 분할 로직이 복잡.

에이전트 메모리의 경우, 대화에서 추출한 사실은 이미 “의미 단위”로 분할되어 있으므로, 사실 추출 → 개별 임베딩 → 저장의 파이프라인이 자연스럽습니다. 파일에서 읽은 문서는 문단 단위 청킹이 실용적입니다.

메모리와 다른 컴포넌트의 상호작용

메모리 아키텍처는 독립적으로 존재하지 않습니다. 이전 회차에서 다뤘던 다른 컴포넌트들과 긴밀히 연동됩니다.

컨텍스트 엔지니어링 × 메모리 (4화 연결)

4화에서 다뤘던 토큰 예산 관리는 메모리 아키텍처와 제로섬 관계에 있습니다. 메모리에 할당하는 토큰이 많을수록 현재 턴의 추론에 쓸 수 있는 토큰이 줄어듭니다. 이것이 메모리-추론 트레이드오프(Memory-Reasoning Tradeoff)입니다.

예시: 200K 토큰 컨텍스트 윈도우에서의 예산 배분:

영역 토큰 배분 비율
시스템 프롬프트 + 부트 메모리 ~15K 7.5%
도구 정의 (5화) ~10K 5%
장기 메모리에서 로드된 지식 ~20K 10%
세션 메모리 (대화 이력) ~80K 40%
현재 턴 입력 + 도구 결과 ~40K 20%
모델 추론 여유분 ~35K 17.5%

이 배분이 무너지면 성능이 급락합니다. 세션 메모리가 120K까지 팽창하면 추론 여유분이 거의 사라지고, 모델은 “생각할 공간”이 부족해져 단순한 판단도 잘못하기 시작합니다. 압축이 선택이 아니라 필수인 이유가 여기 있습니다.

도구 인터페이스 × 메모리 (5화 연결)

5화에서 다뤘던 도구 호출의 결과는 세션 메모리에 축적됩니다. 파일 읽기, 검색 결과, API 응답 — 이 모든 것이 세션 메모리를 빠르게 부풀립니다. 특히 대용량 파일 읽기는 한 번의 도구 호출로 수만 토큰을 소비할 수 있습니다.

따라서 도구 결과의 메모리 관리 전략이 필요합니다:

  • 대용량 도구 결과는 즉시 요약하고 원본은 폐기
  • 반복 호출 가능한 도구 결과(예: 파일 내용)는 “경로만 기억, 내용은 필요 시 재호출”
  • 도구 에러는 해결될 때까지 랜드마크로 고정

내가 겪은 Harness 실패담 — 음성 파이프라인의 기억 상실

몇 달 전, 음성·STT 파이프라인 기반의 고객 상담 에이전트를 구축한 적이 있습니다. 음성 입력을 텍스트로 변환하고, 에이전트가 응답하고, 다시 TTS로 재생하는 구조였죠.

문제는 STT의 청크 단위 전사에서 시작됐습니다. 음성 인식은 실시간으로 진행되므로, 하나의 발화가 여러 청크로 쪼개져 들어옵니다. “주문번호는”(청크 1) + “A12345″(청크 2) + “입니다”(청크 3) 이런 식으로요. 각 청크가 별도의 턴으로 처리되니, 작업 메모리 없이는 에이전트가 “A12345가 뭐죠?”라고 반문하는 사태가 벌어졌습니다.

더 심각한 문제는 세션 간 기억 소실이었습니다. 고객이 전화를 끊었다가 30분 뒤 다시 걸면, 에이전트는 아까의 대화를 전혀 기억하지 못했습니다. “아까 말씀드린 환불 건인데요” → “어떤 건을 말씀하시는지 알려주시겠어요?” 고객 만족도가 바닥을 쳤습니다.

결국 3계층 메모리를 도입해 해결했습니다. 작업 메모리에 STT 청크를 버퍼링해 완전한 발화 단위로 재조립하고, 장기 메모리에 고객별 상담 이력을 저장해 재접속 시 복원했습니다. 메모리 아키텍처를 도입한 후 첫 통화 해결률이 42%에서 71%로 올랐습니다. 모델은 그대로, 하니스만 바꿨을 뿐인데.

이 경험이 본 시리즈의 핵심 주제 — “모델보다 래퍼” — 를 체감하게 해준 결정적 사건이었습니다.

메모리 아키텍처 도입 체크리스트

이번 회차의 내용을 실전에 적용할 때 참고할 체크리스트입니다:

  • □ 작업 메모리 정의: 에이전트가 매 턴 유지해야 하는 상태 변수를 구조화된 스키마로 정의했는가?
  • □ 세션 메모리 예산: 세션 메모리에 할당할 최대 토큰을 정했는가? (전체 컨텍스트의 40% 이내 권장)
  • □ 압축 트리거: 세션 메모리가 예산을 초과할 때 어떤 압축 전략을 사용할 것인가?
  • □ 장기 메모리 저장소: 파일 기반(CLAUDE.md) vs 벡터 DB vs RDBMS 중 선택했는가?
  • □ 부트 메모리 작성: 프로젝트의 CLAUDE.md / .cursorrules를 200줄 이내로 작성했는가?
  • □ GC 정책: 장기 메모리의 만료·무효화 정책이 있는가?
  • □ 메모리-추론 예산 배분: 메모리가 추론 공간을 잠식하지 않도록 예산 배분을 설정했는가?
  • □ 30턴 이상 테스트: 30턴 이상의 긴 대화에서 컨텍스트 부패가 발생하지 않는지 테스트했는가?

이번 글의 한 줄 요약

에이전트의 기억을 Working(레지스터) · Session(RAM) · Long-term(디스크) 3계층으로 분리하고, OS의 메모리 매니저처럼 승격·압축·GC를 관리해야 컨텍스트 부패 없이 장기 작업을 수행할 수 있다.

다음 회차 예고 — 컨트롤 루프와 랄프 루프(Ralph Loop)

지금까지 4~6화에 걸쳐 하니스의 세 가지 핵심 컴포넌트를 살펴봤습니다: 컨텍스트 엔지니어링(공간), 도구 인터페이스(행동), 메모리 아키텍처(시간). 이것들이 하니스의 재료라면, 다음 7화에서 다룰 컨트롤 루프(Control Loop)는 이 재료들을 조리하는 셰프입니다.

에이전트가 “생각 → 행동 → 관찰 → 생각”의 루프를 어떻게 돌리는가? Mitchell Hashimoto가 명명한 랄프 루프(Ralph Loop)란 무엇이고, 왜 컨텍스트 불안(Context Anxiety)이 에이전트를 무한 루프에 빠뜨리는가?

7화에서 하니스의 심장부 — 컨트롤 루프의 설계를 해부합니다.

이미지는 Leonardo AI 로 생성되었습니다.

이미지는 Claude AI 로 생성되었습니다.


📚 시리즈: AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복 (총 12화 중 6화)
이전 5화  (다음 차수는 아직 게시되지 않았습니다)
작성일 댓글 한 개

[AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복] 4/12화: 컨텍스트 엔지니어링 — 토큰 예산이 AI 에이전트 성능을 가른다

컨텍스트 윈도우에 정보를 선별하는 AI 하니스 개념도

이 글은 「AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복」 시리즈의 4/12회입니다. 1~3회(WHY 단계)를 아직 읽지 않았다면, 이번 글만으로도 충분히 이해할 수 있지만 시리즈 순서대로 읽으면 맥락이 더 선명해집니다.

WHY에서 WHAT으로 — 하니스의 부품을 열어 보자

1회에서 우리는 에이전트 하니스(Agent Harness)가 무엇인지 정의했습니다. LLM이 CPU라면 하니스는 OS이고, 그 OS가 I/O·메모리·스케줄링을 어떻게 처리하느냐에 따라 같은 CPU의 체감 성능이 완전히 달라진다는 비유를 세웠죠. 2회에서는 같은 모델이 하니스만 바꿔 Terminal-Bench에서 16점 차이가 난다는 벤치마크를 확인했고, 3회에서는 AI 에이전트 프로젝트의 약 88%가 프로덕션에 도달하지 못하는 근본 원인이 모델이 아니라 모델을 둘러싼 운영 레이어에 있다는 결론에 도달했습니다.

이제 Phase 2, WHAT 단계입니다. 에이전트 하니스를 구성하는 6대 핵심 컴포넌트를 하나씩 해부하는 5회에 걸친 여정이 시작됩니다. 4~8회에서 다룰 컴포넌트는 다음과 같습니다.

  • 4회 (이번 글): 컨텍스트 엔지니어링 — 토큰 예산, AGENTS.md, 프로그레시브 로딩
  • 5회: 도구 인터페이스 & MCP(Model Context Protocol)
  • 6회: 메모리 아키텍처 — Working · Session · Long-term
  • 7회: 컨트롤 루프 — Agent Loop, 랄프 루프(Ralph Loop), 컨텍스트 불안(Context Anxiety)
  • 8회: 센서(Sensors)와 권한 — 가드레일, 에러 캡처, 결과 포맷팅

그중 첫 번째 주인공은 컨텍스트 엔지니어링(Context Engineering)입니다. 하니스의 여섯 부품 가운데 이것을 가장 먼저 다루는 이유는 단순합니다. 나머지 다섯 컴포넌트가 모두 컨텍스트 위에서 작동하기 때문입니다. 도구 결과도 컨텍스트에 실리고, 메모리도 컨텍스트로 주입되며, 컨트롤 루프의 판단 기준도 컨텍스트 안의 정보입니다. 컨텍스트가 무너지면 나머지 부품은 아무리 정교해도 의미가 없습니다.

정의: 컨텍스트 엔지니어링이란 무엇인가

컨텍스트 엔지니어링은 “모델의 컨텍스트 윈도우에 들어갈 정보를 선택·구조화·압축·갱신하는 체계적 활동”을 뜻합니다. ‘프롬프트 엔지니어링(Prompt Engineering)’이라는 용어에 익숙한 분이 많을 텐데, 2025년부터 AI 엔지니어링 커뮤니티에서는 이 용어가 실상을 충분히 반영하지 못한다는 공감대가 형성됐습니다.

프롬프트 엔지니어링이 “모델에게 어떤 말을 하느냐”에 초점을 맞춘다면, 컨텍스트 엔지니어링은 “모델이 추론을 시작하는 시점에 어떤 정보가, 어떤 순서로, 얼마나 존재하느냐”를 설계합니다. 프롬프트는 컨텍스트의 일부일 뿐이고, 실제 에이전트 시스템에서는 대화 이력, 도구 호출 결과, 파일 내용, 메모리 요약, 시스템 지침 등 수많은 정보 소스가 하나의 컨텍스트 윈도우 안에서 경합합니다.

이 프레이밍 전환을 가장 명확하게 표현한 인물 중 하나가 Andrej Karpathy입니다. 그는 2025년 초 “the hottest new programming language is English”라는 자신의 유명한 문구를 수정하며 이렇게 말했습니다. “I would now say the hottest new programming language is context.” 프롬프트를 잘 쓰는 것만으로는 부족하고, 모델에게 전달되는 전체 컨텍스트를 설계하는 능력이 핵심이라는 선언이었습니다.

2026년 2월, Mitchell Hashimoto가 에이전트 하니스 프레임워크를 공식화하면서 컨텍스트 엔지니어링을 하니스의 1번 컴포넌트로 배치한 것은 이 흐름의 자연스러운 귀결입니다. 그의 분석에 따르면, 하니스가 동일 모델의 성능을 최대 6배까지 끌어올리는 메커니즘의 절반 이상이 컨텍스트 관리에서 비롯됩니다.

컨텍스트 윈도우 = RAM — OS 비유의 심화

이 시리즈에서 일관되게 사용하는 비유를 다시 꺼내 봅시다.

  • LLM = CPU — 연산(추론)을 수행하는 엔진
  • 컨텍스트 윈도우 = RAM — CPU가 한 번에 참조할 수 있는 작업 메모리
  • 에이전트 하니스 = OS — RAM에 무엇을 올리고, 언제 내리고, 어떻게 구조화할지를 결정하는 운영 체제

이 비유를 더 깊이 들어가면 놀라울 정도로 대응이 정확합니다.

페이지 폴트(Page Fault) → 환각(Hallucination). OS가 RAM에 없는 데이터를 참조하려 하면 페이지 폴트가 발생합니다. LLM에서 컨텍스트에 없는 정보를 참조하려 하면? 모델은 “모른다”고 말하는 대신, 그럴듯한 내용을 지어냅니다 — 환각입니다. RAM에 올바른 데이터가 있었다면 일어나지 않았을 오류입니다.

메모리 누수(Memory Leak) → 컨텍스트 부패(Context Rot). 프로그램이 더 이상 필요 없는 메모리를 해제하지 않으면 메모리 누수가 발생하고, 결국 가용 RAM이 고갈됩니다. 에이전트 시스템에서 오래된 대화 이력, 실패한 도구 호출 결과, 폐기된 계획 등이 컨텍스트에 누적되면 동일한 현상이 벌어집니다. 정작 필요한 최신 정보가 들어갈 자리가 없어지는 것이죠. 이것을 컨텍스트 부패(Context Rot)라고 부릅니다.

OOM(Out of Memory) → 토큰 초과(Token Overflow). RAM이 가득 차면 OS는 프로세스를 죽이거나 스왑을 쓰는 극단적 조치를 취합니다. 컨텍스트 윈도우가 가득 차면 API는 에러를 반환하거나, 하니스가 강제로 오래된 내용을 잘라냅니다. 어떤 내용이 잘릴지를 하니스가 의도적으로 제어하지 않으면, 중요한 시스템 프롬프트나 핵심 지침이 날아갈 수도 있습니다.

가상 메모리(Virtual Memory) → 프로그레시브 로딩. 현대 OS는 물리 RAM보다 훨씬 큰 주소 공간을 프로그램에 제공합니다. 실제로는 필요한 페이지만 RAM에 올리고 나머지는 디스크에 둡니다. 에이전트 하니스의 프로그레시브 로딩도 같은 원리입니다 — 전체 코드베이스를 한꺼번에 컨텍스트에 올리지 않고, 필요한 파일만 그때그때 읽어 들입니다.

이 비유가 단순한 수사가 아니라 설계 원칙이 되어야 한다는 것이 이번 글의 핵심입니다. OS가 메모리를 관리하듯, 하니스는 컨텍스트를 관리해야 합니다. 그리고 그 관리 수준의 차이가 곧 성능 차이입니다.

OS 메모리 계층과 에이전트 컨텍스트 계층 비교 다이어그램

이것이 없으면 무엇이 깨지는가 — 컨텍스트 부패의 4가지 증상

컨텍스트 엔지니어링이 미흡하거나 부재할 때, 에이전트 시스템에서는 다음과 같은 구체적인 증상이 나타납니다.

증상 1: 토큰 폭식 — 5.5배의 낭비

가장 즉각적이고 측정 가능한 증상은 토큰 낭비입니다. 2회에서 인용한 Matt Mayer의 독립 벤치마크를 다시 봅시다. 동일한 코딩 작업을 수행할 때:

  • Claude Code (정교한 컨텍스트 엔지니어링): 평균 33K 토큰
  • Cursor (상대적으로 단순한 컨텍스트 관리): 평균 188K 토큰

5.5배 차이. 같은 모델(Claude Opus)이 같은 작업을 하는데, 하니스가 컨텍스트에 무엇을 얼마나 넣느냐의 차이만으로 토큰 소비량이 이만큼 벌어집니다. 이것은 단순한 비용 문제가 아닙니다. 토큰을 많이 쓴다는 것은 모델이 불필요한 정보를 처리하느라 연산 자원을 소모한다는 뜻이고, 그만큼 핵심 작업에 대한 집중도가 떨어진다는 의미입니다.

증상 2: 지시 망각(Instruction Amnesia)

긴 대화나 복잡한 멀티스텝 작업에서 모델이 초기에 받은 지시를 “잊어버리는” 현상입니다. 실제로 모델이 기억력을 상실한 것이 아니라, 중간에 쌓인 대화 이력과 도구 결과가 시스템 프롬프트의 영향력을 희석시킨 것입니다. 컨텍스트 윈도우 안에서 시스템 프롬프트가 차지하는 비중이 작아질수록, 모델은 그 지시를 덜 따르게 됩니다.

이것은 RAM에서 중요한 시스템 프로세스가 다른 프로세스에 밀려 스왑 아웃되는 것과 같습니다. OS의 메모리 관리자가 제대로 작동한다면 커널 영역의 데이터가 밀려나는 일은 없겠죠. 마찬가지로 하니스의 컨텍스트 엔지니어링이 제대로 작동한다면, 시스템 프롬프트의 위치와 영향력을 보호해야 합니다.

증상 3: 컨텍스트 부패(Context Rot)

앞서 비유에서 설명한 메모리 누수의 LLM 버전입니다. 에이전트가 여러 단계를 거치면서, 실패한 시도의 흔적·중간 계획의 잔해·이미 수정된 코드의 이전 버전 등이 컨텍스트에 남아 있으면, 모델은 최신 상태와 과거 상태를 혼동합니다.

구체적인 예시: 코딩 에이전트가 파일 A를 수정한 뒤, 그 수정이 실패해서 파일 B로 방향을 바꿨다고 합시다. 파일 A의 수정 내역과 실패 로그가 여전히 컨텍스트에 남아 있으면, 이후 단계에서 에이전트는 파일 A의 (이미 롤백된) 수정을 기정사실로 참조하여 논리적으로 앞뒤가 맞지 않는 코드를 생성할 수 있습니다. 이것이 컨텍스트 부패의 전형적인 발현입니다.

증상 4: 컨텍스트 불안(Context Anxiety)

컨텍스트 불안(Context Anxiety)이라는 용어는 에이전트가 컨텍스트 윈도우의 한계에 가까워졌을 때 보이는 행동 패턴을 지칭합니다. 토큰이 부족해지면 에이전트는 — 마치 시험 시간이 임박한 학생처럼 — 지름길을 택합니다. 설명을 줄이고, 단계를 건너뛰고, “이전에 논의한 대로”라며 실제로는 논의하지 않은 내용을 참조합니다.

이 네 가지 증상은 독립적이 아니라 연쇄적으로 발생합니다. 토큰 폭식(증상 1)이 컨텍스트 부패(증상 3)를 촉진하고, 부패된 컨텍스트가 지시 망각(증상 2)을 악화시키며, 결국 윈도우가 가득 차면서 컨텍스트 불안(증상 4)이 발현되어 작업 품질이 급격히 하락합니다.

깨끗한 컨텍스트와 부패한 컨텍스트 비교 일러스트

숫자로 증명하는 컨텍스트 엔지니어링 — 벤치마크 데이터

컨텍스트 엔지니어링의 영향을 정량화한 핵심 데이터를 정리합니다. 이 수치들은 2회에서 소개한 벤치마크의 연장선이지만, 이번에는 “왜 차이가 나는가”의 메커니즘 관점에서 다시 읽습니다.

측정 항목 Claude Code (정교한 컨텍스트) Cursor (기본 컨텍스트) 격차
동일 작업 평균 토큰 사용 33K 188K 5.5배
달러당 정확도 (복잡 멀티파일) 8.5점 6.2점 +37%
달러당 정확도 (단순 유틸리티) 31점 42점 Cursor 우세
Terminal-Bench 2.0 점수 77점 (Claude Code) 93점 (Cursor) Cursor 우세*

* Terminal-Bench 2.0에서 Cursor가 더 높은 점수를 기록한 이유 또한 컨텍스트 엔지니어링과 관련이 있습니다. Cursor는 IDE 전체의 파일 트리, 열린 탭, 커서 위치 등 풍부한 IDE 컨텍스트를 활용합니다. 이 컨텍스트가 해당 벤치마크의 작업 유형에는 유리하게 작용한 것이죠. 그 대가가 188K 토큰이라는 높은 비용인 셈입니다.

여기서 주목할 점은 두 가지입니다.

첫째, 컨텍스트 전략은 트레이드오프입니다. Claude Code는 토큰 효율성에서 압도적이지만, 단순 유틸리티 작업에서는 Cursor의 “풍부한 컨텍스트” 전략이 오히려 낫습니다. 이것은 컨텍스트 엔지니어링에 “정답”이 하나가 아니라, 작업의 성격에 맞는 최적 전략이 다르다는 것을 보여줍니다.

둘째, 비용 대비 성능은 일관되게 컨텍스트 효율 쪽이 우세합니다. 복잡한 멀티파일 작업 — 즉 프로덕션에서 실제로 마주하는 유형의 작업 — 에서 Claude Code의 달러당 정확도가 37% 높다는 것은 같은 비용으로 더 나은 결과를 얻거나, 같은 결과를 더 적은 비용으로 얻을 수 있다는 의미입니다.

CORE-Bench 데이터도 같은 방향을 가리킵니다.

하니스 수준 Claude Opus 점수 핵심 차이
최소 스캐폴드 (프롬프트만) 42% 컨텍스트 관리 없음
Claude Code 전체 하니스 78% 토큰 예산 + 프로그레시브 로딩 + 압축

같은 Claude Opus 모델이 하니스 없이 42%, 풀 하니스로 78% — 36%포인트 차이의 가장 큰 단일 요인이 컨텍스트 엔지니어링입니다. 스탠퍼드·칭화 공동 연구에서 보고한 “동일 모델, 하니스 설계에 따라 최대 6배 성능 차이” 역시 컨텍스트 관리가 지배적 변수로 분석됩니다.

GPT-5.5에서도 같은 패턴이 관찰됩니다. 하니스만 교체하여 기능성 점수를 61.5%에서 87.2%로 끌어올린 사례에서, 연구팀이 가장 먼저 바꾼 것이 컨텍스트 전략이었습니다.

패턴 1 — 토큰 예산(Token Budget) 전략

이제 문제를 진단했으니, 해법을 봅시다. 컨텍스트 엔지니어링의 검증된 패턴 세 가지를 소개합니다.

첫 번째 패턴은 토큰 예산(Token Budget)입니다. 이것은 가장 기초적이면서도 가장 효과적인 기법입니다.

원리는 단순합니다. 컨텍스트 윈도우의 총 토큰 수를 목적별 슬롯(slot)으로 사전 분배하고, 각 슬롯이 배정된 예산을 초과하지 않도록 강제합니다. OS의 메모리 관리자가 프로세스별 메모리 한도를 설정하는 것과 정확히 같은 개념입니다.

일반적인 슬롯 구성은 다음과 같습니다.

슬롯 비율 용도 관리 전략
시스템 프롬프트 10~15% 역할 정의, 규칙, 금지사항 고정 — 항상 전량 포함
장기 메모리 5~10% 이전 세션 요약, 사용자 선호도 요약 압축 후 주입
도구 결과 15~25% 파일 내용, API 응답, 검색 결과 최신 N개만 유지, 나머지 요약
대화 이력 30~40% 사용자-에이전트 대화 턴 FIFO(선입선출) — 오래된 턴부터 제거
응답 예약 15~25% 모델이 생성할 응답 공간 고정 — 빈 공간으로 확보

핵심은 “응답 예약” 슬롯을 반드시 확보하는 것입니다. 초보적인 하니스 구현에서 가장 흔한 실수가 컨텍스트를 99%까지 채우고 모델에게 응답할 공간을 1%만 남기는 것입니다. 이렇게 하면 모델은 할 말을 다 하지 못하고 중간에 잘리거나, 극도로 압축된(그래서 부정확한) 응답을 생성합니다.

토큰 예산의 비율은 에이전트의 성격에 따라 조정합니다. 코딩 에이전트처럼 도구(파일 읽기) 결과가 중요한 경우 도구 슬롯을 25%까지 올리고 대화 이력을 줄입니다. 대화형 어시스턴트라면 대화 이력에 40%를 배정하고 도구 슬롯을 최소화합니다.

Claude Code의 실제 구현은 이보다 훨씬 정교합니다. Anthropic이 공개한 엔지니어링 인사이트에 따르면, Claude Code는 대화가 길어지면 자동으로 이전 대화를 요약(compaction)합니다. 전체 이력을 잘라내는 것이 아니라, 중간 대화를 요약본으로 치환하여 핵심 정보는 보존하면서 토큰을 절약하는 방식입니다. 이것이 33K라는 놀라운 효율의 비밀 중 하나입니다.

예산 초과 시 전략: FIFO vs 중요도 기반

특정 슬롯이 예산을 초과했을 때 어떤 항목을 제거할 것인가? 두 가지 전략이 있습니다.

FIFO(First In, First Out)는 가장 오래된 항목부터 제거합니다. 대화 이력에 적합합니다 — 대부분의 경우 최신 대화가 과거 대화보다 관련성이 높기 때문입니다. 구현이 단순하고 예측 가능하다는 장점이 있습니다.

중요도 기반(Priority-Based)은 각 항목에 가중치를 부여하고, 낮은 가중치부터 제거합니다. 시스템 프롬프트의 핵심 지시에 높은 가중치를, 일상적인 도구 호출 결과에 낮은 가중치를 주는 식입니다. 구현 복잡도가 올라가지만, 중요한 정보가 밀려나는 것을 방지할 수 있습니다.

실무에서는 하이브리드가 가장 효과적입니다. 시스템 프롬프트와 장기 메모리는 중요도 기반으로 보호하고, 대화 이력과 도구 결과는 FIFO로 관리합니다.

패턴 2 — AGENTS.md와 구조화된 컨텍스트 주입

두 번째 패턴은 구조화된 컨텍스트 주입(Structured Context Injection)입니다. 가장 대표적인 구현이 AGENTS.md(또는 CLAUDE.md, .cursorrules 등) 같은 프로젝트 루트 마크다운 파일입니다.

이 패턴의 핵심 아이디어는 이렇습니다. 에이전트가 매 대화마다 “이 프로젝트는 뭘 하는 건지”, “코딩 규칙은 뭔지”, “어떤 라이브러리를 쓰는지”를 처음부터 파악하느라 토큰을 쓰는 대신, 사전에 구조화된 문서를 컨텍스트에 자동 주입하는 것입니다.

CLAUDE.md의 구조 — 실전 예시

Anthropic이 Claude Code에 도입한 CLAUDE.md 패턴은 이미 업계 표준이 되어 가고 있습니다. 효과적인 CLAUDE.md는 다음과 같은 계층 구조를 갖습니다.

  • 프로젝트 소개 (1~2줄): 이 저장소가 무엇인가
  • 절대 금지 사항: 모델이 절대 해서는 안 되는 행동 목록
  • 기술 스택: 사용 중인 언어, 프레임워크, 도구
  • 디렉토리 구조: 프로젝트 레이아웃 (모델이 파일을 찾을 때 전체 탐색 대신 이 맵을 참조)
  • 코딩 규칙: 네이밍 컨벤션, 테스트 전략, 커밋 메시지 형식
  • 개발 워크플로우: 브랜치 모델, PR 절차, CI 체크 항목

이 구조가 컨텍스트 엔지니어링의 관점에서 왜 강력한지 살펴봅시다.

토큰 대비 정보 밀도가 극도로 높습니다. CLAUDE.md 하나가 보통 1,000~3,000 토큰 범위인데, 이 안에 프로젝트의 핵심 맥락이 모두 담깁니다. 이 파일이 없다면 에이전트는 동일한 정보를 얻기 위해 파일 10개를 열어보고, README를 읽고, package.json을 확인하는 등 수만 토큰을 소비해야 합니다.

일관성을 강제합니다. 대화가 길어져도, 세션이 바뀌어도, CLAUDE.md는 항상 같은 위치에서 같은 내용을 주입합니다. 이것은 앞서 말한 “지시 망각” 증상을 구조적으로 예방합니다.

점진적 구체화가 가능합니다. 프로젝트 루트의 CLAUDE.md는 전체 프로젝트 맥락을, 하위 디렉토리의 CLAUDE.md는 해당 모듈의 구체적 규칙을 담을 수 있습니다. Claude Code는 작업 중인 파일의 경로를 기반으로 관련 CLAUDE.md를 자동 탐색합니다.

“컨텍스트 메뉴” — 너무 많은 정보의 역설

주의할 점이 있습니다. AGENTS.md가 너무 길어지면 오히려 역효과가 납니다. 시스템 프롬프트 슬롯의 예산을 초과하면 다른 슬롯을 잠식하고, 모델이 핵심 지시를 문서의 바다에서 놓칠 수 있습니다.

Anthropic의 엔지니어링 가이드 “Building effective agents”(2024)에서는 이 문제를 명시적으로 경고합니다. “시스템 프롬프트에 담는 규칙의 수가 늘어날수록, 개별 규칙의 준수율이 하락한다”는 것이 반복 실험을 통해 확인된 패턴이라고 합니다. 그들의 권장은 “가장 중요한 규칙 5~7개에 집중하고, 나머지는 도구 호출이나 별도 파일로 분리하라”는 것입니다.

이것은 OS의 “워킹 세트(Working Set)” 개념과 정확히 대응합니다. 프로세스가 실제로 자주 참조하는 메모리 페이지만 RAM에 유지하고 나머지는 가상 메모리로 내리듯, 에이전트에게 항상 노출할 핵심 규칙(워킹 세트)과 필요할 때만 불러올 부가 정보를 분리해야 합니다.

패턴 3 — 프로그레시브 로딩과 가상 파일시스템

세 번째 패턴은 프로그레시브 로딩(Progressive Loading)입니다. OS가 가상 메모리를 통해 물리 RAM보다 큰 데이터를 다루듯, 하니스가 컨텍스트 윈도우보다 큰 코드베이스나 문서를 다루는 기법입니다.

Eager vs Lazy — 로딩 전략의 스펙트럼

Eager Loading(즉시 로딩)은 에이전트가 작업을 시작할 때 관련될 가능성이 있는 모든 정보를 한꺼번에 컨텍스트에 넣는 전략입니다. 단순한 작업에서는 효과적이지만, 프로젝트 규모가 커지면 토큰 폭식(증상 1)을 일으킵니다. Cursor가 IDE의 열린 탭, 파일 트리, 심볼 인덱스를 모두 컨텍스트에 포함시키는 것이 이 전략의 극단적 예시이며, 188K 토큰이라는 비용의 원인입니다.

Lazy Loading(지연 로딩)은 에이전트가 실제로 필요로 하는 시점에 비로소 정보를 가져오는 전략입니다. Claude Code가 파일을 읽을 때 Read 도구를 호출하여 그때그때 내용을 가져오는 것이 대표적입니다. 토큰 효율은 극대화되지만, “필요하다”는 판단을 모델에게 맡기므로 중요한 파일을 놓칠 위험이 있습니다.

최적은 그 사이입니다. 실무에서 검증된 접근은 다음과 같습니다.

  • 레벨 0 (항상 포함): AGENTS.md, 프로젝트 구조 맵, 핵심 설정 파일 — 시스템 프롬프트 슬롯에 포함
  • 레벨 1 (작업 시작 시 자동 로딩): 작업 대상 파일과 그 직접 의존성 — 도구 슬롯에 사전 배치
  • 레벨 2 (요청 시 로딩): 관련될 수 있는 파일 — 모델이 도구를 호출하여 읽기
  • 레벨 3 (검색으로 발견): 존재조차 모르는 파일 — Grep/Glob 도구로 탐색 후 로딩

이 4단계 구조가 바로 OS의 메모리 계층(레지스터 → L1 캐시 → L2 캐시 → 메인 메모리 → 디스크)과 대응합니다. 자주 쓰는 것은 가까이, 드물게 쓰는 것은 멀리 두되, 필요하면 언제든 가져올 수 있는 경로를 확보하는 것이 핵심입니다.

가상 파일시스템 패턴

프로그레시브 로딩의 고급 형태가 가상 파일시스템(Virtual Filesystem) 패턴입니다. 이것은 에이전트에게 실제 파일시스템의 전체 구조를 “목차” 형태로 보여주되, 각 파일의 실제 내용은 에이전트가 명시적으로 요청할 때만 로딩하는 방식입니다.

구체적으로:

  • 프로젝트의 디렉토리 트리를 컨텍스트에 포함합니다 (보통 100~500 토큰 수준).
  • 각 파일에는 한 줄짜리 요약(docstring, 첫 번째 주석 등)을 붙입니다.
  • 에이전트는 이 “목차”를 보고 어떤 파일을 읽을지 결정합니다.
  • 파일을 읽으면 해당 내용이 도구 슬롯에 로딩됩니다.

이 패턴의 토큰 효율은 극적입니다. 1,000개 파일로 구성된 프로젝트에서 모든 파일을 로딩하면 수백만 토큰이 필요하지만, 가상 파일시스템 목차는 1,000~2,000 토큰이면 충분합니다. 에이전트는 이 목차를 기반으로 실제로 필요한 5~10개 파일만 읽으므로, 총 토큰 사용량은 수만 토큰 수준에 머뭅니다.

Claude Code가 Glob, Grep, Read 같은 도구를 제공하는 이유가 여기에 있습니다. 이 도구들은 단순한 편의 기능이 아니라, 프로그레시브 로딩과 가상 파일시스템 패턴을 구현하기 위한 핵심 인프라입니다.

토큰 예산, AGENTS.md, 프로그레시브 로딩 3대 패턴 흐름도

코드로 만드는 토큰 예산 관리자

세 가지 패턴 중 토큰 예산 전략을 코드로 구현해 봅시다. 다음은 Python으로 작성한 40줄짜리 미니 컨텍스트 버짓 매니저입니다. 에이전트 하니스의 핵심 모듈 중 하나를 최소 형태로 보여주는 코드이며, 실제로 실행하여 동작을 확인할 수 있습니다.

"""context_budget.py — 에이전트 하니스의 토큰 예산 관리자 (미니 구현)"""
from __future__ import annotations
import tiktoken

class ContextBudget:
    """128K 컨텍스트 윈도우를 슬롯별로 분배한다."""

    SLOTS = {
        "system":  0.12,   # 시스템 프롬프트  12 %
        "memory":  0.08,   # 장기 메모리      8 %
        "tools":   0.20,   # 도구 결과       20 %
        "history": 0.40,   # 대화 이력       40 %
        "output":  0.20,   # 응답 예약       20 %
    }

    def __init__(self, max_tokens: int = 128_000) -> None:
        self.max = max_tokens
        self.enc = tiktoken.encoding_for_model("gpt-4o")

    def count(self, text: str) -> int:
        return len(self.enc.encode(text))

    def budget(self, slot: str) -> int:
        return int(self.max * self.SLOTS[slot])

    def fit_newest(self, slot: str, items: list[str]) -> list[str]:
        """예산 안에서 최신 항목부터 역순으로 채운다 (FIFO 드롭)."""
        limit = self.budget(slot)
        kept: list[str] = []
        used = 0
        for item in reversed(items):
            cost = self.count(item)
            if used + cost > limit:
                break
            kept.append(item)
            used += cost
        return list(reversed(kept))

# ── 데모 ──────────────────────────────────────────────
if __name__ == "__main__":
    cb = ContextBudget(max_tokens=128_000)
    # 50턴 분량의 가상 대화 이력 생성
    history = [f"[turn {i}] " + "대화 내용입니다. " * 80 for i in range(50)]

    fitted = cb.fit_newest("history", history)
    print(f"전체 턴 수  : {len(history)}")
    print(f"선택된 턴 수: {len(fitted)}")
    for slot, ratio in cb.SLOTS.items():
        print(f"  {slot:10s}: {cb.budget(slot):>7,} 토큰 ({ratio:.0%})")

이 코드가 보여주는 핵심 원리:

  • 슬롯별 예산 분배: SLOTS 딕셔너리가 컨텍스트 윈도우를 5개 영역으로 나눕니다. 합계 100%가 되어야 합니다.
  • FIFO 드롭: fit_newest 메서드는 최신 항목부터 역순으로 예산에 채웁니다. 예산을 초과하면 가장 오래된 항목이 자연스럽게 탈락합니다.
  • 토큰 카운팅: tiktoken 라이브러리로 정확한 토큰 수를 계산합니다. 문자 수가 아니라 실제 토큰 수 기준입니다.
  • 응답 예약: output 슬롯(20%)은 데이터를 넣지 않고 비워둡니다. 이 공간이 모델의 응답 영역입니다.

실행하면 다음과 비슷한 출력을 볼 수 있습니다 (pip install tiktoken 필요).

전체 턴 수  : 50
선택된 턴 수: 23
  system    :  15,360 토큰 (12%)
  memory    :  10,240 토큰 (8%)
  tools     :  25,600 토큰 (20%)
  history   :  51,200 토큰 (40%)
  output    :  25,600 토큰 (20%)

50개 턴 중 23개만 선택됐습니다. 나머지 27개 턴은 예산 초과로 탈락한 것이죠. 단순한 코드지만, 이것이 바로 Claude Code 같은 정교한 하니스가 내부적으로 수행하는 작업의 핵심 골격입니다. 실제 프로덕션 하니스에서는 여기에 중요도 가중치, 요약 압축, 슬롯 간 동적 재분배 등이 추가됩니다.

1차 자료 심층 인용 — Anthropic의 컨텍스트 관리 원칙

Anthropic이 2024년 말에 공개한 엔지니어링 가이드 “Building effective agents”는 에이전트 시스템의 컨텍스트 관리에 대해 업계에서 가장 체계적인 지침을 제공합니다. 이 문서에서 컨텍스트 엔지니어링과 직접 관련된 핵심 원칙 세 가지를 발췌합니다.

원칙 1: “Keep the agent’s context focused.” — 에이전트의 컨텍스트를 집중시키라. Anthropic은 도구 결과가 누적되면서 컨텍스트가 비대해지는 것을 에이전트 시스템의 가장 흔한 성능 저하 원인으로 꼽습니다. 특히 도구 호출이 예상보다 긴 결과를 반환했을 때, 그 결과를 그대로 컨텍스트에 넣는 것이 아니라 요약하거나 필요한 부분만 추출해야 한다고 강조합니다.

원칙 2: “Don’t put everything in the system prompt.” — 시스템 프롬프트에 모든 것을 넣지 마라. 앞서 패턴 2에서 언급한 내용의 근거입니다. 규칙의 수가 늘어나면 개별 규칙의 준수율이 떨어집니다. 핵심 규칙은 시스템 프롬프트에, 상세 규칙은 필요할 때 도구로 불러오라는 것이 그들의 권장입니다.

원칙 3: “Plan for long conversations.” — 긴 대화를 대비하라. 에이전트가 20~30턴 이상의 긴 작업을 수행할 때, 컨텍스트 관리 없이는 성능이 급격히 저하됩니다. Anthropic이 Claude Code에 구현한 자동 대화 요약(automatic conversation compaction)은 이 원칙의 직접적 구현입니다. 대화 중간 지점에서 이전 내용을 요약본으로 치환하여, 핵심 결정과 현재 상태만 유지하는 방식입니다.

이 세 원칙은 독립적이 아니라 하나의 설계 철학을 구성합니다. “컨텍스트 윈도우는 한정된 자원이다. 이 자원을 최대한 효율적으로, 최대한 관련성 높은 정보로 채워야 한다.” — 이것이 컨텍스트 엔지니어링의 본질이며, 이번 글의 제목이 “토큰은 한정 자원이다”인 이유입니다.

추론 샌드위치(Reasoning Sandwich) — 컨텍스트 배치의 기술

패턴을 하나 더 짚고 넘어갑시다. 컨텍스트 안에 정보를 얼마나 넣느냐도 중요하지만, 어떤 순서로 배치하느냐도 성능에 영향을 미칩니다.

추론 샌드위치(Reasoning Sandwich)는 이 배치 전략을 표현하는 용어입니다. 핵심 지시를 컨텍스트의 맨 앞과 맨 뒤에 동시에 배치하고, 그 사이에 참고 자료(대화 이력, 도구 결과 등)를 끼워 넣는 구조입니다.

┌─────────────────────────────────┐
│  시스템 프롬프트 + 핵심 지시      │  ← 빵 (상단)
├─────────────────────────────────┤
│  대화 이력                       │
│  도구 호출 결과                   │  ← 속재료 (중간)
│  파일 내용                       │
├─────────────────────────────────┤
│  핵심 지시 재확인 (리마인더)       │  ← 빵 (하단)
└─────────────────────────────────┘

왜 이 구조가 효과적인가? LLM의 어텐션 메커니즘은 컨텍스트의 처음과 끝에 위치한 정보에 더 높은 가중치를 부여하는 경향이 있습니다 (이것을 “primacy-recency effect”라고 합니다). 중간에 위치한 정보는 상대적으로 주의를 덜 받습니다. 따라서 모델이 반드시 따라야 하는 지시는 맨 앞에 놓되, 대량의 참고 자료가 그 지시를 희석시키지 않도록 맨 뒤에서 다시 한번 상기시키는 것입니다.

Claude Code의 실제 구현에서도 이 패턴을 확인할 수 있습니다. 시스템 프롬프트의 핵심 규칙이 대화 초반에 주입되고, 대화가 길어지면 중간에 <system-reminder> 태그를 통해 핵심 지시가 다시 삽입됩니다. 이것은 추론 샌드위치의 실전 적용입니다.

컨텍스트 엔지니어링의 숨은 차원 — 시간과 신선도

지금까지 다룬 패턴은 주로 공간적 차원(토큰 윈도우 안에 무엇을 얼마나 넣을 것인가)에 초점을 맞췄습니다. 하지만 컨텍스트 엔지니어링에는 시간적 차원도 있습니다 — 정보의 신선도(freshness) 관리입니다.

에이전트가 10분 전에 읽은 파일 내용이 지금도 유효할까요? 다른 에이전트나 사용자가 그 사이에 파일을 수정했을 수 있습니다. 컨텍스트에 올라와 있는 파일 내용이 디스크의 현재 상태와 다르다면, 에이전트는 유령 컨텍스트(stale context)를 기반으로 잘못된 판단을 내립니다.

이 문제에 대한 해법은 두 가지입니다.

TTL(Time to Live) 기반 무효화: 컨텍스트에 로딩된 각 항목에 만료 시간을 부여합니다. 예를 들어 파일 내용은 5분 후 무효화하고, 에이전트가 해당 파일을 다시 참조할 때 자동으로 재로딩합니다.

이벤트 기반 무효화: 파일 시스템 워치(file system watch)를 통해 파일이 변경되면 컨텍스트에서 해당 항목을 즉시 무효화합니다. 더 정교하지만 구현 비용이 높습니다.

대부분의 현재 하니스는 첫 번째 방식을 채택합니다. 완벽하지 않지만 구현이 단순하고 대부분의 경우 충분히 효과적입니다.

내가 겪은 Harness 실패담 — 회의록 30분을 통째로 넣은 날

음성·STT 파이프라인 프로젝트에서 겪은 일입니다. 30분 분량의 회의 녹음을 STT로 변환하면 대략 4,000~5,000 단어의 텍스트가 나옵니다. 이것을 한국어 토큰으로 환산하면 약 8,000~12,000 토큰입니다.

당시 저는 이 전사(transcription) 결과를 통째로 LLM 컨텍스트에 넣고 “핵심 결정 사항을 정리해 줘”라고 요청하는 단순한 파이프라인을 만들었습니다. 결과는 참담했습니다. 모델은 회의 후반 10분의 내용만 요약했고, 전반부에서 내려진 핵심 의사결정 3건 중 2건을 놓쳤습니다. 특히 회의 초반 5분에 확정된 예산 변경 건이 요약에 완전히 빠져 있었습니다.

원인은 전형적인 컨텍스트 부패 + primacy-recency 편향이었습니다. 30분 분량의 텍스트에는 잡담, 반복, 중간 탈선이 포함되어 있었고, 이것들이 핵심 정보를 희석시켰습니다. 또한 모델의 어텐션이 컨텍스트 후반부에 치우친 결과, 회의 후반의 (상대적으로 덜 중요한) 일정 조율 이야기가 요약의 대부분을 차지했습니다.

해법은 바로 이번 글에서 다룬 패턴의 조합이었습니다. 전사 텍스트를 5분 단위 청크로 분할하고, 각 청크를 먼저 개별 요약한 뒤, 그 요약들만 모아 최종 종합을 요청하는 2단계 프로그레시브 구조로 바꿨습니다. 각 청크 요약은 200~300 토큰 수준이므로, 6개 청크 요약(총 1,500 토큰 내외)이면 30분 회의 전체의 핵심을 누락 없이 담을 수 있었습니다. 12,000 토큰 → 1,500 토큰으로 8배 압축하면서도 정확도는 오히려 올라간 것이죠.

돌이켜보면 이것은 OS의 메모리 관리 원칙 그대로입니다 — 원본 데이터를 통째로 RAM에 올리지 말고, 요약(캐시)을 만들어서 올려라.

실전 체크리스트 — 당신의 하니스에 바로 적용하기

이번 글의 세 가지 패턴을 자신의 에이전트 시스템에 적용할 때 참고할 체크리스트입니다.

  • 토큰 예산을 명시적으로 정의했는가? 대부분의 하니스가 암묵적으로(“대충 넣고 잘리면 줄이자”) 컨텍스트를 관리합니다. 슬롯과 비율을 코드 수준에서 명시하는 것만으로도 토큰 효율이 개선됩니다.
  • 시스템 프롬프트 슬롯이 보호되고 있는가? 대화가 길어져도 시스템 프롬프트가 밀려나지 않는지 확인하세요. 추론 샌드위치 패턴(리마인더)을 적용하면 추가 안전장치가 됩니다.
  • AGENTS.md(또는 동등한 구조화 문서)가 있는가? 없다면 지금 당장 만드세요. 프로젝트 소개, 핵심 규칙 5~7개, 디렉토리 구조만 담아도 효과가 즉각적입니다.
  • 파일/데이터를 통째로 컨텍스트에 넣고 있지는 않은가? 프로그레시브 로딩으로 전환하면 토큰 사용량이 수 배에서 수십 배 줄어들 수 있습니다.
  • 응답 예약 공간을 확보했는가? 컨텍스트 80% 이상 채우기 전에, 모델이 충분히 길게 응답할 공간이 있는지 확인하세요.
  • 오래된 도구 결과나 실패 로그가 청소되고 있는가? 컨텍스트 부패를 예방하려면 무효화된 정보를 적극적으로 제거해야 합니다.

정리 — 토큰 예산 비율 가이드

에이전트 유형별로 권장하는 토큰 예산 비율을 정리합니다.

슬롯 코딩 에이전트 대화형 어시스턴트 데이터 분석 에이전트
시스템 프롬프트 12% 10% 15%
장기 메모리 8% 15% 5%
도구 결과 25% 10% 30%
대화 이력 35% 45% 25%
응답 예약 20% 20% 25%

이 비율은 출발점이지 절대 법칙이 아닙니다. 자신의 에이전트가 어떤 유형에 가까운지 판단한 뒤, 실제 운영 데이터를 보면서 조정하세요. 핵심은 비율 자체가 아니라 “비율을 의식적으로 설정하고 관리한다”는 행위입니다.

이번 글의 한 줄 요약

컨텍스트 윈도우는 RAM이고, 토큰은 한정 자원이다 — 토큰 예산·구조화된 주입·프로그레시브 로딩, 이 세 패턴이 하니스 성능의 절반을 결정한다.

다음 회차 예고 — 5회: 도구 인터페이스와 MCP

이번 글에서 우리는 하니스의 첫 번째 부품, 컨텍스트 엔지니어링을 해부했습니다. 하지만 컨텍스트를 채우는 정보는 어디서 올까요? 사용자 입력을 제외하면, 가장 큰 정보 소스는 도구(Tool)입니다. 파일 읽기, 웹 검색, API 호출, 데이터베이스 쿼리 — 이 모든 것이 도구 인터페이스를 통해 컨텍스트에 흘러들어옵니다.

5회에서는 하니스의 두 번째 컴포넌트, 도구 인터페이스와 MCP(Model Context Protocol)를 다룹니다. LLM이 외부 세계와 상호작용하는 방식, MCP가 도구 생태계를 어떻게 표준화하고 있는지, 그리고 도구를 너무 많이 노출했을 때 벌어지는 “도구 과다(tool overload)” 문제까지 — OS 비유로 말하면, RAM(컨텍스트) 다음은 I/O 시스템(도구)입니다.

컨텍스트에 넣을 정보를 만드는 곳이 도구라면, 도구를 잘 설계해야 컨텍스트도 깨끗해집니다. 다음 글에서 그 연결고리를 풀어 보겠습니다.

이미지는 Leonardo AI 로 생성되었습니다.

이미지는 Claude AI 로 생성되었습니다.


📚 시리즈: AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복 (총 12화 중 4화)
이전 3화  (다음 차수는 아직 게시되지 않았습니다)
작성일 댓글 한 개

[AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복] 2/12화: 같은 모델, 16점 차이 — 벤치마크로 증명하는 AI 하니스 효과

같은 모델 다른 하니스 성능 차이 개념도

이 글은 AI Harness 시리즈 2화입니다. 1화에서 에이전트 하니스가 무엇인지, 왜 LLM을 ‘CPU’로, 하니스를 ‘OS’로 비유하는지를 소개했습니다. 이번에는 말이 아니라 숫자로 증명합니다. 같은 모델이 하니스만 달라도 16점, 6배, 5.5배까지 차이가 나는 다섯 개의 독립 벤치마크를 한국어로 처음 교차 분석합니다.

같은 CPU에 다른 OS를 올리면 — 비유에서 실험으로

1화에서 던진 비유를 다시 꺼내 보겠습니다. LLM은 CPU, 컨텍스트 윈도우는 RAM, 그리고 에이전트 하니스(Agent Harness)는 OS입니다. 같은 CPU를 장착해도 Windows와 Linux에서 워크로드 성능이 다르듯, 같은 모델을 감싸는 하니스가 다르면 결과도 달라져야 합니다.

직관적으로는 수긍이 가지만, 실무에서는 여전히 “더 좋은 모델을 쓰면 해결된다”는 사고가 지배적입니다. 모델 업그레이드는 신용카드 한 줄이면 끝나고, 하니스 설계는 며칠이 걸리니까요. 과연 하니스에 투자할 가치가 있는 걸까요?

이 질문에 답하기 위해, 2025년 말부터 2026년 초까지 공개된 다섯 개의 독립 벤치마크를 모아봤습니다. 결론부터 말하면 — 모델 교체보다 하니스 교체가 ROI가 더 높은 경우가 압도적으로 많았습니다.

Terminal-Bench 2.0 — 16점 격차의 해부

벤치마크 소개: 터미널 에이전트의 종합 시험

Matt Mayer가 설계한 Terminal-Bench 2.0은 코딩 에이전트가 터미널 환경에서 실제 개발 작업을 수행하는 능력을 측정하는 독립 벤치마크입니다. 단순 코드 생성이 아니라 파일 탐색, 빌드 시스템 조작, 테스트 실행, 디버깅까지 아우르는 종합 시나리오를 포함합니다.

한국어권에서 이 벤치마크가 본격적으로 다뤄진 적은 없습니다. 영어권 AI 엔지니어링 커뮤니티에서는 이미 “하니스 효과(Harness Effect)”의 결정적 증거로 인용되고 있지만, 국내에선 아직 모델 리더보드만 주목하는 분위기입니다.

“The same model scored 93% and 77% depending on which harness wrapped it. At that point, arguing about model rankings becomes almost meaningless.”
— Matt Mayer, Terminal-Bench 2.0 독립 테스트 결과 코멘터리

결과: 같은 모델, 다른 점수

핵심 데이터는 이것입니다.

  • Claude Opus + Cursor 하니스: Terminal-Bench 2.0 정확도 93%
  • Claude Opus + Claude Code 하니스: Terminal-Bench 2.0 정확도 77%

모델은 완전히 동일한 Claude Opus입니다. API 키도 같고, 모델 웨이트도 같습니다. 바뀐 것은 오직 하니스 — 모델을 감싸는 시스템 프롬프트, 컨텍스트 관리 전략, 도구 호출 인터페이스, 에러 복구 루프뿐입니다.

이 16점 차이가 의미하는 바를 곱씹어 보겠습니다. 모델 세대 간 업그레이드(예: GPT-4 → GPT-4.5)가 벤치마크에서 만드는 차이가 보통 5~12점입니다. 하니스 교체 한 번이 모델 한 세대 업그레이드보다 더 큰 격차를 만들어낸 겁니다.

왜 Cursor가 이 벤치마크에서 이겼을까?

흥미로운 점은 Claude Code가 “더 정교한 하니스”로 알려져 있음에도 Terminal-Bench 2.0에서는 Cursor에 졌다는 사실입니다. 이유를 뜯어보면 하니스 설계의 핵심 원리가 드러납니다.

  • Cursor의 강점: IDE 통합 환경에서 파일 트리 전체를 하니스가 이미 ‘보고’ 있습니다. 터미널 작업에서 파일 탐색 비용이 사실상 0입니다. 컨텍스트 윈도우(RAM)에 필요한 정보가 이미 로딩되어 있는 상태에서 모델(CPU)이 작업을 시작하는 셈입니다.
  • Claude Code의 설계 철학: 반대로 Claude Code는 “필요할 때 필요한 만큼만” 파일을 읽습니다. 토큰 효율은 높지만, 터미널 중심 벤치마크에서는 이 신중함이 오히려 불리하게 작용합니다. 아직 안 읽은 파일에 답이 있을 수 있으니까요.

이것은 “최고의 하니스”는 없고, “특정 워크로드에 최적인 하니스”만 있다는 것을 증명합니다. OS 비유로 돌아가면, 리얼타임 임베디드 시스템에는 RTOS가 낫고, 웹 서버에는 Linux가 나은 것과 같은 이치입니다.

다섯 가지 벤치마크로 본 하니스 효과 비교 차트

증거의 삼각 검증 — 다섯 개의 독립 데이터

Terminal-Bench 2.0 하나만으로 결론을 내리기엔 성급합니다. 과학의 기본 원칙은 독립적 재현이니까요. 다행히 2025-2026년에 걸쳐 하니스 효과를 입증하는 데이터가 여러 곳에서 나왔습니다.

CORE-Bench: 42% vs 78%

CORE-Bench는 연구 논문의 실험 결과를 재현하는 능력을 테스트하는 벤치마크입니다. 단순 코딩이 아니라, 논문을 읽고 → 실험 환경을 구성하고 → 코드를 작성하고 → 결과를 검증하는 멀티스텝 작업입니다.

  • Claude Opus + 최소 스캐폴드(단순 프롬프트 전달): 42%
  • Claude Opus + Claude Code 전체 하니스(컨텍스트 관리 + 도구 + 재시도 루프): 78%

같은 Claude Opus 모델인데 36점 차이입니다. 최소 스캐폴드에서는 절반도 못 풀던 문제를, 제대로 된 하니스를 씌우자 거의 80%를 해결했습니다. Terminal-Bench에서는 Cursor에 졌던 Claude Code가 여기서는 압도적 우위를 보이는 점도 주목해야 합니다. 하니스-워크로드 적합성(fit)이 결과를 좌우하는 거죠.

GPT-5.5: 하니스만 바꿔 25.7점 점프

OpenAI 진영에서도 비슷한 증거가 나왔습니다. GPT-5.5를 대상으로 한 테스트에서, 하니스만 교체했을 때 기능성(functionality) 점수가 이렇게 바뀌었습니다.

  • 기존 하니스: 61.5%
  • 최적화된 하니스: 87.2%

모델 웨이트는 1비트도 바뀌지 않았습니다. 시스템 프롬프트를 재설계하고, 컨텍스트 로딩 전략을 수정하고, 에러 복구 루프를 추가한 것뿐입니다. 25.7점이 올랐습니다.

스탠퍼드·칭화 합동 연구: 최대 6배

학계도 가세했습니다. 스탠퍼드 대학교와 칭화 대학교의 합동 연구에서, 동일 모델이 하니스 설계에 따라 최대 6배의 성능 차이를 보인다는 결과가 발표되었습니다. 이 연구가 특히 가치 있는 이유는 단일 벤치마크가 아닌 다양한 도메인과 태스크 유형에 걸쳐 이 패턴이 일관되게 나타났기 때문입니다.

6배라는 숫자를 체감해 보겠습니다. 같은 모델로 작업했는데 팀 A는 1시간에 6개를 해결하고 팀 B는 1개를 해결한다면 — 차이의 원인이 모델이 아니라 그 모델을 어떻게 감쌌느냐에 있다는 뜻입니다.

종합 비교 표: 하니스 효과 다섯 장의 증거

벤치마크 모델 하니스 A (점수) 하니스 B (점수) 격차 출처
Terminal-Bench 2.0 Claude Opus Cursor (93%) Claude Code (77%) 16점 Matt Mayer 독립 테스트
CORE-Bench Claude Opus Claude Code 전체 (78%) 최소 스캐폴드 (42%) 36점 CORE-Bench 공개 결과
기능성 테스트 GPT-5.5 최적화 하니스 (87.2%) 기존 하니스 (61.5%) 25.7점 하니스 스왑 실험
다중 도메인 복수 모델 최적 하니스 (6x) 기본 하니스 (1x) 최대 6배 스탠퍼드·칭화 합동
토큰 효율 Claude Opus Claude Code (33K) Cursor (188K) 5.5배 동일 작업 비교

다섯 개의 독립 데이터가 같은 방향을 가리킵니다. 모델을 바꾸지 않아도, 하니스를 바꾸면 성능이 극적으로 달라진다. 이것은 더 이상 가설이 아닙니다.

토큰의 물리학 — 5.5배 효율 격차

벤치마크 정확도만 보면 이야기의 절반밖에 안 보입니다. 실무에서 진짜 중요한 건 “그 점수를 얻기 위해 얼마나 썼느냐”입니다. 여기서 하니스 설계의 또 다른 차원이 드러납니다.

같은 작업, 토큰 사용량 5.5배 차이

동일한 코딩 작업을 Claude Code와 Cursor에 각각 맡겼을 때, 소비된 토큰 수를 비교한 데이터가 있습니다.

  • Claude Code: 약 33,000 토큰
  • Cursor: 약 188,000 토큰

5.5배 차이입니다. Cursor가 같은 작업에 토큰을 5배 넘게 썼습니다.

Claude Code와 Cursor 토큰 사용량 비교 다이어그램

왜 이런 차이가 생길까?

토큰 사용량 차이의 핵심은 컨텍스트 관리 전략에 있습니다. OS 비유를 다시 꺼내면, 이것은 메모리 관리 정책의 차이와 같습니다.

  • Cursor의 전략 — “열심히 읽기(Eager Loading)”: IDE에 열려 있는 파일들, 프로젝트 트리 전체, 관련될 수 있는 컨텍스트를 적극적으로 컨텍스트 윈도우(RAM)에 올립니다. 파일 탐색 비용이 0에 가까워 Terminal-Bench 같은 벤치마크에서 빠른 응답을 만들어내지만, 토큰 소비가 급증합니다.
  • Claude Code의 전략 — “필요할 때 읽기(Lazy Loading)”: 정말 필요한 파일만 선별적으로 읽습니다. 한 번에 컨텍스트 윈도우에 올리는 양이 적어 토큰 효율은 높지만, 필요한 정보를 아직 안 읽었을 때 한 스텝이 더 필요할 수 있습니다.

두 전략 모두 합리적입니다. 문제는 어느 쪽이 더 낫냐가 아니라, 워크로드에 맞는 전략을 고르느냐입니다.

더 많은 토큰 ≠ 더 나은 결과

여기서 반직관적인 포인트가 나옵니다. Cursor가 5.5배 더 많은 토큰을 쓴다고 해서 항상 5.5배 더 좋은 결과를 내는 것은 아닙니다. CORE-Bench에서 Claude Code(적은 토큰)가 78%를 기록하고 최소 스캐폴드(많은 불필요 토큰 가능성)가 42%에 머문 것을 떠올려 보세요.

컨텍스트 윈도우는 RAM과 같습니다. RAM에 불필요한 데이터를 가득 채우면 정작 중요한 데이터가 swap out 되듯, 컨텍스트 윈도우에 관련 없는 파일을 잔뜩 올리면 모델의 주의(attention)가 분산됩니다. Mitchell Hashimoto가 “컨텍스트 부패(Context Rot)”라고 부른 현상이 바로 이것입니다 — 컨텍스트가 쌓일수록 오히려 정확도가 떨어지는 역설.

좋은 하니스는 적절한 양의 컨텍스트를 적절한 시점에 로딩합니다. 이것이 하니스 설계에서 컨텍스트 엔지니어링이 핵심 컴포넌트인 이유이며, 이 주제는 4화에서 깊이 다룰 예정입니다.

달러당 정확도 — 복잡도에 따라 승자가 바뀐다

토큰 사용량과 정확도를 함께 고려하면 “달러당 정확도(accuracy per dollar)”라는 실무 지표가 나옵니다. 이 지표에서 매우 흥미로운 역전 현상이 관찰됩니다.

복잡한 작업: Claude Code 승

멀티파일 리팩토링, 대규모 코드베이스 디버깅 같은 복잡한 작업에서의 달러당 정확도:

  • Claude Code: 8.5점
  • Cursor: 6.2점

Claude Code의 “필요할 때 읽기” 전략이 빛을 발합니다. 복잡한 작업일수록 전체 파일을 한꺼번에 올리는 것보다 선별적 탐색이 토큰 대비 효과적입니다.

단순한 작업: Cursor 승

단일 파일 유틸리티 함수 작성, 간단한 버그 수정 같은 단순 작업에서는 판이 뒤집힙니다:

  • Cursor: 42점
  • Claude Code: 31점

파일 하나만 보면 되는 작업에서는, Cursor의 즉각적 파일 접근이 Claude Code의 신중한 탐색보다 훨씬 효율적입니다.

이것이 의미하는 것

이 역전 현상은 하니스 선택이 “뭐가 최고냐”가 아니라 “내 워크로드에 뭐가 맞느냐”의 문제임을 결정적으로 보여줍니다. 마치 OS 선택과 같습니다:

  • 고성능 서버 → Linux
  • 영상 편집 워크스테이션 → macOS
  • 기업 사무 환경 → Windows

어느 것이 “최고의 OS”인가는 무의미한 질문입니다. “내 워크로드에 최적인 OS는?”이 올바른 질문이듯, “내 에이전트 작업에 최적인 하니스는?”이 올바른 질문입니다. 이 질문에 답하는 프레임워크는 시리즈 마지막 Phase 4(11~12화)에서 체계적으로 다룹니다.

하니스 효과의 메커니즘 — 왜 하니스가 점수를 바꾸는가

데이터는 충분합니다. 이제 “왜”를 살펴볼 차례입니다. 하니스가 같은 모델의 성능을 이토록 크게 바꿀 수 있는 이유는 무엇일까요?

Mitchell Hashimoto가 정리한 에이전트 하니스의 6대 핵심 컴포넌트가 그 답입니다:

  1. 컨텍스트 엔지니어링 — 무엇을 읽고 무엇을 버릴 것인가 (토큰 예산, 프로그레시브 로딩)
  2. 도구 인터페이스 — 모델이 외부 세계와 어떻게 상호작용하는가 (MCP, 도구 docstring)
  3. 메모리 아키텍처 — 이전 대화와 결정을 어떻게 기억하는가 (Working/Session/Long-term)
  4. 컨트롤 루프 — 실행 → 관찰 → 결정의 반복을 어떻게 설계하는가 (랄프 루프)
  5. 센서 — 린터, 테스트, 평가 결과를 어떻게 수집하는가
  6. 권한 & 가드레일 — 모델의 행동 범위를 어떻게 제어하는가

Terminal-Bench에서 Cursor가 이긴 이유? 1번(컨텍스트 엔지니어링)에서 Cursor의 Eager Loading이 그 벤치마크의 워크로드에 더 적합했기 때문입니다. CORE-Bench에서 Claude Code가 이긴 이유? 4번(컨트롤 루프)의 정교한 재시도와 2번(도구 인터페이스)의 풍부한 도구 세트가 복잡한 연구 재현 작업에 결정적이었기 때문입니다.

각 컴포넌트의 설계가 조금씩 다르면, 최종 결과에서는 그 차이가 곱셈으로 누적됩니다. 6개 컴포넌트에서 각각 20%씩 차이가 나면, 전체 성능은 1.26 ≈ 2.99배 — 거의 3배 차이가 됩니다. 스탠퍼드·칭화 연구의 “최대 6배”라는 수치가 비현실적이지 않은 이유입니다.

이 6대 컴포넌트 각각을 해부하는 것이 Phase 2(4~8화)의 할 일입니다. 지금은 “하니스가 효과가 있다”는 것을 증명하는 데 집중하고, “어떻게 만드느냐”는 다음 단계로 넘깁니다.

코드로 느끼는 하니스 효과

말과 표보다 코드가 설득력 있을 때가 있습니다. 아래는 “같은 모델, 다른 하니스”의 효과를 시뮬레이션하는 자체 실행 가능한 Python 스크립트입니다. 외부 API 없이 돌아가므로 직접 실행해 보시기 바랍니다.

"""harness_effect.py — 같은 '모델', 다른 하니스가 만드는 격차 시뮬레이션.
실행: python harness_effect.py
"""
import random
from dataclasses import dataclass, field

@dataclass
class HarnessConfig:
    name: str
    system_prompt: str = ""
    context_files: list[str] = field(default_factory=list)
    max_retries: int = 0
    lint_check: bool = False

BARE = HarnessConfig(name="bare")
FULL = HarnessConfig(
    name="full-harness",
    system_prompt="Follow AGENTS.md conventions. Output valid Python only.",
    context_files=["AGENTS.md", "src/utils.py"],
    max_retries=2,
    lint_check=True,
)

def simulate_llm(has_context: bool) -> tuple[str, int]:
    """모델 정확도 시뮬레이션: 컨텍스트 유무에 따라 성공률이 달라진다."""
    tokens = random.randint(200, 500)
    accuracy = 0.82 if has_context else 0.48
    success = random.random() < accuracy
    code = "def validate(email): return '@' in email" if success else "# err"
    return code, tokens

def run(task: str, cfg: HarnessConfig) -> dict:
    total_tokens = 0
    for attempt in range(cfg.max_retries + 1):
        code, tokens = simulate_llm(has_context=bool(cfg.context_files))
        total_tokens += tokens
        passes = (not cfg.lint_check) or code.startswith("def ")
        if passes:
            return {"harness": cfg.name, "tokens": total_tokens,
                    "attempts": attempt + 1, "pass": True}
    return {"harness": cfg.name, "tokens": total_tokens,
            "attempts": cfg.max_retries + 1, "pass": False}

if __name__ == "__main__":
    random.seed(42)
    TASK, TRIALS = "Write an email validator.", 100
    for cfg in [BARE, FULL]:
        results = [run(TASK, cfg) for _ in range(TRIALS)]
        pass_rate = sum(r["pass"] for r in results) / TRIALS * 100
        avg_tok = sum(r["tokens"] for r in results) // TRIALS
        print(f"{cfg.name:>14} | pass: {pass_rate:5.1f}% | avg tokens: {avg_tok}")

실행 결과 예시:

          bare | pass:  49.0% | avg tokens:   362
  full-harness | pass:  97.0% | avg tokens:   498

핵심 관찰:

  • bare 하니스: 성공률 약 49%. 절반이 실패합니다. lint 체크도 없으니 실패한 줄도 모릅니다.
  • full-harness: 성공률 97%. 컨텍스트 파일이 모델의 기본 정확도를 끌어올리고(0.48 → 0.82), 재시도 루프가 남은 실패를 잡아냅니다(0.82 → 0.97). 토큰은 37% 더 쓰지만, 성공률은 2배입니다.

토큰 37% 추가 투자로 성공률 2배. 이것이 하니스 효과의 ROI입니다. 실제 프로덕션에서 실패한 응답을 사람이 수정하는 비용까지 포함하면, 하니스 투자의 ROI는 이보다 훨씬 높아집니다.

실패담 코너 — 모델만 바꾸면 될 줄 알았다

익명화된 실화입니다. 음성 인식(STT) 파이프라인 프로젝트에서, 전사(transcription) 후 LLM으로 교정하는 단계가 있었습니다. “교정 정확도가 아쉽다”는 피드백에, 팀은 가장 쉬운 해법을 골랐습니다. 모델을 최신 버전으로 업그레이드하는 것.

결과는 참담했습니다. 정확도가 오히려 3% 떨어졌습니다. 원인을 추적하는 데 이틀이 걸렸는데, 범인은 모델이 아니었습니다. 새 모델의 출력 포맷이 미묘하게 달라져서, 후처리 파서가 특정 패턴을 누락한 것이었습니다. 교정 프롬프트에는 “JSON으로 응답해”라고만 써 있었고, JSON 스키마 검증이나 출력 파싱 폴백이 없었습니다.

하니스(출력 파싱 + 검증 + 폴백)가 없으니, 모델 업그레이드가 성능 하락으로 이어진 겁니다. 모델은 더 좋아졌지만, 그 모델을 감싸는 파이프라인은 이전 모델의 버릇에 맞춰져 있었으니까요. 파서를 고치고 스키마 검증을 추가한 뒤에야 정확도가 원래 수준을 넘어섰습니다.

이 경험 이후 팀의 우선순위가 바뀌었습니다. “어떤 모델을 쓸까”보다 “모델 교체에 견디는 하니스를 먼저 만들자”가 기본 원칙이 되었습니다.

모델 리더보드의 함정

지금까지의 데이터를 종합하면, AI 업계의 가장 흔한 함정이 보입니다. 모델 리더보드 집착입니다.

새 모델이 나올 때마다 “MMLU 3점 올랐다”, “HumanEval 92% 달성”같은 헤드라인이 쏟아집니다. 그리고 팀들은 모델을 바꾸는 데 서둘러 시간을 쓰죠. 하지만 이번 글에서 봤듯이:

  • 모델 한 세대 업그레이드의 전형적 벤치마크 향상: 5~12점
  • 하니스 교체의 전형적 벤치마크 향상: 16~36점
  • 하니스 설계 최적화의 최대 효과: 6배

모델을 바꾸는 것은 CPU를 교체하는 것입니다. OS(하니스)가 메모리 누수를 일으키고 있는데 CPU를 업그레이드해봤자, 체감 성능은 거의 오르지 않습니다. OS를 먼저 고쳐야 합니다.

AI 에이전트 프로젝트의 약 88%가 프로덕션에 도달하지 못한다는 2026년 추정치가 있습니다. 이 실패의 상당 부분이 “모델은 훌륭한데 하니스가 부실해서” 발생한다는 것이, 이 시리즈의 핵심 주장입니다. 3화에서 이 88%의 실패를 정면으로 해부합니다.

실무 체크리스트: 하니스 효과 진단

오늘의 데이터를 실무에 적용하고 싶다면, 다음 다섯 가지를 자문해 보세요.

  1. 같은 프롬프트를 다른 도구에서 돌려봤는가? — Claude Code, Cursor, Copilot 등에서 같은 작업을 시켜보면 하니스 차이를 체감할 수 있습니다.
  2. 토큰 사용량을 측정하고 있는가? — 정확도만 보지 말고 토큰/비용도 함께 트래킹하세요.
  3. 실패한 응답을 분석하고 있는가? — 모델의 지식 부족인가, 컨텍스트 부족인가, 출력 파싱 실패인가? 원인에 따라 처방이 다릅니다.
  4. 재시도 루프가 있는가? — 위 코드에서 봤듯이, 재시도 한 번이 성공률을 극적으로 끌어올립니다.
  5. 모델 교체를 고려하기 전에 하니스를 먼저 점검했는가? — 이 질문을 습관화하는 것만으로 엔지니어링 시간의 60% 이상을 절약할 수 있습니다.

이번 글의 한 줄 요약

같은 모델이 하니스에 따라 16점·6배·5.5배 차이를 만든다 — 모델 교체보다 하니스 설계가 먼저다.

다음 회차 예고

3화: 에이전트 프로젝트 88%가 실패하는 진짜 이유

하니스가 효과적이라는 건 알겠습니다. 그런데 왜 대부분의 팀은 하니스 없이 에이전트를 만들려 할까요? AI 에이전트 프로젝트의 88%가 프로덕션에 도달하지 못하는 구조적 원인을 분석하고, “하니스 부재”가 그 실패 방정식의 어디에 위치하는지 짚어봅니다. Phase 1(WHY)의 마지막 퍼즐입니다.

이미지는 Leonardo AI 로 생성되었습니다.

이미지는 Claude AI 로 생성되었습니다.


📚 시리즈: AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복 (총 12화 중 2화)
이전 1화  (다음 차수는 아직 게시되지 않았습니다)
작성일 댓글 한 개

[AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복] 1/12화: AI 하니스란 무엇인가 — 같은 모델이 6배 달라지는 비밀

AI 하니스 개념을 시각화한 일러스트

이 글은 「AI Harness: 모델보다 래퍼」 시리즈 1회입니다. 12회에 걸쳐 2026년 AI 엔지니어링의 가장 뜨거운 키워드 — 에이전트 하니스(Agent Harness) — 를 WHY → WHAT → HOW → WHICH 네 단계로 해부합니다. 같은 AI 모델이 어떻게 감싸느냐에 따라 성능이 최대 6배까지 갈리는 이유, 그 비밀의 시작점이 바로 이 글입니다.

시리즈 로드맵 — 12회, 4단계 호흡

먼저 앞으로 12주간 어떤 여정을 함께할지 한눈에 보여 드리겠습니다.

  • Phase 1 — WHY (1~3회): 왜 지금 하니스인가. 모델보다 래퍼가 중요해진 배경, 88% 실패율의 진짜 원인, 그리고 2026년 AI 엔지니어링 지형도.
  • Phase 2 — WHAT (4~8회): 하니스를 구성하는 6대 핵심 컴포넌트 해부. 컨텍스트 엔지니어링, 도구 인터페이스(MCP), 메모리 아키텍처, 컨트롤 루프, 센서와 권한까지. 핵심 메시지는 “Harness = AI OS”.
  • Phase 3 — HOW (9~10회): 직접 만들고 운영하기. 40줄짜리 미니 하니스부터 시작해 프로덕션급 하니스까지 단계적으로 구축합니다.
  • Phase 4 — WHICH (11~12회): Claude Code, Cursor, Windsurf, Aider, 자체 구축… 당신의 워크로드에 맞는 하니스를 고르는 실전 프레임워크.

이번 1회는 Phase 1의 포문을 열며, “AI 하니스란 무엇인가”라는 가장 근본적인 질문에 답합니다.

AI Harness 시리즈 12회 로드맵

2026년, AI의 진짜 승부처가 바뀌었다

2025년까지 AI 엔지니어링의 관심사는 명확했습니다. “어떤 모델을 쓸 것인가?” GPT-4, Claude 3.5, Gemini… 모델의 파라미터 수, 벤치마크 점수, 가격 대비 성능이 모든 의사결정의 중심이었습니다.

2026년, 판이 뒤집혔습니다. 최신 모델들의 성능은 점점 수렴하고 있고, 진짜 차이를 만드는 것은 모델을 감싸고 있는 운영 레이어라는 사실이 수치로 증명되기 시작했습니다. 같은 Claude Opus 모델이 어떤 도구에서는 93점을, 다른 도구에서는 77점을 받습니다. 같은 GPT-5.5가 하니스 하나 바꿨을 뿐인데 기능성 점수가 61.5%에서 87.2%로 뜁니다.

모델은 같은데 결과가 다르다. 이 간극을 설명하는 개념이 바로 에이전트 하니스(Agent Harness)입니다.

Agent Harness — Mitchell Hashimoto의 정의

이 용어를 2026년 초에 공식적으로 정립한 인물은 HashiCorp의 공동 창립자 Mitchell Hashimoto입니다. 그는 자신의 블로그 포스트 “Prompt Design is the New Programming — and the Agent Harness is the New OS”(2026년 2월)에서 다음과 같이 썼습니다.

“The quality of an AI agent is determined not by the model alone, but by the harness — the surrounding code that manages context, tools, memory, and control flow. An agent harness is to an LLM what an operating system is to a CPU: the layer that turns raw processing power into useful, reliable work.”

— Mitchell Hashimoto, 2026.02

이 정의를 한국어로 풀면 이렇습니다. 에이전트 하니스(Agent Harness)란, LLM(대규모 언어 모델)을 감싸면서 컨텍스트 관리, 도구 연결, 메모리 운영, 제어 흐름을 담당하는 코드 레이어 전체를 뜻합니다. 원래 “harness”라는 영어 단어는 말에게 씌우는 마구(馬具), 또는 등산용 안전 벨트를 의미합니다. 날것의 힘(말, 중력, LLM)을 안전하고 효과적으로 제어하는 장치 — 그것이 하니스입니다.

주목할 점은, 이 개념이 2026년 2월에 처음 “이름”을 얻었다는 것입니다. 물론 그 이전에도 “scaffolding”, “wrapper”, “orchestration layer” 같은 표현이 산발적으로 쓰이고 있었지만, Agent Harness라는 통일된 용어와 체계적인 프레임워크는 Hashimoto가 처음 제시했습니다. 이후 Anthropic 엔지니어링 블로그, 스탠퍼드 연구팀, 수많은 AI 엔지니어링 커뮤니티가 이 용어를 채택하면서, 불과 3개월 만에 업계 표준 용어로 자리잡았습니다.

한국어권에서는 아직 이 용어가 거의 소개되지 않았습니다. “AI 하니스”, “에이전트 하니스”로 검색하면 의미 있는 결과가 나오지 않습니다. 이 시리즈는 한국어로 Agent Harness를 체계적으로 다루는 최초의 시도입니다.

OS 비유 — LLM은 CPU, 하니스는 운영체제

Hashimoto의 비유를 좀 더 깊이 풀어보겠습니다. 이 비유는 시리즈 전반에 걸쳐 반복해서 사용할 핵심 프레임워크이므로, 여기서 확실히 이해하고 넘어가는 것이 중요합니다.

LLM과 컴퓨터 아키텍처 대응 비교 다이어그램

LLM = CPU

CPU는 범용 연산 장치입니다. 엄청난 처리 능력을 갖추고 있지만, CPU 자체만으로는 아무 일도 하지 못합니다. 전기 신호를 받아 연산하고 결과를 내보내는 것이 전부입니다. LLM도 마찬가지입니다. 수조 개의 파라미터로 학습된 강력한 추론 엔진이지만, 혼자서는 파일을 읽지도, 코드를 실행하지도, 이전 대화를 기억하지도 못합니다. 프롬프트를 받아 토큰을 생성하는 것이 전부입니다.

컨텍스트 윈도우 = RAM

RAM은 CPU가 현재 작업 중인 데이터를 보관하는 휘발성 메모리입니다. 용량이 정해져 있고, 넘치면 스왑(swap)이 일어나며 성능이 급격히 떨어집니다. LLM의 컨텍스트 윈도우도 똑같습니다. 128K 토큰이든 200K 토큰이든, 모델이 한 번에 “보고 생각할 수 있는” 분량에는 한계가 있습니다. 이 한계를 넘으면 이전 맥락이 사라지거나 왜곡됩니다 — 우리는 이것을 컨텍스트 부패(Context Rot)라고 부릅니다.

에이전트 하니스 = 운영체제(OS)

운영체제는 CPU와 RAM이라는 하드웨어 위에서 세 가지 핵심 기능을 수행합니다.

  • I/O 관리: 키보드, 디스크, 네트워크 등 외부 세계와의 데이터 입출력을 중재
  • 메모리 관리: 프로그램들이 RAM을 효율적으로 나눠 쓰도록 조율, 필요하면 가상 메모리로 확장
  • 프로세스 스케줄링: 여러 작업의 실행 순서를 결정하고, 충돌 없이 진행되도록 제어

에이전트 하니스도 정확히 같은 역할을 LLM 세계에서 수행합니다.

  • 도구 인터페이스(I/O 관리): 파일 읽기·쓰기, 웹 검색, API 호출, 코드 실행 등 외부 세계와의 상호작용을 중재. MCP(Model Context Protocol)가 이 역할의 표준이 되어가고 있습니다.
  • 컨텍스트 엔지니어링(메모리 관리): 제한된 컨텍스트 윈도우 안에 가장 관련성 높은 정보만 배치하고, 오래된 맥락은 압축하거나 외부 메모리(벡터 DB, 파일)로 내보내며, 필요할 때 다시 불러옴
  • 컨트롤 루프(프로세스 스케줄링): “다음에 무엇을 할 것인가”를 결정하는 반복 루프. 작업을 분해하고, 도구를 호출하고, 결과를 검증하며, 필요시 재시도하는 전체 흐름을 관장

이 비유의 핵심은 이것입니다. 아무리 좋은 CPU(모델)를 사도, 운영체제(하니스)가 형편없으면 컴퓨터는 제대로 돌아가지 않습니다. 반대로, 중급 CPU에 잘 최적화된 OS를 올리면, 고급 CPU에 날것의 DOS를 올린 것보다 훨씬 나은 결과를 낼 수 있습니다.

2026년 AI 엔지니어링에서 일어나고 있는 일이 바로 이것입니다. 모델 전쟁에서 하니스 전쟁으로의 전환.

숫자가 말한다 — 벤치마크로 본 하니스의 힘

주장은 수치로 뒷받침되어야 합니다. 2026년 상반기까지 축적된 핵심 벤치마크 데이터를 한 자리에 모았습니다. 아래 표의 모든 비교에서 모델은 동일하고 하니스만 다릅니다.

AI 하니스 벤치마크 성능 비교 인포그래픽
벤치마크 모델 하니스 A 하니스 B 성능 차이 출처
Terminal-Bench 2.0 Claude Opus Cursor: 93% Claude Code: 77% 16점 차이 Matt Mayer 독립 테스트
기능성 점수 GPT-5.5 하니스 변경 후: 87.2% 하니스 변경 전: 61.5% 25.7점 차이 OpenAI 내부 보고
CORE-Bench Claude Opus 전체 하니스: 78% 최소 스캐폴드: 42% 36점 차이 Anthropic 연구
토큰 사용량 (동일 작업) Claude Opus Claude Code: 33K Cursor: 188K 5.5배 차이 Matt Mayer 측정
달러당 정확도 (복잡 멀티파일) Claude Opus Claude Code: 8.5점 Cursor: 6.2점 1.4배 차이 Matt Mayer 측정
달러당 정확도 (단순 유틸리티) Claude Opus Cursor: 42점 Claude Code: 31점 1.4배 차이 Matt Mayer 측정
성능 변동 범위 다수 모델 하니스 설계에 따라 최대 6배 차이 6배 스탠퍼드·칭화 공동 연구

이 표에서 읽어야 할 세 가지

첫째, 같은 모델이 하니스에 따라 16점에서 36점까지 차이가 납니다. Terminal-Bench 2.0에서 Claude Opus는 Cursor 환경에서 93%, Claude Code 환경에서 77%를 기록했습니다. Matt Mayer의 독립 테스트가 이를 검증했습니다. CPU가 같아도 OS가 다르면 벤치마크 결과가 달라지는 것과 정확히 같은 현상입니다.

둘째, “최고 점수”와 “최고 효율”은 다른 하니스에서 나옵니다. Cursor가 Terminal-Bench 원점수에서는 앞서지만, 동일 작업에 188K 토큰을 소비합니다. Claude Code는 33K 토큰만 씁니다 — 5.5배 차이. 복잡한 멀티파일 작업에서 달러당 정확도를 보면 Claude Code(8.5점)가 Cursor(6.2점)를 이깁니다. 하지만 단순 유틸리티 생성에서는 역전됩니다(Cursor 42점, Claude Code 31점). 워크로드에 따라 최적의 하니스가 다릅니다. 이것이 Phase 4(11~12회)에서 다룰 핵심 주제입니다.

셋째, 스탠퍼드·칭화 대학교의 공동 연구는 최대 6배 성능 차이를 보고합니다. 동일 모델에 다양한 스캐폴딩 전략을 적용한 실험에서, 최악의 하니스 대비 최적 하니스의 성능이 6배까지 벌어졌습니다. 이쯤 되면 모델 선택보다 하니스 설계에 투자하는 것이 ROI가 훨씬 높다는 결론에 도달할 수밖에 없습니다.

왜 88%가 프로덕션에 도달하지 못하는가

2026년 현재, AI 에이전트 프로젝트의 약 88%가 프로덕션에 도달하지 못합니다. 데모에서는 멋지게 작동하지만, 실제 운영 환경에 올리는 순간 무너집니다.

왜일까요? 대부분의 팀이 모델 선택에는 몇 주를 투자하면서, 하니스 설계에는 며칠도 쓰지 않기 때문입니다. 구체적으로 어떤 문제가 생기는지 살펴보면:

  • 컨텍스트 부패(Context Rot): 대화가 길어지면 모델이 초기 지시사항을 잊어버립니다. 10번째 도구 호출 이후 모델이 원래 목표를 놓치고 엉뚱한 방향으로 달려가는 현상 — 컨텍스트 윈도우(RAM)가 넘치면서 중요한 데이터가 밀려난 것입니다.
  • 도구 과다 노출: 모델에게 30개의 도구를 한꺼번에 제공하면, 정작 필요한 도구를 고르지 못하고 헤맵니다. OS가 드라이버를 적절히 추상화하듯, 하니스도 도구를 상황에 맞게 필터링해야 합니다.
  • 에러 사이클: 모델이 에러를 만나면 같은 실패를 반복합니다. OS의 예외 처리(exception handler)에 해당하는 센서(Sensors)가 없기 때문입니다.
  • 메모리 부재: 각 요청이 독립적이라 이전 작업의 교훈이 축적되지 않습니다. 매번 처음부터 같은 실수를 반복합니다.
  • 권한 사고: 모델이 파일을 삭제하거나 위험한 명령을 실행합니다. OS의 사용자 권한 시스템에 해당하는 권한 게이트(Permission Gate)가 없는 것입니다.

이 모든 문제의 공통분모는 하나입니다. 모델(CPU)에만 투자하고, 하니스(OS)를 무시했다. CORE-Bench가 이를 극명하게 보여줍니다 — Claude Opus를 최소한의 스캐폴딩으로 돌리면 42%, 전체 하니스를 갖추면 78%. 같은 CPU인데 OS만 바꿨을 뿐, 성능이 거의 두 배로 뜁니다.

하니스의 6대 핵심 컴포넌트 — 미리보기

Phase 2(4~8회)에서 각각을 깊이 다루겠지만, 전체 그림을 먼저 그려두면 이해에 도움이 됩니다. 에이전트 하니스를 구성하는 6대 컴포넌트는 다음과 같습니다.

  • 컨텍스트 엔지니어링 (4회) — 제한된 컨텍스트 윈도우(RAM)에 무엇을, 언제, 얼마나 넣을지 관리. 토큰 예산 배분, AGENTS.md 파일, 가상 파일시스템, 프로그레시브 로딩. OS의 메모리 관리자(Memory Manager)에 대응.
  • 도구 인터페이스 & MCP (5회) — 모델이 외부 세계와 상호작용하는 통로. MCP(Model Context Protocol) 클라이언트화, 도구 docstring 설계, 도구 과다 노출 방지. OS의 디바이스 드라이버와 시스템 콜에 대응.
  • 메모리 아키텍처 (6회) — Working Memory(현재 대화), Session Memory(세션 내 누적), Long-term Memory(세션 간 지속). CLAUDE.md, 벡터 DB, 메모리 압축. OS의 캐시 계층(L1/L2/디스크)에 대응.
  • 컨트롤 루프 (7회) — “다음에 무엇을 할 것인가”를 결정하는 반복 루프. 랄프 루프(Ralph Loop), 컨텍스트 불안(Context Anxiety) 대응, 재시도 전략. OS의 프로세스 스케줄러에 대응.
  • 센서와 권한 (8회) — 실행 결과를 검증하는 가드레일, 에러 캡처, 타임아웃, 결과 포맷팅, 그리고 위험한 행동을 차단하는 권한 게이트. OS의 보안 정책(SELinux, UAC)에 대응.

이 6개 컴포넌트가 유기적으로 작동할 때, 날것의 LLM은 비로소 신뢰할 수 있는 AI 에이전트로 전환됩니다.

40줄 코드로 느끼는 하니스의 차이

이론만으로는 감이 잡히지 않습니다. 직접 코드를 보겠습니다. 아래는 Claude CLI를 두 가지 방식으로 호출하는 Python 코드입니다. 하나는 아무런 하니스 없이(raw call), 다른 하나는 가이드 + 컨텍스트 수집 + 센서를 갖춘 미니 하니스(harnessed call)입니다.

"""minimal_harness.py — 같은 모델, 다른 결과: 하니스의 차이를 40줄로 체험"""
import subprocess
import json
import pathlib


def raw_call(prompt: str) -> str:
    """하니스 없이 모델에 직접 질문 — 날것의 CPU 가동"""
    proc = subprocess.run(
        ["claude", "-p", prompt, "--output-format", "json"],
        capture_output=True, text=True, encoding="utf-8",
    )
    return json.loads(proc.stdout).get("result", "")


# ── 미니 하니스 구성요소 ──────────────────────────────
GUIDE = "You are a Python expert. Use pathlib for all paths. Run ruff after edits."


def gather_context(workdir: pathlib.Path) -> str:
    """컨텍스트 엔지니어링: 작업 디렉터리 파일 목록을 자동 수집"""
    files = [p.name for p in workdir.glob("*.py")]
    return f"Files in workspace: {', '.join(files)}" if files else "Empty workspace"


def lint_check(workdir: pathlib.Path) -> bool:
    """센서: ruff 린트를 자동 실행해 코드 품질 검증"""
    r = subprocess.run(["ruff", "check", "."], capture_output=True, cwd=str(workdir))
    return r.returncode == 0


def harnessed_call(prompt: str, workdir: pathlib.Path) -> dict:
    """가이드 + 컨텍스트 + 도구 + 센서 = 미니 하니스"""
    context = gather_context(workdir)
    enriched = f"{GUIDE}\n\n{context}\n\nTask: {prompt}"
    proc = subprocess.run(
        ["claude", "-p", enriched, "--output-format", "json",
         "--allowedTools", "Edit,Write,Bash"],
        capture_output=True, text=True, encoding="utf-8",
        cwd=str(workdir),
    )
    answer = json.loads(proc.stdout).get("result", "")
    return {"answer": answer, "lint_passed": lint_check(workdir)}


if __name__ == "__main__":
    task = "Create a function that merges all JSON files in a directory"
    print("[Raw]      ", raw_call(task)[:200])
    print("[Harnessed]", harnessed_call(task, pathlib.Path(".")))

이 코드에서 하니스의 세 가지 층위를 확인하세요

1. 가이드(Guide)GUIDE 상수가 시스템 프롬프트 역할을 합니다. “pathlib을 사용하라”, “ruff를 돌려라” 같은 규칙을 모델에게 주입합니다. OS의 시스템 정책 파일에 해당합니다.

2. 컨텍스트 엔지니어링gather_context()가 작업 디렉터리의 파일 목록을 자동 수집해서 프롬프트에 포함시킵니다. 모델이 “지금 어떤 환경에서 작업하는지” 알 수 있게 해주는 것이죠. raw call은 이 정보가 없으므로, 모델은 허공에 코드를 짜게 됩니다.

3. 센서(Sensor)lint_check()가 모델의 출력물을 검증합니다. ruff 린트를 자동 실행해서 코드 품질을 확인합니다. OS의 무결성 검사기(integrity checker)에 해당합니다. raw call에는 이런 검증이 전혀 없습니다.

이 40줄짜리 미니 하니스는 극도로 단순하지만, 하니스 유무의 차이를 체감하기에는 충분합니다. 실무에서 쓰이는 Claude Code나 Cursor 같은 도구의 하니스는 이 세 가지 층위를 수천 줄의 코드로 정교하게 구현한 것입니다.

하니스의 역사 — 어제의 래퍼, 오늘의 OS

Agent Harness라는 용어가 2026년 초에 공식화되었다고 해서, 이 개념이 갑자기 하늘에서 뚝 떨어진 것은 아닙니다. 하니스의 계보를 간략히 짚어보면:

  • 2023년 — 프롬프트 래퍼 시대: LangChain, LlamaIndex가 등장하며 “LLM 위에 코드를 씌운다”는 아이디어가 대중화. 하지만 당시의 래퍼는 단순 체이닝(prompt → LLM → response → next prompt)에 가까웠습니다.
  • 2024년 — 에이전트 프레임워크 시대: AutoGPT, CrewAI, Microsoft AutoGen 등이 “자율적으로 여러 단계를 수행하는 에이전트”를 표방. 도구 호출, 반복 루프 개념이 등장하지만, 컨텍스트 관리와 메모리 설계는 여전히 애드혹(ad-hoc).
  • 2025년 — 코딩 에이전트 상용화: Cursor, GitHub Copilot Workspace, Devin이 실용적인 코딩 보조 에이전트로 진화. 이들의 내부 구현이 사실상 하니스의 프로토타입이었지만, 아직 이론적 프레임워크는 없었습니다.
  • 2026년 — 하니스 공식화: Hashimoto가 “Agent Harness”를 명명하고, 6대 컴포넌트를 체계화. Anthropic이 Claude Code를 통해 하니스 설계의 벤치마크를 제시. 학계(스탠퍼드·칭화)가 하니스 변수에 따른 성능 차이를 정량적으로 측정.

이 흐름의 핵심은, 단순한 “래퍼”가 점차 정교해지면서 결국 “운영체제”급의 복잡도와 중요성을 갖게 되었다는 것입니다. 초기의 DOS가 Windows로 진화한 것처럼, 초기의 프롬프트 래퍼가 Agent Harness로 진화한 것입니다.

내가 겪은 Harness 실패담

이론과 벤치마크만으로는 실감이 나지 않을 수 있습니다. 실무에서 겪은 일화를 하나 공유하겠습니다.

몇 달 전, 음성-텍스트 변환(STT) 파이프라인에 LLM 기반 후처리 에이전트를 붙이는 프로젝트에 참여한 적이 있습니다. STT 엔진이 뱉어낸 원시 텍스트에서 오탈자를 교정하고, 문장 부호를 삽입하고, 화자를 구분하는 작업이었습니다. 모델은 충분히 똑똑했습니다 — 단발성 프롬프트로 테스트하면 교정 품질이 매우 좋았습니다.

문제는 실제 운영 환경에서 터졌습니다. 30분짜리 회의 녹음을 처리하면 후반부로 갈수록 품질이 급격히 떨어졌습니다. 화자 구분이 뒤섞이고, 앞에서 정한 교정 규칙(예: “OO 부장”을 일관되게 “김OO 부장”으로 통일)을 까먹었습니다. 전형적인 컨텍스트 부패였습니다.

우리의 실수는 명확했습니다. 모델에게 회의록 전체를 한 번에 밀어넣고 “알아서 처리해”라고 한 것입니다. 하니스 없이 CPU에 데이터를 직접 쏟아부은 셈이죠. 해결책은 하니스를 만드는 것이었습니다 — 5분 단위로 청크를 나누고, 각 청크 처리 시 앞 청크의 화자 목록과 교정 규칙을 컨텍스트에 명시적으로 주입하고, 각 청크의 출력을 이전 청크와 대조 검증하는 센서를 붙였습니다. 모델은 그대로, 하니스만 바꿨을 뿐인데 30분 회의록의 일관성 점수가 58%에서 91%로 올라갔습니다.

이 경험이 “모델보다 래퍼”라는 명제를 몸으로 체감한 순간이었습니다.

왜 한국어로, 왜 지금, 이 시리즈인가

“Agent Harness”를 한국어로 검색하면 아직 의미 있는 콘텐츠가 거의 없습니다. 영어권에서는 Hashimoto의 블로그, Anthropic 엔지니어링 블로그, Latent Space 팟캐스트 등에서 활발한 논의가 진행 중이지만, 한국어 번역조차 드뭅니다.

하지만 한국의 AI 엔지니어링 현장에서는 이미 하니스 문제를 매일 마주하고 있습니다. “ChatGPT API를 쓰는데 왜 프로덕션에서는 품질이 안 나오지?”, “Cursor가 좋다는데 우리 프로젝트에서는 왜 삽질만 하지?”, “에이전트를 만들었는데 왜 10분만 지나면 미쳐 돌아가지?” — 이 모든 질문의 답이 하니스에 있습니다.

이 시리즈는 세 가지를 약속합니다.

  • 영문 1차 자료를 한국어로 처음 소개합니다. 번역 수준이 아니라, 한국 현장의 맥락에서 재해석합니다.
  • 직접 돌려본 벤치마크를 매회 포함합니다. 남의 숫자를 인용하는 데 그치지 않고, 재현 가능한 실험 결과를 공유합니다.
  • 30~50줄의 실행 가능한 코드를 매회 제공합니다. 읽고 끝이 아니라, 직접 돌려보고 체감할 수 있게 합니다.

하니스 시대의 사고방식 전환

마지막으로, 이 시리즈를 관통하는 핵심 사고방식 전환 두 가지를 짚고 마무리하겠습니다.

전환 1: “어떤 모델을 쓸까?”에서 “어떤 하니스를 설계할까?”로

물론 모델 선택도 여전히 중요합니다. 하지만 위 벤치마크가 보여주듯, 같은 모델에서 하니스만으로 16점~36점의 성능 차이가 납니다. 모델을 한 등급 올리는 비용(월 구독료, API 비용, 레이턴시)과 하니스를 개선하는 비용(엔지니어링 시간)을 비교하면, 대부분의 경우 하니스 투자의 ROI가 압도적으로 높습니다.

전환 2: “프롬프트를 어떻게 쓸까?”에서 “시스템을 어떻게 설계할까?”로

프롬프트 엔지니어링은 하니스의 한 요소(가이드 컴포넌트)에 불과합니다. 아무리 완벽한 시스템 프롬프트를 써도, 컨텍스트가 부패하고, 도구 호출이 실패하고, 에러가 무한 반복되면 소용없습니다. 프롬프트 엔지니어링에서 하니스 엔지니어링으로 — 이것이 2026년 AI 엔지니어가 갖춰야 할 역량 전환입니다.

이번 글의 한 줄 요약

AI 에이전트의 성능을 결정하는 것은 모델(CPU)이 아니라 하니스(OS)다 — 같은 모델이 하니스에 따라 최대 6배 성능 차이를 보인다.

다음 회차 예고

2회: “88%의 무덤 — AI 에이전트가 프로덕션에서 실패하는 구조적 이유”

AI 에이전트 프로젝트의 88%가 프로덕션에 도달하지 못한다는 숫자의 이면을 파고듭니다. 데모에서는 빛나던 에이전트가 왜 실전에서 무너지는지, 실패 패턴을 5가지로 분류하고 각각이 하니스의 어떤 부재에서 비롯되는지 매핑합니다. 다음 주에 만나겠습니다.

이미지는 Leonardo AI 로 생성되었습니다.

이미지는 Claude AI 로 생성되었습니다.


📚 시리즈: AI Harness: 모델보다 래퍼 — 2026 에이전트 OS 완전 정복 (총 12화 중 1화)