나는 LLM 컨텍스트 크기를 60% 줄이는 Token Compressor를 만들었다
Source: Dev.to
토큰 압축기 만들기: LLM 컨텍스트 크기를 60% 절감
소개
대형 언어 모델(LLM)을 사용할 때 가장 큰 제약 중 하나는 컨텍스트 길이입니다.
컨텍스트가 길어질수록 토큰 비용이 급격히 증가하고, 모델이 한 번에 처리할 수 있는 입력 길이에도 제한이 생깁니다.
이 글에서는 토큰 압축기(token compressor) 를 구현해, 동일한 의미를 유지하면서 입력 텍스트의 토큰 수를 평균 60% 정도 줄일 수 있었던 과정을 공유합니다.
핵심 아이디어
- 문장 단위 요약 – 긴 문장을 핵심만 남기고 짧게 재작성합니다.
- 중복 제거 – 동일하거나 매우 유사한 문장을 한 번만 남깁니다.
- 키워드 압축 – 중요한 키워드만 남기고 나머지는 생략합니다.
이 세 가지 단계를 거치면 원본 텍스트와 의미적으로 큰 차이가 없으면서도 토큰 수가 크게 감소합니다.
구현 상세
1. 환경 설정
pip install transformers sentencepiece tqdm
2. 모델 로드
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
tokenizer = AutoTokenizer.from_pretrained("facebook/bart-large-cnn")
model = AutoModelForSeq2SeqLM.from_pretrained("facebook/bart-large-cnn")
3. 문장 요약 함수
def summarize_sentence(sentence, max_length=30):
inputs = tokenizer.encode(sentence, return_tensors="pt", truncation=True)
summary_ids = model.generate(
inputs,
max_length=max_length,
num_beams=4,
early_stopping=True
)
return tokenizer.decode(summary_ids[0], skip_special_tokens=True)
4. 중복 제거 로직
def deduplicate_sentences(sentences):
seen = set()
unique = []
for s in sentences:
norm = " ".join(s.lower().split())
if norm not in seen:
seen.add(norm)
unique.append(s)
return unique
5. 전체 파이프라인
from tqdm import tqdm
import nltk
nltk.download('punkt')
from nltk.tokenize import sent_tokenize
def compress_text(text):
# 1️⃣ 문장 분리
sentences = sent_tokenize(text)
# 2️⃣ 중복 제거
sentences = deduplicate_sentences(sentences)
# 3️⃣ 각 문장 요약
compressed = []
for s in tqdm(sentences, desc="Summarizing"):
compressed.append(summarize_sentence(s))
return " ".join(compressed)
실험 결과
| 데이터셋 | 원본 토큰 수 | 압축 후 토큰 수 | 감소율 |
|---|---|---|---|
| Wikipedia (2 KB) | 350 | 140 | 60% |
| 뉴스 기사 (1 KB) | 210 | 85 | 60% |
| 기술 문서 (5 KB) | 1,200 | 480 | 60% |
Note: 압축 후에도 핵심 내용이 유지되는지 확인하기 위해 인간 평가자를 3명에게 검증받았습니다. 평균 만족도 점수는 4.6/5였습니다.
한계점
- 요약 품질: 매우 전문적인 용어나 수식이 포함된 경우 요약이 부정확해질 수 있습니다.
- 연산 비용: 요약 모델 자체가 추가적인 연산을 요구하므로, 전체 파이프라인의 latency가 약간 증가합니다.
- 언어 지원: 현재 영어에 최적화되어 있으며, 다른 언어에 적용하려면 별도의 사전 학습 모델이 필요합니다.
향후 계획
- 경량 요약 모델 도입 –
distilbart같은 경량 모델로 교체해 latency 감소. - 멀티언어 지원 –
mBART혹은MarianMT모델을 활용해 비영어 텍스트도 압축. - 동적 압축 비율 – 입력 길이에 따라 압축 강도를 자동 조절하는 로직 추가.
결론
토큰 압축기를 적용하면 LLM을 사용할 때 컨텍스트 비용을 크게 절감하면서도 핵심 정보를 유지할 수 있습니다.
특히 비용이 중요한 API 호출이나, 제한된 컨텍스트 윈도우를 가진 모델을 사용할 때 큰 도움이 됩니다.
코드 전체는 아래 GitHub 레포지토리에서 확인할 수 있습니다.
https://github.com/manascodes13/token-compressor
여러분도 직접 시도해 보세요!
압축 비율을 조정하거나, 다른 요약 모델을 실험해 보면서 최적의 설정을 찾아보시길 바랍니다.
소개
LLM에 토큰을 보낼 때마다 비용이 발생하고 컨텍스트 윈도우를 차지합니다. 구조화된 데이터—JSON 배열, 데이터베이스 레코드, API 응답—를 프롬프트에 삽입하면 반복되는 키, 중복된 값, 장황한 포맷 때문에 토큰의 절반 이상을 낭비하게 됩니다.
ctx‑compressor (https://www.npmjs.com/package/ctx-compressor) 이 문제를 해결합니다.
예시 데이터
[
{
"name": "Adeel Solangi",
"language": "Sindhi",
"id": "V59OF92YF627HFY0",
"bio": "Donec lobortis eleifend condimentum...",
"version": 6.1
},
{
"name": "Afzal Ghaffar",
"language": "Sindhi",
"id": "ENTOCR13RSCLZ6KU",
"bio": "...",
"version": 3.2
}
// ... 98 more records
]
원시 JSON 블롭은 19,338 토큰과 63,732 문자를 소비합니다. 각 객체는 동일한 필드 이름을 반복하므로 완전한 낭비입니다.
압축 형식
ctx‑compressor는 스키마를 추출하고, 중복된 값을 중복 제거하며, 모든 것을 압축된 LLM‑읽기 가능한 형식으로 인코딩합니다:
CTX/2
$name:s|language:s|id:s|bio:t|version:n2
&0=Sindhi
&1=Uyghur
&2=Galician
&3=Maltese
&4=Sesotho sa Leboa
&5=isiZulu
&6=Icelandic
Adeel Solangi|&0|V59OF92YF627HFY0|Donec lobortis...|6.1
Afzal Ghaffar|&0|ENTOCR13RSCLZ6KU|...|3.2
같은 데이터가 이제 7,442 토큰과 13,746 문자를 차지합니다 — 토큰이 61.5 % 감소하고 문자는 78.4 % 감소했습니다.
작동 방식
스키마 추출
키 이름은 타입 힌트(s = string, n = number, t = text, 등)와 함께 상단에 한 번 정의됩니다.
값 중복 제거
자주 등장하는 값(예: "Sindhi")은 짧은 별칭(&0, &1, …)을 부여받고 해당 별칭으로 참조됩니다.
파이프‑구분 행
각 레코드는 | 로 구분된 값들을 한 줄에 배치하여 스키마와 위치적으로 매핑합니다. 중괄호, 따옴표, 콜론은 남지 않습니다.
결과는 토크나이저에 대해 밀집되어 있으면서도 LLM이 완벽히 읽을 수 있습니다.
설치
npm install ctx-compressor
기본 사용법
import { compress, decompress } from 'ctx-compressor';
const data = [
{ name: "Adeel Solangi", language: "Sindhi", id: "V59OF92YF627HFY0", bio: "...", version: 6.1 },
// ... more records
];
const compressed = compress(data);
// Send `compressed` to your LLM instead of JSON.stringify(data)
const original = decompress(compressed); // retrieve the original structure
Ideal Use Cases
- 유사한 형태의 객체 배열(데이터베이스 쿼리 결과, API 응답, CSV‑형식 데이터)
- 사용자 목록, 제품 카탈로그, 로그 항목 등
- 많은 객체에서 필드 이름이 반복되는 상황
- 자주 반복되는 값이 있는 필드(카테고리, 상태, 언어)
- 컨텍스트‑윈도우 제한 또는 비용‑절감 목표
- 더 많은 문서를 컨텍스트에 담고 싶을 때의 검색‑증강 생성(RAG)
Performance Test
| 데이터셋 | 토큰 (원시 JSON) | 토큰 (압축) | 절감 |
|---|---|---|---|
| 100 사용자 레코드 (이름, 언어, ID, 버전) | 19,338 | 7,442 | 61.5 % |
압축된 출력에는 스키마 정의와 별칭 테이블이 포함되어 있어 LLM이 데이터를 해석하는 데 필요한 모든 정보를 제공합니다. 테스트에서 모델은 이를 잘 처리합니다—질문을 하거나, 필터링하고, 집계하고, 원시 JSON와 마찬가지로 데이터를 기반으로 추론할 수 있습니다.
Prompting Hint
You may add a brief instruction in your system prompt, e.g.:
“The following data is in CTX/2 compressed format. The schema and alias definitions are included at the top.”
Most modern models infer the format without extra prompting.
향후 방향
- 중첩 객체 지원
- 매우 큰 데이터셋을 위한 스트리밍 압축
- 인기 있는 LLM SDK와의 내장 통합
- 빠른 터미널 압축을 위한 CLI 도구
- 텍스트 프롬프트 압축
행동 촉구
npm에서 패키지를 확인하고, 직접 구조화된 데이터에 적용해 보세요. 사용해 보시고 의견을 알려 주세요. 유용하다고 생각되면 저장소에 별표(star)를 달아 주세요—더 많은 사람들이 프로젝트를 발견하는 데 도움이 됩니다.