LLM에 'Junk' 토큰을 공급하지 마세요. (이를 해결하기 위해 Proxy를 만들었습니다)
Source: Dev.to – “Stop feeding junk tokens to your LLM – I built a proxy to fix it”
번역을 진행하려면 실제 기사 내용(텍스트 본문)을 제공해 주시겠어요?
본문을 알려 주시면, 원본 형식과 마크다운을 유지하면서 한국어로 번역해 드리겠습니다.
Overview
저는 최근에 로그를 가져오고, 데이터베이스를 쿼리하고, 코드를 검색하는 SRE 작업을 처리하는 에이전트를 만들었습니다. 작동은 했지만, 트레이스를 확인했을 때 짜증이 났습니다.
비용이 많이 들었기 때문만은 아니었습니다(청구서가 계속 올라가고 있었습니다). 비효율성 자체가 문제였습니다.
- 단일 도구 출력—Python 파일을 검색한 결과—가 40 000 토큰에 달했습니다.
- 약 35 000 토큰은
"type": "file"와"language": "python"이 2 000번 반복된 것이었습니다.
우리는 최신 모델에게 표준 JSON 보일러플레이트를 읽히기 위해 프리미엄 컴퓨팅 비용을 지불하고 있었습니다.
이 문제를 해결하면서도 에이전트를 깨뜨리지 않는 도구를 찾지 못해 직접 만들었습니다. Headroom이라는 이름의 이 도구는 애플리케이션과 LLM 사이에 위치해 컨텍스트를 약 85 % 압축하면서 의미를 잃지 않습니다.
오픈소스 – Apache‑2.0
코드: (link to repository)
실제 Headroom 코드베이스 URL이 있다면 “(link to repository)” 부분을 해당 URL로 교체해 주세요.
왜 잘라내기와 요약이 작동하지 않는가
컨텍스트 창이 가득 차면 업계 표준은 잘라내기(가장 오래된 메시지나 문서 중간을 잘라내는 것)입니다. 에이전트에게 잘라내기는 위험합니다:
- 로그 파일: 로그 중간을 잘라내면 충돌을 설명하는 단일 오류 라인이 사라질 수 있습니다.
- 파일 목록: 항목을 제거하면 사용자가 요청한 정확한 설정 파일이 숨겨질 수 있습니다.
저는 요약을 시도해 보았습니다(먼저 데이터를 요약하기 위해 저렴한 모델 사용). 하지만 그 과정에서 환상이 발생했습니다. 요약기는 원시 로그의 특정 오류 코드를 무시하고 배포가 “괜찮아 보인다”고 말했습니다.
우리가 필요한 것: 세 번째 옵션—무손실 압축, 혹은 최소한 “의도‑무손실”.
핵심 아이디어: 통계 분석, 무작위 절삭이 아니라
도구 출력의 **~90 %**가 단순히 스키마 골격이라는 것을 깨달았습니다. LLM은 status: active가 천 번 반복되는 것을 볼 필요가 없고, 이상치만 보면 됩니다.
Headroom의 SmartCrusher는 데이터를 다루기 전에 통계 분석을 수행합니다:
- 상수 팩터링 – 배열의 모든 항목에
"type": "file"이 있다면, 해당 상수를 한 번만 추출하고 반복하지 않습니다. - 이상치 탐지 – 숫자 필드의 표준 편차를 계산하고, 평균으로부터 > 2σ인 급증을 보존합니다. 이러한 급증이 보통 중요한 부분입니다.
- 오류 보존 – 강력 규칙: 스택 트레이스, 오류 메시지, 실패와 같은 문자열은 절대 삭제하지 않습니다. 오류는 신성합니다.
- 관련성 점수 – 사용자의 질의와 일치하는 항목을 하이브리드 BM25 + 시맨틱 임베딩 점수를 사용해 유지합니다.
- 첫/마지막 보존 – 항상 처음 몇 개와 마지막 몇 개 항목을 유지합니다; LLM은 몇 가지 예시를 기대하고, 최신성이 중요합니다.
결과:
40 000 토큰 → ~4 000 토큰. 동일한 정보 밀도, 환각 위험 없음.
CCR: 압축을 가역적으로 만들기
핵심 통찰: 압축은 가역적이어야 한다.
아키텍처 이름은 CCR (Compress‑Cache‑Retrieve) 입니다.
| 단계 | 설명 |
|---|---|
| 1. 압축 | SmartCrusher 가 도구 출력(예: 2 000개 항목 → 20개)을 압축합니다. |
| 2. 캐시 | 원본 2 000개 항목을 로컬에 캐시합니다(5분 TTL, LRU 교체). |
| 3. 검색 | Headroom 이 headroom_retrieve() 도구를 LLM 컨텍스트에 주입합니다. 요약을 본 뒤 모델이 더 많은 데이터가 필요하면 해당 도구를 호출합니다. Headroom 은 캐시에서 필요한 항목을 가져와 반환합니다. |
왜 중요한가
- 극단적인 압축(90 % 이상 감소)이 가능합니다. 왜냐하면 데이터가 완전히 사라지는 일은 없기 때문에 모델이 필요할 때 언제든 “압축을 풀어” 사용할 수 있기 때문입니다.
- 위험 계산이 바뀝니다: LLM 이 필요할 때 전체 데이터를 온‑디맨드로 요청할 수 있어 “정보 손실” 문제가 사라집니다.
예시 대화
Turn 1: "Search for all Python files"
→ 1 000 files returned, compressed to 15 items
Turn 5: "Actually, what was that file handling JWT tokens?"
→ LLM calls headroom_retrieve("jwt")
→ Returns jwt_handler.py from cached data
- 추가 API 호출이 없습니다.
- “죄송합니다, 더 이상 해당 정보를 가지고 있지 않습니다.” 같은 상황이 발생하지 않습니다.
TOIN: 네트워크 효과
Headroom는 TOIN(Tool Output Intelligence Network)을 통해 압축 패턴을 학습합니다. 압축 후에 일어나는 일을 익명으로 추적합니다:
- 가장 자주 검색되는 필드는 무엇인가요?
- 검색 비율이 높은 도구 유형은 무엇인가요?
- 어떤 쿼리 패턴이 검색을 유발하나요?
이 데이터는 압축 권장 사항에 다시 반영됩니다. 예를 들어, TOIN이 사용자가 압축 후 error_code 필드를 자주 검색한다는 것을 학습하면, SmartCrusher에 다음 번에는 error_code를 더 적극적으로 보존하도록 지시합니다.
내장된 프라이버시
| Aspect | Implementation |
|---|---|
| Data values | Never stored |
| Tool identifiers | Stored as structure hashes |
| Field names | Stored as SHA‑256[:8] hashes |
| User tracking | No user identifiers |
네트워크 효과
사용자가 많을수록 → 압축 이벤트가 많아질수록 → 모두에게 더 나은 권장 사항이 제공됩니다.
메모리: 대화 간 학습
에이전트는 종종 대화 사이에 사실을 기억해야 합니다 (예: “다크 모드를 선호합니다”, “내 시간대는 PST입니다”, “인증 리팩터 작업 중입니다”). Headroom은 이러한 사실을 자동으로 추출하고 저장하는 메모리 시스템을 제공합니다.
빠른 메모리 (추천)
- 추가 지연 없음 – LLM이 응답에
memory블록을 인라인으로 출력합니다. - Headroom이 블록을 파싱하고 향후 요청을 위해 메모리를 저장합니다.
from headroom.memory import with_fast_memory
client = with_fast_memory(OpenAI(), user_id="alice")
# Memories are extracted automatically from responses
# and injected into future requests
백그라운드 메모리
- 별도의 LLM 호출이 메모리를 비동기적으로 추출합니다.
- 더 정확하지만 약간의 지연이 추가됩니다.
from headroom import with_memory
client = with_memory(OpenAI(), user_id="alice")
두 접근 방식 모두 메모리를 로컬(SQLite)로 저장하고 이후 대화에 주입하여 모델이 외부 서비스 없이도 “기억”할 수 있게 합니다.
TL;DR
- Headroom는 통계 분석을 사용하여 도구 출력량을 약 85 % 압축하면서 이상치, 오류 및 관련성을 보존합니다.
- CCR (Compress‑Cache‑Retrieve)는 압축을 복원 가능하게 하여, LLM이 필요할 때 원시 데이터를 가져올 수 있게 합니다.
- TOIN은 집합적 사용으로부터 학습하여 향후 압축을 개선합니다.
- 내장된 memory는 에이전트가 대화 간 사실을 지연 없이(또는 최소한의 지연으로) 유지하도록 합니다.
한 번 사용해 보세요, 그리고 컨텍스트 윈도우가 여러분에게 for 유리하게 작동하도록 하세요, 방해가 되지 않게.
The Transform Pipeline
Headroom은 각 요청에 대해 네 가지 변환을 수행합니다:
1. CacheAligner
LLM 제공업체는 캐시‑토큰 가격을 제공합니다 (Anthropic: 90 % 할인, OpenAI: 50 % 할인). 캐시는 프롬프트 접두사가 안정적일 때만 작동합니다.
Problem – 시스템 프롬프트에 타임스탬프가 들어 있을 가능성이 높습니다. 예:
Current time: 2024-01-15 10:32:45
이렇게 되면 캐시가 깨집니다.
Solution – CacheAligner는 동적 콘텐츠를 추출해 끝부분으로 이동시켜 접두사를 안정화합니다. 동일한 정보는 유지되지만 캐시 적중률이 향상됩니다.
2. SmartCrusher
통계적 압축 엔진. 배열을 분석하고 패턴을 탐지하며 이상치를 보존하고 상수를 인수화합니다.
3. ContentRouter
콘텐츠 종류에 따라 압축 방식이 달라야 합니다. 코드는 JSON이 아니고, 로그도 아니며, 산문도 아닙니다.
ContentRouter는 머신러닝 기반 콘텐츠 감지를 이용해 데이터를 특화된 압축기로 라우팅합니다:
| Content Type | Compressor |
|---|---|
| Code | AST‑aware compression (tree‑sitter) |
| JSON | SmartCrusher |
| Logs | LogCompressor (clusters similar messages) |
| Text | Optional LLMLingua integration (≈20× compression, adds latency) |
4. RollingWindow
컨텍스트가 모델 한도를 초과하면 무언가를 제거해야 합니다. RollingWindow는 가장 오래된 tool calls + responses를 함께 삭제합니다 (데이터가 고아가 되지 않도록). 시스템 프롬프트와 최신 턴은 유지됩니다.
사용 방법 세 가지
옵션 1: 프록시 서버 (코드 변경 없음)
pip install headroom-ai
headroom proxy --port 8787
OpenAI 클라이언트를 http://localhost:8787/v1 로 지정하세요. 완료.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8787/v1")
# No other changes
Claude Code, Cursor, 그리고 모든 OpenAI 호환 클라이언트와 작동합니다.
옵션 2: SDK 래퍼
from headroom import HeadroomClient
from openai import OpenAI
client = HeadroomClient(OpenAI())
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
headroom_mode="optimize" # or "audit" or "simulate"
)
모드
| 모드 | 설명 |
|---|---|
| audit | 관찰만 합니다. 최적화될 내용을 로그에 기록하지만 실제로 변경하지 않습니다. |
| optimize | 압축을 적용합니다 – 토큰을 절약하는 방식입니다. |
| simulate | 드라이런입니다. API를 호출하지 않고 최적화된 메시지를 반환합니다. |
audit 모드로 시작해 잠재적인 절감 효과를 확인하고, 확신이 서면 optimize 로 전환하세요.
옵션 3: 프레임워크 통합
LangChain
from langchain_openai import ChatOpenAI
from headroom.integrations.langchain import HeadroomChatModel
base_model = ChatOpenAI(model="gpt-4o")
model = HeadroomChatModel(base_model, mode="optimize")
# Use in any chain or agent
chain = prompt | model | parser
Agno
from agno.agent import Agent
from headroom.integrations.agno import HeadroomAgnoModel
model = HeadroomAgnoModel(original_model, mode="optimize")
agent = Agent(model=model, tools=[...])
MCP (Model Context Protocol)
from headroom.integrations.mcp import compress_tool_result
# LLM에 반환하기 전에 모든 도구 결과를 압축합니다
compressed = compress_tool_result(tool_name, result_data)
실제 수치
| 작업량 | 이전 (토큰) | 이후 (토큰) | 절감율 |
|---|---|---|---|
| 로그 분석 | 22 000 | 3 300 | 85 % |
| 코드 검색 | 45 000 | 4 500 | 90 % |
| 데이터베이스 쿼리 | 18 000 | 2 700 | 85 % |
| 긴 대화 | 80 000 | 32 000 | 60 % |
앞으로 다가올 내용
더 많은 프레임워크
- CrewAI 통합
- AutoGen 통합
- Semantic Kernel 통합
관리형 스토리지
- 클라우드‑호스팅 TOIN 백엔드(옵트‑인)
- 기기‑간 메모리 동기화
- 팀‑공유 압축 패턴
향상된 압축
- 도메인‑특정 프로파일(SRE, 코딩, 데이터 분석)
- 맞춤형 압축기 플러그인
- 실시간 도구를 위한 스트리밍 압축
왜 이걸 만들었는가
저는 현재 AI 과대광고 주기의 **“최적화 단계”**에 있다고 믿습니다. 동작하게 만드는 것은 기본이고, 저렴하고 안정적으로 동작하게 만드는 것이 진정한 엔지니어링 작업입니다.
Headroom는 통계 분석과 가역 압축을 사용해 “컨텍스트 팽창” 문제를 해결합니다—휴리스틱이나 무분별한 잘라내기가 아니라. 완전히 로컬에서 실행되므로(일반적인 OpenAI/Anthropic 호출을 제외하고) 데이터가 여러분의 머신을 떠나지 않습니다.
- License: Apache‑2.0
- Repo: GitHub – headroom (실제 URL로 교체)
버그를 발견하거나 아이디어가 있으면 이슈를 열어 주세요. 저는 이 프로젝트를 적극적으로 유지보수하고 있습니다.