sy/dev
Tool
12 min read

Headroom: LLM 앞단에 context compression layer를 두는 이유

Headroom을 예로 agent 입력을 LLM에 넣기 전 압축·캐시·복원하는 context layer 설계 포인트를 정리한다.

한 줄 요약

Headroom은 tool output, 로그, RAG chunk, 파일, 대화 기록을 LLM에 보내기 전에 압축하는 context compression layer다. 핵심은 "긴 컨텍스트 모델을 쓰면 된다"가 아니라, LLM 앞단에서 어떤 정보를 줄이고, 어떤 원본을 되찾을 수 있게 만들지를 별도 런타임으로 다루는 점이다.

이 글은 Headroom을 완성된 표준으로 찬양하려는 글이 아니다. 오히려 agent 시스템을 만들 때 context layer가 왜 독립 컴포넌트가 되어야 하는지 보는 사례로 읽는 게 낫다.

왜 지금 context layer가 필요한가

요즘 agent는 모델에게 짧은 prompt만 던지지 않는다. 실제 워크플로에서는 다음 입력이 계속 쌓인다.

  • shell 명령 출력
  • test log와 stack trace
  • RAG 검색 결과
  • 여러 파일의 코드 조각
  • 이전 tool call 결과
  • planning trace와 user instruction
  • 다른 agent가 남긴 중간 산출물

문제는 단순히 token 비용이 아니다. 컨텍스트가 길어질수록 모델이 중요한 line을 놓치거나, 이전 tool output의 노이즈를 과하게 신뢰하거나, provider cache hit가 깨져 latency가 튀는 문제가 같이 온다.

그래서 나는 context engineering을 prompt 작성 기술로만 보는 관점이 꽤 부족하다고 본다. agent가 실제로 파일과 도구를 많이 읽는다면, context engineering은 prompt 안쪽이 아니라 LLM 호출 직전의 data plane에서 시작해야 한다.

Headroom이 잡는 위치

Headroom README 기준으로 이 프로젝트는 Python/TypeScript library, proxy, MCP server, agent wrapper 형태를 제공한다. 압축 대상도 일반 prompt만이 아니라 tool output, logs, RAG chunks, files, conversation history까지 포함한다고 설명한다.

대략 구조는 이렇게 볼 수 있다.

agent / app
  -> tool output, log, RAG chunk, file, message
  -> Headroom context layer
      -> content type routing
      -> compression
      -> cache alignment
      -> 원본 저장 / 필요 시 retrieve
  -> LLM provider

여기서 흥미로운 점은 "요약기 하나 붙이기"가 아니라는 점이다. README에는 ContentRouter, SmartCrusher, CodeCompressor, Kompress-base, CacheAligner, CCR 같은 구성요소가 언급된다. 이름만 보면 다음 역할 분리를 의도한 것으로 보인다.

  • ContentRouter: 입력이 JSON인지, 코드인지, 자연어 로그인지 구분한다.
  • SmartCrusher / CodeCompressor / Kompress 계열: 타입별로 다른 압축 전략을 쓴다.
  • CacheAligner: provider KV cache가 잘 맞도록 prefix 안정성을 관리한다.
  • CCR: 압축 전 원본을 저장하고 필요하면 다시 가져오게 한다.
  • MCP server: agent가 compress, retrieve, stats 같은 도구로 context layer를 호출하게 한다.

확인해야 할 부분은 많다. 예를 들어 "same answers"나 "60-95% fewer tokens" 같은 수치는 workload와 평가 방식에 따라 크게 달라질 수 있다. 하지만 설계 방향 자체는 실무적으로 맞다. agent 입력은 내용별로 압축 손실 허용도가 다르기 때문이다.

모든 텍스트를 같은 방식으로 줄이면 안 된다

context compression에서 제일 위험한 실수는 모든 입력을 자연어 요약으로 뭉개는 것이다.

예를 들어 JSON API 응답은 key, id, status, timestamp가 중요할 수 있다. 테스트 로그는 마지막 error line뿐 아니라 그 위의 fixture setup이 원인일 수 있다. 코드는 함수명과 import, 타입, call site가 중요하다. RAG chunk는 문장 하나보다 출처와 근거 범위가 중요하다.

따라서 압축기는 최소한 다음 정책을 나눠야 한다.

입력 타입줄여도 되는 것보존해야 하는 것
JSON/tool output반복 필드, verbose metadataid, status, error, schema boundary
code긴 구현부 일부symbol, signature, import, call graph hint
log반복 progress lineerror, warning, timestamp, command
RAG chunk중복 문장citation, section, 핵심 claim
conversation의례적 표현user constraint, decision, unresolved question

Headroom 같은 layer의 가치는 여기서 나온다. "압축률"만 높이는 도구는 오히려 agent를 망친다. 좋은 context layer는 무엇을 버렸는지 알고, 버린 원본을 다시 찾을 수 있어야 한다.

Reversible compression이 중요한 이유

agent 운영에서 압축은 손실을 만든다. 이걸 인정해야 한다. 중요한 건 손실이 없는 척하는 게 아니라, 손실을 복구할 경로를 설계하는 것이다.

Headroom README의 CCR은 원본을 캐시하고 필요 시 retrieve하게 하는 방향으로 설명된다. 이 패턴은 RAG와 비슷하지만 목적이 다르다.

  • RAG는 외부 지식을 검색해 context에 넣는다.
  • context compression layer는 이미 읽은 정보를 줄여 넣고, 필요하면 원본을 다시 펼친다.

실무적으로는 다음 흐름이 좋다.

1. tool output 전체를 저장한다.
2. LLM에는 압축본과 원본 handle을 준다.
3. 모델이 더 자세한 근거가 필요하면 retrieve(handle)를 호출한다.
4. 최종 답변에는 어떤 원본 handle을 근거로 썼는지 남긴다.

이렇게 하면 처음부터 모든 로그를 밀어 넣지 않아도 된다. 대신 중요한 결정을 내릴 때는 원본으로 돌아갈 수 있다. 특히 코드 수정, 배포, 데이터 삭제, 보안 판단처럼 side effect가 큰 작업에서는 이 복원 경로가 없으면 위험하다.

MCP server로 노출하는 게 자연스러운 이유

Headroom이 MCP server 형태를 제공한다는 점도 꽤 자연스럽다. context layer는 특정 agent framework 안에 박아 넣기보다, 여러 agent와 tool runtime이 공유하는 하위 계층으로 두는 편이 낫다.

MCP tool로 보면 인터페이스는 대략 이렇게 나눌 수 있다.

headroom_compress(input, policy?) -> compressed_text, handle, stats
headroom_retrieve(handle, range?) -> original_or_slice
headroom_stats(session?) -> token_saved, cache_hit, retrieve_count

이런 경계가 있으면 Claude Code, Codex, Cursor, LangGraph, 자체 agent가 같은 압축 정책을 공유할 수 있다. 더 중요한 건 관측 가능성이다. agent가 실패했을 때 "모델이 멍청했다"로 끝내지 않고 다음 질문을 할 수 있다.

  • 어떤 입력이 압축됐나?
  • 압축 후 error line이 사라졌나?
  • 원본 retrieve가 필요한데 호출하지 않았나?
  • cache alignment 때문에 prefix가 안정화됐나?
  • token 절감이 정확도 하락보다 컸나?

agent 품질 개선은 이런 로그가 있어야 가능하다.

RAG 앞단에도 쓸 수 있지만, 만능은 아니다

Headroom의 설명처럼 RAG chunk를 압축하는 것은 매력적이다. 실제로 enterprise RAG에서는 검색 결과가 10개만 넘어가도 같은 boilerplate, navigation text, footer, 중복 문단이 많이 들어온다.

다만 RAG 압축은 조심해야 한다. 압축기가 근거 문장을 바꾸거나 출처를 흐리면 답변의 grounding이 약해진다. 특히 법률, 의료, 보안, 재무 같은 영역에서는 "비슷한 의미"로 줄이는 게 충분하지 않다.

내가 권하는 정책은 이렇다.

  • citation URL, 문서 id, section id는 절대 압축하지 않는다.
  • 숫자, 날짜, 권한, 조건문은 원문 그대로 보존한다.
  • 요약된 chunk에는 원본 range handle을 붙인다.
  • 최종 답변 전 critical claim은 원본 retrieve로 재확인한다.
  • 압축률보다 answer preservation test를 먼저 본다.

즉 RAG compression의 목표는 예쁜 요약이 아니라 근거를 잃지 않는 token budget 관리다.

도입할 때 볼 체크리스트

Headroom 같은 context layer를 팀에 붙인다면 나는 다음 순서로 검증하겠다.

  1. 읽기 전용 워크로드부터 적용
    코드 검색, 로그 분석, 문서 QA처럼 side effect 없는 작업에서 먼저 본다.

  2. baseline trace를 저장
    압축 없이 agent가 성공하던 task와 실패하던 task를 분리해 둔다.

  3. 압축률보다 task success를 본다
    token이 80% 줄어도 bug 위치를 놓치면 실패다.

  4. 원본 retrieve 사용률을 본다
    모델이 중요한 순간에 원본을 다시 여는지 확인해야 한다.

  5. 타입별 policy를 나눈다
    JSON, code, log, RAG, conversation을 같은 prompt summarizer에 넣지 않는다.

  6. 보안 경계를 확인한다
    로컬 실행, 캐시 저장 위치, 민감정보 masking, retention policy를 봐야 한다.

  7. agent instruction에 retrieve 규칙을 넣는다
    "확신이 낮으면 원본을 다시 봐라"가 아니라, 어떤 조건에서 retrieve해야 하는지 명시한다.

내 의견

Headroom의 개별 알고리즘이나 수치 주장은 실제 benchmark를 더 봐야 한다. 특히 answer preservation을 어떤 task set에서 측정했는지, 압축 후 실패 사례가 무엇인지가 중요하다.

그래도 방향은 맞다. 앞으로 agent runtime에는 memory, tool broker, sandbox, eval harness와 함께 context compression layer가 기본 구성요소로 들어갈 가능성이 높다. 긴 context window는 필요하지만 충분조건이 아니다. 긴 창이 있어도 쓰레기를 많이 넣으면 모델은 여전히 흔들린다.

좋은 agent는 많은 정보를 무작정 읽는 agent가 아니라, 읽은 정보를 보존 가능한 형태로 줄이고 필요할 때 다시 펼칠 줄 아는 agent다. Headroom은 그 방향을 꽤 직접적으로 보여주는 사례다.

참고 자료

Comments