컨텍스트 압축

컨텍스트 압축 (Context Compression, Compresr)

Compresr는 답변에 필요한 LLM 컨텍스트를 질문에 대해 압축해요. 핵심 토큰만 남기고 나머지를 버려요. LiteLLM 가드레일로서 도구 출력, 검색 문서, 데이터베이스 결과, RAG 페이로드를 모델에 도달하기 전에 압축해, 동일한 답변을 입력 토큰의 일부로 얻을 수 있게 해요.

출처: 문서

본문

이것은 /v1/chat/completions, /v1/messages(Anthropic 형식), /v1/responses에서 사용할 수 있어요.

동작 방식 (How it works)

가드레일은 pre_call 단계에서 인프로세스로 실행되고 Compresr의 호스팅 API(https://api.compresr.ai)를 호출하므로 배포할 추가 서비스가 없어요. 프록시만 Compresr와 통신하고, 압축용으로 선택된 메시지 텍스트만 보내요. 클라이언트와 업스트림 LLM 제공자 모두 절대 연결하지 않아요. 요청 입력만 다시 쓰이고, 응답은 그대로 통과해요.

압축은 메시지별로, 쿼리 인식 방식으로 일어나요:

  1. 대상 선택 (Select targets). 기본적으로 최소 500자의 도구/함수 출력만 압축돼요. system 메시지, 이전 이력, 마지막 user 메시지는 옵트인(compress_system, compress_history, compress_last_user)하지 않는 한 그대로 통과해요.
  2. 대상별 쿼리 파생 (Derive a query per target). 도구 출력은 그것을 만든 도구 호출의 의도에 대해 압축돼요. LiteLLM은 tool_call_id로 어시스턴트 호출을 찾아 예를 들어 web_search: {"query": "2026 EV range"}로 렌더링해요. 다른 모든 대상은 마지막 user 메시지에 대해 압축돼요. 쿼리가 없는 대상은 압축되지 않은 채 남아요.
  3. 압축 (Compress). 대상은 {api_base}/api/compress/question-specific/(.../batch, 여러 개일 때)로 X-API-Key로 인증되어 보내져요.
  4. 재작성 (Rewrite). 반환된 compressed_context가 각 대상의 텍스트를 제자리에서 대체해요. 선택되지 않은 메시지는 바이트 동일로 유지되고, 이미지 같은 멀티모달 부분은 보존되며, 동일하거나 빈 결과는 요청을 변경 없이 진행시켜요.

압축은 기본적으로 Compresr의 가장 빠른 쿼리별 모델인 latte_v2에서 실행돼요. 추출적이에요. 쿼리에 중요하지 않은 스팬을 삭제하고 나머지는 그대로 유지해요.

동적 압축 (Dynamic compression)

dynamic: true가 기본이에요. 서비스가 각 페이로드가 얼마나 압축 가능한지 측정하고, dynamic_min_ratio(서버 기본 1.5x)와 dynamic_max_ratio(서버 기본 10.0x) 사이의 비율을 target_compression_ratio를 무시하고 선택해요. 밀도 높은 콘텐츠는 가볍게, 희소 콘텐츠는 공격적으로 압축돼요.

고정 비율을 고정하려면 dynamic: falsetarget_compression_ratio를 설정하세요. 0~1 사이 값은 해당 비율의 토큰을 제거하고(0.5는 대략 절반 제거), 1 이상의 값은 Nx 배수로 동작해요(4는 대략 1/4 유지). 지출 로그의 tokens_saved 통계를 사용해 워크로드가 견딜 수 있는지 판단하세요.

요구사항 (Requirements)

필요한 것은 compresr 가드레일을 포함한 LiteLLM 빌드와 compresr.ai의 API 키뿐이에요. 온프레미스 배포는 [email protected] 연락하세요.

빠른 시작 (Quick Start)

1. 콘피그에서 가드레일 정의

config.yaml:

model_list:
  - model_name: claude-sonnet-5
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
guardrails:
  - guardrail_name: compresr-compression
    litellm_params:
      guardrail: compresr
      mode: pre_call
      api_key: os.environ/COMPRESR_API_KEY
#     api_base: https://api.compresr.ai  [OPTIONAL, change only for on-prem]
#     model: latte_v2                    [OPTIONAL, this is the default]
#     unreachable_fallback: fail_open    [OPTIONAL, defaults to fail_closed]
#     default_on: true                   [OPTIONAL]

mode: pre_call을 사용하세요. 가드레일은 요청 입력만 변환하기 때문이에요. api_key는 필수이며 콘피그 또는 COMPRESR_API_KEY env var에서 올 수 있어요. default_on: true를 설정해 모든 요청을 압축하거나, 꺼두고(권장) 키 또는 요청별로 압축 옵트인을 유지하세요.

그 밖의 모든 것은 optional_params로 구성돼요. 전체 목록은 구성 참조에 있어요:

config.yaml:

guardrails:
  - guardrail_name: compresr-compression
    litellm_params:
      guardrail: compresr
      mode: pre_call
      api_key: os.environ/COMPRESR_API_KEY
      optional_params:
        compress_tool_outputs: true   # default; tool/function results
        compress_system: false        # default; opt in to compress system prompts
        min_chars_to_compress: 500    # default; shorter messages skipped
        dynamic: true                 # default; service picks ratio per payload

가드레일은 Admin UI의 Guardrails 아래에서 같은 필드로도 만들 수 있어요. 위 데모 동영상이 이 흐름을 안내해요.

2. LiteLLM 게이트웨이 시작

litellm --config config.yaml

3. 요청 보내기

OpenAI 형식:

curl -i http://0.0.0.0:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [
      {"role": "user", "content": "Which filing discusses Q3 revenue?"},
      {"role": "assistant", "tool_calls": [{"id": "call_1", "type": "function", "function": {"name": "search_filings", "arguments": "{\"query\": \"Q3 revenue\"}"}}]},
      {"role": "tool", "tool_call_id": "call_1", "content": "<...tens of thousands of tokens of filing text...>"}
    ],
    "guardrails": ["compresr-compression"]
  }'

Anthropic 형식:

curl -i http://0.0.0.0:4000/v1/messages \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Which filing discusses Q3 revenue?"}
    ],
    "litellm_metadata": {"guardrails": ["compresr-compression"]}
  }'

도구 출력은 페이로드가 업스트림으로 전달되기 전에 search_filings: {"query": "Q3 revenue"}에 대해 압축돼요. 그 밖의 것은 그대로 유지돼요.

키별 압축 활성화 (Enabling compression per key)

default_on이 설정되지 않으면 압축은 옵트인한 요청에만 실행돼요. 일반적인 패턴은 가드레일을 가상 키에 붙여, 그 키를 쓰는 사람이 클라이언트 변경 없이 압축을 받는 거예요.

curl -X POST 'http://0.0.0.0:4000/key/generate' \
  -H "Authorization: Bearer ***" \
  -H 'Content-Type: application/json' \
  -d '{
        "guardrails": ["compresr-compression"]
      }'

반환된 키로 만든 모든 요청이 compresr-compression을 거쳐요. 기존 키는 /key/update 같은 guardrails 필드로 처리하세요. 가상 키로 인증하면 원본이 키별로 저장되므로 검색도 활성화돼요. 가상 키 없이 만든 요청은 여전히 압축되지만 검색은 할 수 없어요.

요청별 압축 활성화 (Enabling compression per request)

클라이언트는 관리자 개입 없이 단일 호출에서 옵트인할 수 있어요.

OpenAI 형식:

curl -i http://0.0.0.0:4000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [...],
    "guardrails": ["compresr-compression"]
  }'

Anthropic 형식:

/v1/messages에는 최상위 guardrails 필드가 없으므로 litellm_metadata로 옵트인하세요:

curl -i http://0.0.0.0:4000/v1/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ***" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 1024,
    "messages": [...],
    "litellm_metadata": {"guardrails": ["compresr-compression"]}
  }'

응답에 x-litellm-applied-guardrails: compresr-compression 헤더가 포함되어 호출자가 압축이 실제 실행됐는지 확인할 수 있어요.

자동 라우터 뒤의 압축 (Compression behind an auto router)

자동 라우터가 서빙하는 요청은 분류용 한 번, 라우팅된 모델용 한 번의 두 호출을 만들어요. 기본적으로 둘 다 같은 압축 텍스트를 봐요. v1.101.0부터 라우터가 auto_router_routing_compressionauto_router_model_compression으로 홉별 압축 가드레일을 지정하거나 어느 쪽도 없게 할 수 있어요. 어느 필드를 설정하든 라우터가 자체 요청의 압축을 담당하고, 이 페이지가 키/팀/요청 수준에서 붙이는 가드레일을 그것들에 대해 억제해요. Compression 참고.

팀에 롤아웃하기 (Rolling out to a team)

플랫폼 관리자는 클라이언트 변경 없이 전체 팀에 압축을 켤 수 있어요. 가장 적합한 것은 검색 결과, 검색 문서, 티켓 스레드, CRM 레코드, 트랜스크립트 같은 비코드 도구 출력이 볼륨을 지배하는 에이전트 및 RAG 워크로드예요.

  • 관리자: config.yaml에 Compresr 등록. Quick Start처럼 compresr-compression 정의. 옵트인 키만 압축을 받도록 default_on을 꺼두기.
  • 관리자: 개발자별 키에 Compresr 붙여 발급.
    curl -X POST 'http://0.0.0.0:4000/key/generate' \
      -H "Authorization: Bearer ***" \
      -H 'Content-Type: application/json' \
      -d '{
            "key_alias": "support-agent-alice",
            "guardrails": ["compresr-compression"],
            "models": ["claude-sonnet-5"],
            "metadata": {"team": "compression-rollout"}
          }'
    
  • 개발자: 클라이언트를 프록시로 지정. base URL과 키만 바뀌므로 모든 OpenAI·Anthropic 호환 클라이언트가 동작해요.
    from openai import OpenAI
    client = OpenAI(
        base_url="https://your-litellm-proxy.example.com/v1",
        api_key="«redacted:sk-…»",
    )
    

이제부터 그 키로 만든 모든 요청은 전달 전에 도구 결과가 압축돼요. 단일 요청의 압축을 건너뛰려면(예: 압축되지 않은 기준선 비교) optional_params에서 allow_bypass_header: true를 설정하고 그 호출에 x-compresr-bypass: true를 보내세요. 관리자가 활성화하지 않으면 헤더는 무시돼요. 어떤 호출자든 헤더를 설정할 수 있기 때문이에요.

압축 콘텐츠 검색 (compresr_retrieve) (Retrieving compressed content)

압축된 모든 메시지의 원본 텍스트는 요청 기간 동안 사용 가능해요. enable_retrieval(기본)이 켜져 있으면 모든 압축은:

  • SHA-256의 처음 24개 hex 문자를 키로 원본 텍스트를 메모리에 저장
  • 압축 텍스트에 마커 추가: [compresr hash=<hash>: parts of this content were compressed away. If you need the full original, call the compresr_retrieve tool with this hash.]
  • 요청의 기존 도구 옆에 필수 hash 파라미터 하나를 가진 compresr_retrieve 함수 도구 주입

모델이 compresr_retrieve를 호출하면 LiteLLM이 호출을 가로채 원본을 복원하고, 올바르게 짝지어진 도구 왕복을 추가하며 요청을 다시 발행해, 클라이언트는 하나의 정상 completion을 봐요. 이것은 세 표면 모두에서 형식이 올바르다. Anthropic은 tool_use/tool_result 블록, Responses API는 function_call/function_call_output 항목, chat completions는 role: tool 메시지. 모델이 한 턴에 compresr_retrieve와 실제 도구 호출을 섞으면 검색 호출만 서버 측에서 해결되고, 모델은 후속에서 실제 호출을 재계획하므로 도구 호출과 결과가 짝을 유지해요.

검색 루프는 제한돼 있어요:

  • 테넌트 격리 (Tenant isolation). 원본 컨텍스트는 요청을 만든 가상 키 아래 저장되므로, hash는 발급된 키에 대해서만 해결되고 다른 키는 아무것도 못 얻어요. 가상 키가 없는 요청은 스토어를 범위 지정할 신원이 없으므로 검색이 비활성화되고 프로세스당 한 번 경고가 기록돼요.
  • 증폭 (Amplification). 단일 턴은 최대 8개 hash를 해결하고 각 hash는 한 번만 해요. 조작된 hash는 아무것도 해결하지 못하고 후속 요청도 트리거하지 않아요.
  • 메모리 (Memory). 원본 컨텍스트는 15분 동안 메모리에 유지돼요. 단일 호출은 최대 10 MiB(max_bytes_per_call)를 저장하고, 전체 프로세스는 최대 256개 호출과 256 MiB를 보유하며 가장 오래된 것부터 추출해요. 저장하기 너무 큰 컨텍스트는 절대 마커를 받지 않아요. 만료되거나 추출된 컨텍스트는 그냥 해결에 실패해요.

스토어는 워커 프로세스에 있으므로, 멀티 워커 배포에서는 후속 요청이 압축한 워커와 다른 워커에 떨어질 수 있어요. --workers 1로 실행하거나 enable_retrieval: false를 설정하세요.

tokens_saved가 0일 수 있는 이유 (Why tokens_saved can be 0)

  • 기본적으로 도구/함수 메시지만 압축돼요. min_chars_to_compress자(500) 이상의 도구 출력이 없는 요청은 가드레일 통계 없이 그대로 통과해요.
  • 쿼리를 파생할 수 없는 대상은 압축되지 않은 채 남아요.
  • 입력과 동일한 압축 텍스트는 무동작이에요.
  • Responses API에서 압축 콘텐츠는 매핑이 모호하지 않을 때만 미러링돼요. 모호한 경우 페이로드 손상을 감수하는 대신 그대로 둬요.
  • allow_bypass_header가 활성화되면 우회 헤더가 압축을 건너뛰어요.

실패 의미론 (Failure semantics)

기본은 fail_closed예요. Compresr 서비스에 도달할 수 없거나, 타임아웃(60초 예산)되거나, 잘못된 상태·본문을 반환하면 요청이 502와 일반 오류 메시지로 실패해요. 업스트림 응답 본문은 클라이언트가 아닌 서버 로그로 가요.

unreachable_fallback: fail_open을 설정해 요청을 압축 없이 전달하게 하고, 프록시 로그에 경고를 기록할 수 있어요. 정확한 문자열 fail_open이 아닌 값은 모두 fail_closed로 취급돼요.

보안 참고 (Security notes)

통합은 여러 감사 패스를 거쳤어요. 다음 각각은 코드로 강제되고 회귀 테스트로 커버돼요:

  • SSRF 검증 api_base. http/https 체계만 허용되고, 클라우드 메타데이터 엔드포인트(예: 169.254.169.254)는 십진, hex, IPv4 매핑 인코딩을 포함해 초기화에서 거부돼요.
  • 검색 스토어의 교차 테넌트 격리. 조작된 user_api_key 문자열은 명시적으로 신뢰하지 않아요.
  • 인젝션 안전 패스스루. compression_params는 그대로 전달되지만 예약 키 context, query, inputs는 경고와 함께 제거되고, 명명된 구성 필드가 충돌 시 이겨요.
  • 로그 인젝션 방어. 모델 공급 hash는 비인쇄 문자를 제거하고 로그에 반영되기 전에 잘라요.
  • 오류 교정. 업스트림 오류 본문은 서버 로그에 남고, 클라이언트는 일반 502를 받아요.

Compresr 실행 검증 (Validate Compresr ran)

  • x-litellm-applied-guardrails: compresr-compression 응답 헤더.
  • 지출 로그 행의 guardrail_information: messages_compressed, tokens_before, tokens_after, tokens_saved, compression_model. 이들은 최소 하나의 메시지가 실제로 압축됐을 때만 기록돼요.
  • Admin UI: Logs에서 아무 요청이나 열고 Guardrails & Policy Compliance로 스크롤하면 compresr-compression이 Request Lifecycle에서 지연시간과 함께 pre-call 단계로, Evaluation Details 아래에 항목으로 나타나요.

무엇을 기대할까 (What to expect)

일반 워크로드는 품질 손실 없이 2x~10x 압축돼요. 희소 콘텐츠가 가장 많이 압축되며, 종종 그 범위를 훨씬 넘어요. 트랜스크립트, 리포트, 서류, 티켓 스레드, 로그, 검색 결과는 대부분 주어진 질문에 무관한 토큰이에요. 구조화 데이터(JSON, XML, HTML)도 잘 압축돼요. 코드는 현재 압축되지 않아요.

구성 참조 (Configuration reference)

최상위 litellm_params:

파라미터 타입 설명
guardrail str 반드시 compresr.
mode str pre_call 사용. 가드레일은 요청 입력만 변환, 응답은 그대로 통과
api_key str Compresr API 키, X-API-Key로 전송. COMPRESR_API_KEY로 폴백. 필수, 없으면 init 실패
api_base str Compresr API base URL. COMPRESR_API_BASE, 그다음 https://api.compresr.ai로 폴백. 온프레미스 배포에서만 변경
model str 압축 모델(LLM 아님). 기본값 latte_v2
unreachable_fallback str fail_closed(기본)는 서비스 실패 시 502 반환, fail_open은 요청을 압축 없이 전달
default_on bool 호출별 옵트인 없이 모든 요청에 실행. 기본 false

중첩 optional_params (각각 litellm_params 아래 직접도 허용, 중첩 값이 이김):

파라미터 타입 기본값 설명
target_compression_ratio float 0.5 0~1 사이: 제거할 토큰 비율. 1 초과: Nx 압축 배수. dynamic이 켜져 있는 동안 무시
coarse bool true 문단 수준 점수화 (빠름). 토큰 수준에는 false
min_chars_to_compress int 500 이보다 짧은 메시지는 건너뜀
compress_tool_outputs bool true 도구/함수 결과 압축
compress_system bool false system 메시지 압축
compress_history bool false 마지막 user 메시지 이전의 user 메시지 압축
compress_last_user bool false 마지막 user 메시지 압축, 자체 텍스트를 쿼리로 사용
enable_retrieval bool true 원본 저장, 마커 추가, compresr_retrieve 도구 주입
max_bytes_per_call int 10485760 저장 원본의 호출별 캡 (10 MiB). 0은 캡 비활성화, 음수는 거부
allow_bypass_header bool false x-compresr-bypass: true 존중. 호출자가 헤더를 제어하므로 기본 꺼짐
dynamic bool true 서비스가 페이로드별 비율 선택 (latte_v2)
dynamic_min_ratio float 미설정 동적 모드 하한, 서버 기본 1.5
dynamic_max_ratio float 미설정 동적 모드 상한, 서버 기본 10.0
compression_params dict 미설정 압축 API에 그대로 전달되는 추가 필드. context, query, inputs는 예약되어 제거

환경 변수:

변수 설명
COMPRESR_API_KEY 가드레일 콘피그에 없을 때 api_key의 폴백
COMPRESR_API_BASE 가드레일 콘피그에 없을 때 api_base의 폴백

Compresr 소개 (About Compresr)

Compresr는 Microsoft, Bell Labs, UBS에서의 경력을 가진 EPFL 연구원 4명이 만든 YC backed 회사(W26)예요. compresr.ai, YC 페이지, LinkedIn에서 찾을 수 있어요.