내 비용을 증가시킨 토큰 압축기 — 그리고 그것이 그래야만 했던 증거
요약
AI 코딩 에이전트의 컨텍스트를 압축하는 Paritok 모델 사용 시, 통계 수치와 실제 API 비용 간의 괴리가 발생하는 원인을 분석합니다. 프록시가 원본 데이터를 복구하기 위해 추가로 전송하는 트래픽이 비용 상승의 주범임을 밝힙니다.
핵심 포인트
- Paritok은 비파괴적 압축 방식을 사용하여 필요 시 원본을 복구함
- 압축 통계(/stats)는 프록시가 생성하는 추가 트래픽을 반영하지 않음
- 에이전트가 원본을 재전송할 때마다 압축-확장 루프가 반복되어 비용 누적
- 도구 호출을 통한 컨텍스트 확장 과정에서 발생하는 숨겨진 비용 주의 필요
나는 오픈 소스 도구의 작은 개선 사항을 찾으러 떠났습니다. 나는 잘못된 방향을 가리키는 숫자를 발견했고, 그 후 왜 그래야만 했는지 알아냈습니다.
라이브 데모 — 직접 파일을 붙여넣고 확인해 보세요:
https://pin-on-expand.onrender.com
설정 (The setup)
Paritok은 AI 코딩 에이전트의 컨텍스트 (context)를 압축하는 4B 모델입니다. 이 모델은 에이전트와 Anthropic 또는 OpenAI 사이에서 작동하며, 파일 읽기 및 도구 출력 (tool output)을 압축하고 절약된 양을 알려줍니다.
진정으로 훌륭한 작업입니다. 45,000개의 실제 에이전트 궤적 (trajectories)으로 학습되었기 때문에, 디버그 라인보다 함수 시그니처 (function signature)가 더 중요하다는 것을 알고 있습니다. Apache 2.0 라이선스이며, 소비자용 GPU에서 실행됩니다. 그들의 벤치마크 (benchmark) 수치는 신뢰할 수 있습니다.
나는 그 위에 정책 개선 (policy improvement)을 구축하고 싶었습니다. 나의 개선 사항이 도움이 되었다는 것을 증명하기 위해, 먼저 Paritok이 소모하는 비용을 측정해야 했습니다.
그 측정이 이야기의 전부입니다.
서로 일치하지 않는 두 숫자
한 번의 코딩 에이전트 세션. 컨텍스트에 포함된 20,005 토큰의 파일 하나.
Paritok 자체의 /stats 엔드포인트: 입력 토큰의 64.0% 절약.
제공업체(provider)에 실제로 POST된 내용: 압축을 전혀 하지 않고 파일을 보냈을 때보다 69.2% 더 많음.
동일한 세션. 동일한 파일. 두 숫자 모두 정확합니다.
사라진 토큰들은 어디로 갔는가
Paritok은 설계상 비파괴적 (non-destructive)이며, 이것이 좋은 점입니다. 압축된 콘텐츠는 [REF:id]로 태그가 지정되며, 모델이 정확한 원본이 필요할 때 주입된 expand_context 도구를 호출하여 다시 가져옵니다. 전송 시에는 손실적 (lossy)이지만, 중요할 때는 복구 가능합니다.
프록시 (proxy)가 그 호출에 직접 응답합니다. 프록시는 전체 원본을 프록시 로컬 스레드 (proxy-local thread)에 추가하고, 해당 스레드를 상위 서버로 두 번째 POST 합니다.
그리고 stats는 process_request에서 한 번 계산됩니다 — 그 루프가 실행되기 전에 말입니다.
post 0: 6,919 tokens 압축된 요청 ← /stats에 의해 계산됨
post 1: 26,924 tokens 전체 원본을 포함함 ← 계산되지 않음
─────────
...
그다음에는 문제가 누적됩니다. 프록시(Proxy)가 가상 교환(virtual exchange)을 클라이언트로부터 숨기기 때문에, 여러분의 에이전트(Agent)는 이를 전혀 볼 수 없습니다. 다음 턴에 에이전트가 원본 파일을 다시 보내면, Paritok은 이를 동일한 참조값으로 다시 압축(re-compress)하고, 모델은 이를 다시 확장(expand)합니다.
매 턴마다. 영원히.
공정하게 말하자면, Paritok의 README에는 /stats가 "Paritok이 실제로 개입하는 범위 내로 제한된다"라고 명시되어 있습니다. 숨겨진 것은 없습니다. 하지만 제외된 트래픽은 _게이트웨이(Gateway) 자체가 생성한 것_입니다. 이는 여러분이 카운트될 것이라고 가장 기대할 만한 바로 그 카테고리입니다.
예상하지 못했던 부분
저는 제 테스트 환경(harness)을 검증하기 위해 비용 모델(cost model)을 작성했습니다. 이 모델은 "버그가 있다"라는 말보다 더 강력한 사실을 말해줍니다.
에이전트가 운반하는 파일의 토큰 수를 $T$라고 하고, 압축률(compression ratio)을 $c$라고 합시다 — 원본 대비 압축된 비율로, $0 < c < 1$이며, 작을수록 좋습니다. 호스팅된 4B 모델에서 측정된 값은 $c ≈ 0.36$입니다.
모델이 확장(expand)하지 않는 턴의 비용은 압축이 약속한 비용과 정확히 일치합니다:
$C_{quiet} = cT$
모델이 확장(expand)하는 턴의 비용은 압축된 요청 비용에, 동일한 컨텍스트와 복원된 원본을 모두 담은 두 번째 POST 요청 비용을 더한 것입니다:
$C_{expand} = (cT){post 0} + (cT + T){post 1} = T(1 + 2c)$
이제 이를 단순히 파일을 보낼 때의 비용인 $T$와 비교해 보겠습니다:
$\frac{C_{expand}}{T} = 1 + 2c > 1$ (모든 $c > 0$에 대해)
모델이 확장하는 모든 턴에서, 압축기가 아무리 뛰어나더라도 압축은 승리할 수 없습니다.
잠시 이 내용을 곱씹어 보십시오. 컨텍스트를 단 하나의 토큰으로 압축하는 완벽한 압축기라 할지라도, 확장(expanding)이 일어나는 턴에서는 여전히 손해를 보게 됩니다. 확장이 일어날 때 어차피 원본을 다시 보내게 되며, 압축된 복사본은 순수한 오버헤드(overhead)로서 따라붙기 때문입니다.
이 손실은 구조적입니다. 이는 모델의 가중치(weights)가 아니라 게이트웨이의 제어 흐름(control flow)에 존재합니다. 아무리 뛰어난 압축 기술도 이를 해결할 수 없습니다.
그리고 여기에 깔끔하면서도 끔찍한 부분이 있습니다. 사용자가 보게 되는 두 수치 모두 동일한 $c$에서 도출된다는 점입니다.
$\text{보고된 절감액 (reported saving)} = 1 - c = 64%$
$\text{실제 초과 지출 (actual overspend)} = 2c = 72%$
측정 결과: 보고된 절감액 64.0%, 실제 초과 지출 69.2%. 이 격차는 모델이 무시하는 메시지 구조 오버헤드(message-structure overhead)입니다.
하나의 변수로부터 계산되었지만, 서로 반대 방향을 가리키는 한 세션에 대한 두 가지 진실된 진술.
해결책은 하나의 아이디어입니다
모델이 expand_context를 호출할 때, 그것은 해당 콘텐츠를 압축한 것이 실수였다고 당신에게 말하고 있는 것입니다.
추측하는 것이 아닙니다. 도구 호출 (tool call)을 통해 정확한 참조 ID (reference id)와 함께 이를 명시하는 것입니다. 이는 시스템 자체에 의해 모든 실패 상황에서 생성되는, 무료로 얻을 수 있는 지도 신호 (supervision signal)입니다.
그 무엇도 이를 소모하지 않습니다.
그러므로 그 신호를 존중하십시오: 해당 콘텐츠를 고정(pin)하고, 그 이후부터는 있는 그대로(verbatim) 전달하십시오. 참조(reference)는 다시 나타나지 않으므로 확장할 것이 남지 않으며, 해당 턴(turn)의 비용은 두 번의 POST가 아닌 한 번의 POST로 줄어듭니다.
고정(Pin)은 콘텐츠 해시 (content hash)와 소스 경로 (source path)를 키(key)로 사용하므로, 다른 오프셋 (offset)에서 동일한 파일을 다시 읽더라도 여전히 작동합니다. 아무도 확장하지 않는 콘텐츠는 이전과 정확히 동일하게 압축됩니다. 이는 모델이 이미 압축에 실패했음을 증명한 곳에서만 압축 범위를 좁힙니다.
모델이 매번 정확한 소스가 필요한 n번의 턴 동안:
Cstock(n)=nT(1+2c)
Cpin(n)=T(1+2c)learn\nthe\npin+(n−1)T\nverbatim\nafter=T(n+2c)
\frac{Cpin(n)}{Cstock(n)}=1-\frac{n+2c}{n(1+2c)}\xrightarrow{n\to\infty}\approx 1+\frac{2c}{1+2c}\approx 41.9%
n=3\text{ 일 때, 모델의 예측값은 } \mathbf{27.9%}\text{ 입니다.}
Paritok의 호스팅 GPU를 사용한 두 개의 실제 프록시 프로세스에서 측정한 결과: \mathbf{29.1%}\text{ 입니다.
| 3 turns, hosted GPU | POSTs | Billed | vs no proxy |
|---|---|---|---|
| stock Paritok | 6 | 104,229 | −73.7% |
| pin-on-expand | 4 | 73,901 | −23.1% |
그리고 대조군(control) — 모델이 절대 확장하지 않는 경우 — 은 각각 20,787 토큰으로 막상막하입니다. 고정(Pinning)은 호출되지 않을 때는 비용이 전혀 들지 않습니다.
산술적 계산과 프록시 측정은 독립적으로 수행되었으며, 약 1% 포인트 차이로 일치합니다. 이 부분이 제가 가장 만족스러운 지점입니다.
해결하지 못하는 것
고정(Pinning)이 해당 세션을 수익성 있게 만들어주는 것은 아니며, 그런 척할 생각도 없습니다. 그것은 73.7%의 초과 지출을 23.1%로 옮길 뿐입니다. 잔여분은 공식이 예측하는 것과 정확히 일치합니다:
\frac{Cpin(n)}{Cstock(n)}-1=\frac{2c}{n}\xrightarrow{n\to\infty}0
핀(pin)은 첫 번째 확장(expansion)으로부터 _학습(learned)_되므로, 첫 번째 턴은 항상 전액 지불됩니다. 핀 고정(Pinning)은 위에서부터 손익분기점으로 수렴합니다. 모델이 파일을 있는 그대로 필요로 하는 세션에서는 파일을 있는 그대로 보내는 것보다 결코 더 나은 결과를 내지 못합니다.
그것이 정직한 한계치이며, 정직한 목표는 존재하지 않는 승리를 주장하기보다 심각한 손실을 멈추는 것입니다.
또한 저는 프롬프트 캐싱(prompt caching)을 모델링하지 않았으며, 이는 절대적인 초과 지출 수치를 낮추겠지만 — 확장된 원본은 새로운 입력이며 캐시 히트(cache hit)가 될 수 없으므로 — 그 방향성(direction)을 바꾸지는 못할 것입니다.
모든 심각한 버그는 나의 측정 방식에 있었다
말할 가치가 있습니다. 왜냐하면 이 과정이 거의 잘못된 방향으로 갈 뻔했기 때문입니다.
저의 첫 번째 테스트 고정 장치(fixture)에는 거의 동일한 26개의 함수가 있었는데, Paritok의 deduplicate_definitions가 이를 거의 아무것도 남지 않을 정도로 압축해 버렸습니다. 이는 영광스럽고도 가짜인 95%의 압축률을 만들어냈습니다. 저의 SEG-파싱 정규 표현식(regex)은 실제 세그먼트(segment) 대신 프롬프트의 _지시 텍스트(instruction text)_에 있는 리터럴 [SEG]에 매칭되었고, 그 결과 모든 세그먼트가 ...로 압축되었습니다. 저는 Paritok 자체의 wrapper.py를 고정 장치로 사용하면서, 그 안에 expand_context와 [REF:라는 문자열이 포함되어 있어 고정 장치 자체의 텍스트에 대해 저의 탐지 로직이 작동한다는 사실을 알아차리지 못했습니다. 그리고 프록시(proxy)의 인메모리 캐시(in-memory cache)가 수정 사항보다 더 오래 유지되어, 제대로 작동하는 수정 사항이 마치 고장 난 것처럼 보이게 만들었습니다.
이 네 가지 중 세 가지는 자신 있게 틀린 헤드라인을 배포했을 것입니다.
더 나쁜 것은, 저의 첫 번째 재현(reproduction)이 잘못된 것에 대해 맞았다는 점입니다. 저는 확장 후 재압축(expand-then-re-collapse) 루프를 확인했지만, 그것이 SDK 모드에만 적용된다는 것을 깨달았습니다. 프록시 모드에서의 실패는 다릅니다. 모델을 버리고 소스 코드로부터 다시 유도(re-derive)했습니다.
제가 맞닥뜨린 모든 심각한 버그는 Paritok이 아니라 저의 측정 방식에 있었습니다.
이것이 제가 실제로 이번 일을 통해 얻은 교훈입니다: 타인의 수치에 대해 주장할 때, 당신이 거짓말을 하고 있지 않음을 증명하는 인프라(infrastructure)가 바로 결과물(deliverable)이다. 수정 사항은 약 120줄 정도입니다. 그것을 게시할 권리를 부여하는 테스트 하네스(harness)가 저장소의 대부분을 차지합니다.
직접 시도해 보세요
https://pin-on-expand.onrender.com
어떤 소스 파일이든 붙여넣어 보세요. 두 개의 실제 paritok proxy 프로세스(하나는 기본형, 하나는 패치된 버전)를 콜드 스타트(cold-start)하여, 각각에 대해 실제 멀티턴 세션(multi-turn session)을 구동하고 그 대조 결과를 보여줍니다. 즉, 모든 업스트림 POST 요청 중 어떤 것이 /stats에 집계되었는지, 그리고 실제 청구 금액은 얼마였는지를 보여줍니다.
해당 페이지의 그 어떤 것도 하드코딩(hard-coded)되어 있지 않습니다. 매 실행마다 프록시를 콜드 스타트하는데, 이는 압축 캐시(compression cache), 섀도 스토어(shadow store), 핀 세트(pin set)가 모두 프로세스의 수명 동안 유지되기 때문입니다. 만약 프록시가 웜(warm) 상태라면 이전 실행의 답변을 조용히 반환할 것입니다.
코드, 하네스(harness), 모든 측정 지표:
https://github.com/GauravGupte20/paritok-pin-on-expand — Apache 2.0, Paritok과 동일합니다.
다음 단계
Paritok의 level 파라미터 — L0부터 L3까지, ≤0.50에서 ≤0.20까지의 실제 목표 비율(target ratios)에 매핑됨 — 는 완전히 구현되어 모델에 연결되었습니다.
값 사이를 선택하는 로직은 아직 없습니다. 도구(tool) 결과는 항상 기본값인 L1을 사용합니다. 히스토리(history)는 L3로 하드코딩되어 있습니다.
Pin-on-expand는 등급이 매겨져야 할 정책의 이진 사례(binary case)입니다. 즉, _압축할 것인가 말 것인가_가 아니라, _이 세그먼트의 연령, 종류, 그리고 사용자가 실제로 질문한 내용과의 관련성을 고려할 때 얼마나 강하게 압축할 것인가_의 문제입니다. 그리고 bge-small은 이미 도구 탐색(tool discovery)을 위한 의존성(dependency)으로 사용되고 있으므로, 의도 유사도 점수(intent-similarity scoring)를 위해 새로운 패키지를 추가할 필요는 없습니다.
Paritok은 다이얼(dial)을 만들었습니다. 다만 아직 컨트롤러(controller)가 없을 뿐입니다.
그것이 제가 원래 만들고자 했던 것이며, 여전히 만들 의도가 있습니다.
Paritok의 Token-Efficiency Hackathon을 위해 Paritok으로 제작되었습니다. #BuiltWithParitok
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기