Compaction: 컨텍스트 압축 전략
루프는 계속 돌아야 한다
섹션 제목: “루프는 계속 돌아야 한다”에이전틱 루프는 외부로부터 작업을 받고, 도구를 실행하고, 관찰을 쌓고, 다시 추론한다. 이 과정이 반복될수록 컨텍스트 창은 채워진다. 컨텍스트 창이 꽉 차면 어떻게 될까? 가장 단순한 대응은 루프를 멈추고 실패를 반환하는 것이다. 하지만 실제 장기 에이전트에서 그것은 허용되지 않는 결과다. 수 시간 진행된 작업이 컨텍스트 한도 때문에 중간에 끊어지면 사용자 경험과 비용 모두 최악이다.
Compaction은 루프를 끊지 않고 이어가기 위해 컨텍스트를 압축하거나 재구성하는 기법 일체를 가리킨다. 핵심 목표는 하나다. 루프가 멈추지 않게 하면서도 중요한 정보는 잃지 않는다.
언제 Compaction을 트리거하나
섹션 제목: “언제 Compaction을 트리거하나”일반적으로 두 가지 임계치를 기준으로 삼는다.
┌───────────────────────────────────────────────────────┐│ Compaction 트리거 기준 │├───────────────────────────────────────────────────────┤│ ① 토큰 수 임계치 전체 창의 70~80% 도달 시 압축 시작 ││ 예: 128k 창 → 90k 토큰 시점에 compaction 수행 ││ ││ ② 이터레이션 주기 매 N회 이터레이션마다 주기적 압축 ││ 예: 10회 실행마다 중간 관찰 요약·정리 ││ ││ → 두 조건 중 먼저 도달한 것을 기준으로 사용 │└───────────────────────────────────────────────────────┘지나치게 일찍 압축하면 유용한 정보를 잃는다. 너무 늦게 압축하면 한도 초과로 호출이 실패할 수 있다. 실제 시스템에서는 경험적으로 70~80% 지점을 트리거로 설정하는 경우가 많다.
주요 압축 기법
섹션 제목: “주요 압축 기법”1. 대화 요약 (Conversation Summarization)
섹션 제목: “1. 대화 요약 (Conversation Summarization)”가장 일반적인 기법이다. 현재까지의 메시지 히스토리를 별도 모델 호출로 요약한 뒤, 그 요약으로 히스토리를 교체한다.
# 개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다.
def compact_by_summary(messages: list, keep_recent: int = 5) -> list: """ 오래된 메시지를 요약으로 압축하고, 최근 N개는 원본 유지. """ if len(messages) <= keep_recent: return messages
old_messages = messages[:-keep_recent] recent_messages = messages[-keep_recent:]
# 요약 생성 (별도 모델 호출 또는 경량 모델 사용) summary_text = summarize(old_messages)
summary_message = { "role": "system", "content": f"[이전 대화 요약]\n{summary_text}" }
return [summary_message] + recent_messages요약 시 보존해야 할 핵심 정보는 무엇인가? Anthropic은 다음을 권장한다. 완료된 행동과 그 결과, 핵심 발견 사항, 현재 진행 중인 하위 작업의 상태, 이후 스텝을 위한 중요 변수 값.
2. 도구 결과 정리 (Tool Result Clearing)
섹션 제목: “2. 도구 결과 정리 (Tool Result Clearing)”모든 과거 도구 호출 결과를 유지할 필요는 없다. 특히 대용량 파일 읽기나 긴 API 응답의 경우, 이미 모델이 그 내용을 처리한 후라면 전체 결과를 컨텍스트에 유지할 이유가 없다.
| 도구 결과 유형 | 처리 방식 |
|---|---|
| 파일 읽기 (대용량) | 핵심 발췌 또는 “읽기 완료” 마커로 교체 |
| API 응답 (반복적) | 마지막 결과만 유지, 이전 것 삭제 |
| 테스트 출력 (누적) | 최신 실행 결과만 유지 |
| 오류 메시지 (해결됨) | 해결 완료 표시 후 제거 |
3. 컨텍스트 재초기화 (Context Reinitialization)
섹션 제목: “3. 컨텍스트 재초기화 (Context Reinitialization)”가장 과감한 기법이다. 현재까지의 작업 상태를 구조화된 형태(파일, 메모 등)로 외부에 저장한 뒤, 컨텍스트를 완전히 새로 시작하고 저장된 상태를 다시 주입한다. 이 방식은 손실이 가장 적을 수 있지만, 상태를 올바르게 직렬화·역직렬화하는 설계가 필요하다.
# 개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다.
def reinitialize_context(task: str, state_file: str) -> list: """ 외부 상태 파일로부터 컨텍스트를 재구성. compaction의 극단적 형태 — 완전 재시작. """ # 현재 상태를 파일에 저장 (이전 스텝에서 수행됨) saved_state = load_state(state_file)
# 새 컨텍스트: 시스템 프롬프트 + 압축된 상태 요약 new_messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": task}, { "role": "assistant", "content": f"[재개 — 이전 진행 상황]\n{saved_state['progress_summary']}" } ] return new_messagesClaude Code와 Codex의 사례
섹션 제목: “Claude Code와 Codex의 사례”실제 프로덕션 에이전트들이 compaction을 어떻게 처리하는지 살펴보면 설계 원칙이 명확해진다.
Anthropic의 컨텍스트 엔지니어링 글에 따르면 Claude Code는 컨텍스트 창이 꽉 찰 때 전체 대화를 압축해 새 컨텍스트로 재시작하는 방식을 사용한다. 이때 최근 파일 변경 사항과 현재 작업 상태는 우선적으로 보존한다.
OpenAI Codex 루프 글에서도 유사한 접근이 확인된다. 장기 실행 코딩 에이전트는 이전 도구 결과를 축적하지 않고, 필요한 파일은 필요한 시점에 다시 읽는 방식(JIT 로드)을 결합해 컨텍스트를 관리한다.
두 시스템 모두 공통적으로 최근 작업 상태(최신 파일 상태, 현재 목표, 미완료 하위 작업)를 compaction 후에도 반드시 보존한다는 원칙을 따른다.
Compaction과 정보 손실의 트레이드오프
섹션 제목: “Compaction과 정보 손실의 트레이드오프”Compaction은 공짜가 아니다. 요약은 항상 손실이 있고, 어떤 세부 사항이 나중에 중요해질지 사전에 알 수 없다. 이 트레이드오프를 완화하는 방법이 두 가지 있다.
첫째, 외부 저장소 활용. 중요한 발견이나 결정을 파일(NOTES.md, 체크포인트 등)에 기록해 두면, compaction으로 컨텍스트에서 제거되더라도 나중에 도구 호출로 다시 읽어올 수 있다. 이 전략은 다음 챕터(5-4)에서 자세히 다룬다.
둘째, JIT(Just-In-Time) 검색. 파일 전체를 미리 컨텍스트에 올리는 대신, 필요한 시점에 필요한 부분만 가져온다. compaction과 JIT 검색을 결합하면 컨텍스트를 항상 “지금 당장 필요한 것”으로만 채울 수 있다. 이 전략은 챕터 5-5에서 다룬다.
Compaction은 루프 엔지니어링의 불가피한 현실이다. 컨텍스트 창이 아무리 커져도 장기 에이전트가 생성하는 정보의 총량은 그것을 초과할 수 있다. 어떤 정보를 어떤 형태로 압축·보존할지를 설계하는 것이 루프의 장기 실행 가능성을 결정한다.
참고 자료
- Anthropic — Effective context engineering for AI agents — 접속 2026-06-30
- OpenAI — Unrolling the Codex agent loop — 접속 2026-06-30
- Anthropic — Effective harnesses for long-running agents — 접속 2026-06-30