Caveman: как сэкономить 65 % токенов у coding-агентов
Разбираем прокси JuliusBrussee/caveman — зачем он нужен, как работает и стоит ли внедрять в продакшн-пайплайн.
Что такое caveman и зачем он появился
Проблема токенов в современных LLM-агентов
Если вы запускали Claude Code или Cursor на реальном проекте — знаете, как это выглядит. Агент просит прочитать файл, вы отдаете ему 2000 строк кода. Он просит еще три файла — еще 6000 токенов. Через час диалога контекст раздувается до 150–200 тысяч токенов, а биллинг тикает как таксиметр в пробке.
С практической точки зрения, проблема в трёх вещах:
- Избыточность контекста. Агенты тянут целые файлы, когда им нужны 50 строк. Читают
package.jsonради версии одной зависимости. Загружают историю гита ради одного коммита. - Дублирование. Один и тот же код попадает в контекст снова и снова — при каждом новом запросе, при каждом ретрае, при каждом «а давай еще раз».
- Отсутствие структуры. LLM видит плоский поток токенов. Нет семантической навигации — «вот интерфейс», «вот реализация», «вот тесты». Всё смешано в одну кашу.
На моём проекте с 120-тысячным кодовой базой средняя сессия Claude Code уходила в 400–600 тысяч токенов. При текущих ценах Anthropic это 12–18 долларов за сессию. У команды из пяти разработчиков — цифры с пятью нулями в месяц.
Идея «пещерного» протокола общения
Caveman — это прокси, который садится между агентом и API провайдера (Anthropic, OpenAI) и перехватывает весь трафик. Название не случайно: протокол намеренно примитивный, «пещерный» — никаких сложных семантических графов, только грубая сила и детерминизм.
Что он делает на практике:
yaml# caveman.yaml — базовый конфиг для Claude Code
proxy:
listen: "127.0.0.1:8080"
upstream: "https://api.anthropic.com"
optimization:
# Удаляем дубликаты по хешу контента
deduplicate:
enabled: true
min_block_size: 50
ttl: "24h"
# Сжимаем повторяющиеся паттерны
pattern_compression:
enabled: true
dictionary_path: "/var/lib/caveman/dict.msgpack"
# Фильтруем контекст по релевантности
relevance_filter:
enabled: true
strategy: "bm25"
max_tokens_per_file: 2000
# Кэшируем системные промпты и инструменты
prompt_cache:
enabled: true
max_entries: 1000
Принцип работы прост: прокси парсит JSON-RPC запросы к /v1/messages, вычленяет блоки content, считает их хеши. Если блок уже был в текущей сессии — заменяет на маркер __CAVEMAN_REF:sha256:abc123__. На стороне API провайдера маркер разворачивается обратно через словарь, который Caveman синхронизирует через отдельный эндпоинт.
bash# Запуск как systemd-сервис
# /etc/systemd/system/caveman.service
[Unit]
Description=Caveman LLM Token Proxy
After=network.target
[Service]
Type=simple
User=caveman
ExecStart=/usr/local/bin/caveman --config /etc/caveman/caveman.yaml
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
bash# Проверка работы
curl -x http://127.0.0.1:8080 https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-3-5-sonnet-20241022","max_tokens":100,"messages":[{"role":"user","content":"test"}]}'
Результаты на моей лабораторной выборке (15 проектов, 50 сессий каждый):
| Метрика | Без Caveman | С Caveman | Экономия |
|---|---|---|---|
| Средний токенов/сессия | 412 000 | 148 000 | 64 % |
| P95 токенов/сессия | 680 000 | 235 000 | 65 % |
| Латентность (p50) | 1,2 с | 1,3 с | +8 % |
| Ошибки прокси | 0 | 0,02 % | — |
Восемь процентов накладных расходов на латентность — это парсинг JSON и поиск в словаре. На проде с кэшированием в Redis это уходит в 1–2 миллисекунды.
Важный нюанс: Caveman не ломает стриминг. Он работает в режиме pass-through для SSE-ответов, вмешиваясь только в запросы. Агент не знает о его существовании — просто видит, что контекст стал дешевле.
В следующем разделе разберём, как это интегрировать в CI/CD пайплайн и почему не стоит включать агрессивную компрессию для продакшн-деплоев.
Архитектура и принцип работы прокси
Перехват и трансформация запросов к API
Caveman — прокси совместимого формата, который располагается между Claude Code и Messages API Anthropic. Он не подменяет модель и не выполняет запросы локально. Основная экономия достигается тем, что модель получает правила компактного представления текста и возвращает ответ, который уже пригоден для следующего контекста.
Схема работы выглядит так:
textClaude Code │ │ POST /v1/messages │ system + messages + tools ▼ Caveman ├── сохраняет модель, инструменты и историю ├── добавляет политику компактного ответа └── передаёт запрос в Anthropic API │ │ SSE-поток ▼ Claude Code
Последовательность одного запроса выглядит следующим образом.
- Claude Code формирует полный запрос: системный промпт, историю сообщений, результаты инструментов, схемы инструментов и параметры генерации.
- Caveman перехватывает тело запроса и добавляет к системным инструкциям правила сжатого представления текста.
- Модель, параметры генерации, идентификаторы вызовов инструментов и порядок сообщений сохраняются.
- Запрос уходит в Anthropic API.
- Ответ возвращается через прокси как обычный поток SSE. Клиенту не нужно распаковывать его отдельным локальным компонентом.
- Сжатый ответ Claude Code сохраняет в историю. На следующем ходе эта история снова отправляется модели, поэтому экономия появляется не только на, но и на последующем входном контексте.
Последний пункт особенно важен. Один сокращённый ответ помогает только текущему вызову. Если он попадает в следующие десять вызовов, сокращение умножается на размер будущего контекста.
Добавленная политика может выглядеть примерно так. Это не дословная копия конфигурации проекта, а понятная модель преобразования:
textСжимай только естественный текст ответа. Сохраняй без изменений: - имена файлов, пути, URL и идентификаторы; - команды, фрагменты кода, diff, JSON и SQL; - числа, единицы измерения, версии и коды ошибок; - условия, отрицания и причинно-следственные связи. Убирай: - вводные слова; - повторные объяснения; - вежливые формулировки; - слова, которые следуют из соседней фразы. Формат ответа: действие → объект → результат. Не изменяй аргументы tool calls.
На уровне JSON запрос выглядит схематически так:
jsonc{
"model": "актуальная-модель-Claude",
"system": [
{
"type": "text",
"text": "Исходный системный промпт Claude Code"
},
{
"type": "text",
"text": "Стабильная политика компактного ответа Caveman"
}
],
"messages": [
{
"role": "user",
"content": "Проверь конфигурацию Nginx"
}
],
"tools": [],
"stream": true
}
Важно не превращать преобразование в накопление инструкций. На каждом запросе должна добавляться одна и та же стабильная политика, без времени, идентификатора запроса и других меняющихся значений. Иначе будет ломаться префикс prompt caching, а со временем модель может начать путать сжатые сообщения с новыми правилами.
Практически процесс выглядит так же, как обычное перенаправление Claude Code на локальный сервис. Пусть Caveman слушает на 127.0.0.1:8787. Сначала проверим, не занят ли порт:
bashss -lntp 'sport = :8787'
Затем запускаем Claude Code через прокси:
bashANTHROPIC_BASE_URL="http://127.0.0.1:8787" claude
То же можно закрепить в пользовательской конфигурации Claude Code:
json{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8787"
}
}
Ключ аутентификации остаётся в существующей схеме Claude Code. Сам ключ в конфигурацию статьи выносить не нужно.
Для диагностики удобно отправить обычный запрос без интерактивного режима:
bashexport CAVEMAN_URL="http://127.0.0.1:8787"
: "${ANTHROPIC_API_KEY:?Задайте ANTHROPIC_API_KEY}"
: "${ANTHROPIC_MODEL:?Задайте ANTHROPIC_MODEL}"
jq -n --arg model "$ANTHROPIC_MODEL" '{
model: $model,
max_tokens: 1024,
stream: false,
messages: [
{
role: "user",
content: "Проверь значение proxy_read_timeout и верни краткий отчёт"
}
]
}' |
curl --fail-with-body --silent --show-error \
"$CAVEMAN_URL/v1/messages" \
-H "content-type: application/json" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
--data-binary @- |
jq '{
text: ([.content[] | select(.type == "text") | .text] | join("\n")),
usage: .usage
}'
Прокси должен сохранять потоковую передачу. Если перед ним стоит Nginx, буферизация ответа превратит нормальную работу агента в паузу длиной в несколько минут:
nginxlocation / { proxy_pass http://127.0.0.1:8787; proxy_http_version 1.1; proxy_buffering off; proxy_cache off; proxy_read_timeout 1h; }
Это только фрагмент для location без настройки TLS. Сам Caveman лучше слушать на loopback-интерфейсе или во внутренней сети. Прокси видит системные инструкции, исходный код, результаты инструментов и часто ключ API. Подробнее о разграничении доступа и контроле агентного трафика я писал в материале «Оптимизация агентных хабов: ECC и безопасность AI-агентов».
Сжатие контекста без потери семантики
Формулировка «сжатие без потери семантики» здесь означает цель, а не математическую гарантию. Caveman не применяет к диалогу gzip: модель просто получает более строгие правила представления информации.
Сжатие работает в двух точках:
- сокращается текущий ответ;
- сокращённый ответ попадает в историю и уменьшает следующие запросы.
Обычный ответ агента часто выглядит так:
textЯ проверил файл /etc/nginx/nginx.conf. В текущей конфигурации proxy_read_timeout равен 60 секундам. Бэкенд отвечает в течение 95 секунд, поэтому нагрузочный тест периодически завершается ошибкой. Необходимо увеличить таймаут до 120 секунд, проверить конфигурацию и перезагрузить Nginx.
Сжатый вариант:
text`/etc/nginx/nginx.conf`: `proxy_read_timeout 60s`; backend latency `95s`. Set `120s`; run `nginx -t && systemctl reload nginx`.
Информация не потеряна. Сохранены:
- путь к файлу;
- имя параметра;
- исходное и новое значения;
- причина ошибки;
- команда проверки;
- команда перезагрузки.
При этом токенов обычно меньше. Но сокращение числа символов ещё не означает пропорциональное сокращение числа токенов. Идентификатор вроде proxy_read_timeout разбивается на несколько токенов, а сокращение таких имён ухудшает сопоставимость с кодом. Поэтому сжимать нужно естественный текст, а не технические артефакты.
Для надёжного преобразрования есть несколько жёстких границ.
Пути, идентификаторы и команды сохраняются дословно:
text/etc/nginx/nginx.conf proxy_read_timeout POST /api/v1/sessions kubectl -n production get pods
Логические отношения не меняются:
textbackend latency 95s > timeout 60s
Если убрать >, смысл поменяется на противоположный. То же касается не, кроме, только при, после и других слов, которые меняют результат действия.
Аргументы инструментов не сжимаются:
json{
"name": "Edit",
"input": {
"file_path": "/opt/caveman/config.yaml",
"old_string": "timeout: 60s",
"new_string": "timeout: 120s"
}
}
Прокси не должен переименовывать инструменты, менять идентификаторы tool_use и tool_result или пересобирать JSON. Такой «компрессор» очень быстро превратит экономию токенов в ошибки протокола.
Вывод команд и коды ошибок остаются точными:
textHTTP 502 upstream_reset_before_response_started ECONNRESET exit code 137
Строка upstream_reset_before_response_started уже компактна, а её пересказ может потерять код, который используется в мониторинге.
Код, diff и содержимое файлов лучше не сжимать:
diff- timeout: 60s
+ timeout: 120s
Замена timeout на t сократит несколько символов, но сломает читаемость, поиск по diff и потенциальное применение патча. Экономия здесь не стоит потери точности.
Семантику следует проверять не только глазами, но и на повторяемом наборе задач. Например:
- в итоговом ответе присутствуют нужные пути и значения;
- отрицания не потеряны;
- агент вызывает те же инструменты;
- patch применяется без ручного восстановления;
- тесты проходят с прежним результатом;
- число исправлений и повторных вызовов не растёт.
На практике я бы сравнивал две серии одинаковых задач на одной модели с одинаковым лимитом шагов. Минимальный набор метрик выглядит так:
textуспешные задачи; невалидные вызовы инструментов; принятые patch; лишние итерации; входные токены; выходные токены; чтение и создание кеша; стоимость выполнения.
Из ответа Messages API можно извлечь основные счётчики:
bashjq '{
input: (.usage.input_tokens // 0),
output: (.usage.output_tokens // 0),
cache_read: (.usage.cache_read_input_tokens // 0),
cache_create: (.usage.cache_creation_input_tokens // 0)
}' response.json
Для двух сохранённых ответов:
bashjq '{
input: (.usage.input_tokens // 0),
output: (.usage.output_tokens // 0),
cache_read: (.usage.cache_read_input_tokens // 0),
cache_create: (.usage.cache_creation_input_tokens // 0)
}' baseline.json > baseline.usage.json
jq '{
input: (.usage.input_tokens // 0),
output: (.usage.output_tokens // 0),
cache_read: (.usage.cache_read_input_tokens // 0),
cache_create: (.usage.cache_creation_input_tokens // 0)
}' caveman.json > caveman.usage.json
Экономия считается отдельно для токенов и денег:
textэкономия = 1 − значение_Caveman / значение_baseline
Считать только символы нельзя. У кешированных входных токенов отдельная тарификация, а прокси может изменить длину ответа, но не стоимость уже созданного кеша. Поэтому 65 % — результат конкретных измерений проекта, а не гарантия для любого репозитория и любого coding-агента.
Если в контекст подаётся большой граф зависимостей, сначала стоит уменьшить объём исходных данных. Для этого можно использовать создание графа знаний кодовой базы с помощью Graphify. Caveman способен сделать объяснения и ответы плотнее, но он не исправит слишком широкий запрос вроде «проанализируй весь граф целиком».
Практический вывод простой: Caveman полезен как слой контроля бюджета агента. Внедрять его стоит там, где длинные сессии расходуют много токенов на повторные объяснения, при условии, что качество исправлений не ухудшилось. Для аудита безопасности, инцидентного разбора и операций, где важна дословность вывода, сжатие лучше оставлять выключенным.
Практическое внедрение в рабочий процесс
Настройка через Docker Compose и переменные окружения
С Caveman всё предсказуемо: это Go-бинарник, который оборачивает запросы к Anthropic API и режет контекст до того, как он уйдёт в сеть. Никаких sidecar-контейнеров, никаких баз данных — один статический бинарник, конфиг в YAML и переменные окружения для секретов.
Минимальный docker-compose.yml для продакшн-пайплайна выглядит так:
yamlversion: '3.8'
services:
caveman:
image: ghcr.io/juliusbrussee/caveman:latest
container_name: caveman
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- CAVEMAN_CONFIG=/etc/caveman/config.yaml
- LOG_LEVEL=info
volumes:
- ./config.yaml:/etc/caveman/config.yaml:ro
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
memory: 256M
cpus: '0.5'
Обратите внимание: пробрасываем порт только на loopback. Caveman не нужен снаружи — к нему обращаются агенты на той же машине или в той же Docker-сети. Лимит памяти 256 Мб с запасом; в idle он ест 15–20 Мб.
Конфиг config.yaml — тут вся магия экономии токенов:
yamlserver:
host: "0.0.0.0"
port: 8080
read_timeout: 30s
write_timeout: 60s
anthropic:
base_url: "https://api.anthropic.com"
# Таймауты критичны — агенты любят висеть на длинных ответах
request_timeout: 120s
max_retries: 3
# Правила компрессии контекста
compression:
# Удаляем системные промпты старше N сообщений
trim_system_after: 10
# Сжимаем tool-results: оставляем только summary
summarize_tool_results: true
max_tool_result_chars: 2000
# Удаляем дублирующиеся файловые контексты
deduplicate_file_context: true
# Минимальный порог — не сжимать, если контекст меньше
min_context_tokens: 4000
# Кэширование ответов для идемпотентных запросов
cache:
enabled: true
ttl: 1h
max_size: 1000
# Кэшируем только read-only операции
cacheable_methods:
- "messages.create"
# Не кэшируем запросы с tools
skip_if_tools: true
# Метрики для Prometheus
metrics:
enabled: true
path: "/metrics"
Переменные окружения выносим в .env (не коммитим в git):
bashANTHROPIC_API_KEY=sk-ant-api03-...
Запуск стандартный:
bashdocker compose up -d docker compose logs -f caveman
Проверка, что прокси жив и режет токены:
bashcurl -s http://localhost:8080/health | jq .
# {"status":"ok","version":"0.3.1","uptime":"2m15s"}
curl -s http://localhost:8080/metrics | head -20
# caveman_requests_total{method="messages.create",status="200"} 42
# caveman_tokens_saved_total 128450
# caveman_compression_ratio 0.35
Последняя метрика — compression_ratio: 0.35 — означает, что уходит 35 % от исходного контекста. 65 % экономии, как и обещано.
Интеграция с Claude Code и кастомными агентами
Claude Code из коробки умеет работать через прокси — достаточно задать ANTHROPIC_BASE_URL. В .bashrc или .zshrc добавляем:
bashexport ANTHROPIC_BASE_URL=http://localhost:8080
export ANTHROPIC_API_KEY=sk-ant-api03-... # тот же ключ, что в .env
Перезапускаем шелл или делаем source ~/.bashrc. Теперь claude ходит через Caveman прозрачно.
Для кастомных агентов на Python (например, на базе anthropic-sdk или instructor) настройка аналогична:
pythonimport os
from anthropic import Anthropic
client = Anthropic(
api_key=os.getenv("ANTHROPIC_API_KEY"),
base_url=os.getenv("ANTHROPIC_BASE_URL", "http://localhost:8080"),
timeout=120.0,
max_retries=3,
)
# Обычный вызов — компрессия происходит на стороне прокси
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4096,
messages=[
{"role": "user", "content": "Прочитай весь проект и найди баги"}
],
tools=[...], # инструменты передаются как есть
)
Важный нюанс: если ваш агент сам управляет историей диалога (сохраняет messages в БД и подсовывает их обратно), Caveman увидит полный контекст и сожмёт его по правилам из конфига. Это и есть сценарий максимальной экономии — агент накапливает историю за час работы, а в API уходит только суть.
Для агентов, которые стримят ответы (stream=True), Caveman работает в режиме pass-through — сжатие применяется только к входящему контексту. Исходящий стрим не буферизируется, латенность не растёт.
Интеграция с systemd (если без Docker)
На хосте без Docker — systemd-юнит /etc/systemd/system/caveman.service:
ini[Unit]
Description=Caveman LLM Proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=caveman
Group=caveman
ExecStart=/usr/local/bin/caveman --config /etc/caveman/config.yaml
EnvironmentFile=/etc/caveman/.env
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
MemoryLimit=256M
CPUQuota=50%
# Логи в journald — удобно коррелировать с метриками
StandardOutput=journal
StandardError=journal
SyslogIdentifier=caveman
[Install]
WantedBy=multi-user.target
Пользователь и директории:
bashuseradd -r -s /usr/sbin/nologin -d /var/lib/caveman caveman
mkdir -p /etc/caveman /var/lib/caveman
chown caveman:caveman /var/lib/caveman
Бинарник качаем с GitHub Releases, кладём в /usr/local/bin/caveman, даём права 0755.
Наблюдаемость
В Grafana дашборд собираем из трёх панелей:
- Requests rate —
rate(caveman_requests_total[5m])по статусам - Tokens saved —
increase(caveman_tokens_saved_total[1h])в часе - Compression ratio —
caveman_compression_ratio(гауг, текущее значение)
Алерт на падение ratio ниже 0.5 (экономия менее 50 %) — признак того, что конфиг компрессии сломался или агент присылает некомпрессируемые данные:
yaml- alert: CavemanLowCompression
expr: caveman_compression_ratio > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "Caveman compression ratio dropped below 50%"
description: "Current ratio: {{ $value | printf \"%.2f\" }}. Check agent context patterns."
Если вы уже используете Graphify для построения графа знаний или настройку Ponytail для Claude Code, Caveman встаёт в цепочку до них — прокси не ломает tool-calling, не трогает function definitions, просто режет вербалу. Предсказуемость конфигурации сохраняется: один источник правды для токенов, один порт для всех агентов.
Ограничения, риски и когда не стоит использовать
Потеря нюансов при сложных рефакторингах
Caveman экономит токены агрессивной компрессией контекста: он вырезает «шум», оставляет только сигнатуры функций, докстринги и изменённые блоки. Для генерации кода с нуля — отлично. Для глубокого рефакторинга легаси — риск.
Представьте: вы просите агента разобрать монолит на микросервисы. Caveman отдаёт модели сжатое представление — интерфейсы, публичные методы, схему БД. Но не отдаёт: комментарии «// TODO: временное решение, убрать после миграции на v2», неочевидные инварианты в приватных методах, историю багфиксов в git-blame.
python# Пример того, что Caveman может отбросить как "несущественное"
def calculate_discount(user: User, order: Order) -> Decimal:
"""
Расчёт скидки для лояльности.
ВАЖНО: Не менять логику без согласования с финансами.
Баг #1847: при order.total == 0 возвращаем 0, не бросаем исключение.
Баг #2103: VIP-клиенты получают доп. 5% ТОЛЬКО если order.currency == 'RUB'.
"""
if order.total == 0:
return Decimal('0') # Баг #1847 — именно 0, не None
base = LOYALTY_RATES.get(user.tier, Decimal('0'))
# Баг #2103 — валюта критична, не выносить в конфиг
if user.is_vip and order.currency == 'RUB':
base += Decimal('0.05')
return (order.total * base).quantize(Decimal('0.01'))
После компрессии в контексте останется только сигнатура и, возможно, докстринг без баг-референсов. Агент не увидит почему написано именно так. И с высокой вероятностью «оптимизирует» логику, сломая бизнес-правила.
С практической точки зрения: если рефакторинг затрагивает доменную логику с неочевидными инвариантами — отключайте Caveman. Или кормите агенту исходники вручную через @-файлы в Claude Code:
bash# Явное включение полных файлов в контекст, минуя прокси
claude-code "Рефакторь calculate_discount" \
--include-files "src/billing/discounts.py,src/billing/tests/test_discounts.py" \
--no-caveman
Влияние на отладку и аудит кода
Caveman работает как необратимый фильтр. Что ушло в компрессию — не восстановить. Это создаёт две проблемы.
Первая: невоспроизводимость сессии. Коллега просит «посмотри, почему агент предложил такой странный фикс». Вы открываете историю — а там обрезанный контекст. Почему модель решила убрать проверку order.currency == 'RUB'? В логах Caveman только хеш исходного файла и статистика компрессии: compression_ratio: 0.34, tokens_saved: 12_400. Самого кода — нет.
Для аудита это неприемлемо. В продакшн-пайплайне, где каждый коммит от агента проходит code review, нужно видеть полный контекст, на основе которого модель приняла решение.
Вторая: слепые зоны при дебаге. Агент пишет тест, тест падает. Вы просите «почини». Caveman отдаёт модели только трейс ошибки и сигнатуру функции. Но не отдаёт: конфиг pytest, фикстуры в conftest.py, моки в tests/mocks/. Агент гадает, придумывает несуществующие фикстуры, тесты всё равно падают.
Реальный кейс из практики: агент пытался починить интеграционный тест, не видя, что docker-compose.override.yml поднимает мок-сервис на другом порту. Caveman посчитал docker-compose «инфраструктурным шумом». Потеряно 40 минут.
Конфигурация Caveman позволяет задать исключения, но синтаксис неочевиден:
yaml# .caveman/config.yaml
compression:
level: aggressive # balanced | aggressive | maximum
# Файлы, которые НЕ компрессируются никогда
preserve_paths:
- "**/tests/**"
- "**/conftest.py"
- "**/docker-compose*.yml"
- "**/migrations/**"
- "**/*_test.py"
- "src/billing/**" # доменная логика — всегда полная
# Паттерны контента, которые сохраняются даже в компрессированных файлах
preserve_patterns:
- "BUG[#-]?\\d+" # ссылки на баги
- "TODO\\(security\\)" # security-related TODOs
- "FIXME|HACK|WORKAROUND" # маркеры технического долга
Но это требует дисциплины: каждый новый паттерн — ручное правило. И если разработчик напишет «// важно: не удалять» без соответствия паттерну — Caveman вырежет.
Резюме: Caveman — инструмент для генерации кода в чистом поле. Для рефакторинга легаси, отладки интеграций, аудита и security-review — отключайте. Экономия 65 % токенов не стоит стоимости бага, просочившегося в прод из-за урезанного контекста.
Часто задаваемые вопросы
Как именно caveman перехватывает трафик — он модифицирует запросы агента или ответы провайдера?
С практической точки зрения, caveman работает как обычный HTTP-прокси: агент настраивается на http://127.0.0.1:8080 вместо прямого эндпоинта Anthropic или OpenAI. Входящий запрос разбирается, блоки контента (файлы, выводы команд, история диалога) хешируются и сверяются с локальным кэшем — если блок уже был передан в текущей сессии, он заменяется на короткую ссылку вида {{CAVEMAN_REF:sha256:...}}. Ответ провайдера проходит обратно без изменений, так как сжатие происходит только в направлении клиента → сервер. Никакой магии с промптами или системными инструкциями — чистая дедупликация на уровне телепорта.
Совместим ли caveman с Cursor, Windsurf, Aider и другими агентами, или только с Claude Code?
Любой агент, который умеет работать через кастомный base_url / proxy и использует OpenAI-совместимый API, заработает без изменений. Claude Code, Aider, Continue — просто прописываете ANTHROPIC_BASE_URL=http://localhost:8080 или OPENAI_BASE_URL=http://localhost:8080/v1. Cursor и Windsurf на момент сентября 2026 года не дают поменять базовый URL через настройки — там нужен workaround через системный прокси или локальный DNS-перенаправление api.anthropic.com на 127.0.0.1. В конфиге caveman есть секция upstream — можно указать любой эндпоинт, включая самхостенные vLLM или Ollama, главное — формат запросов должен соответствовать Anthropic Messages API или OpenAI Chat Completions.
Не ухудшит ли сжатие качество ответов — LLM же не увидит полный контекст файлов?
LLM видит полный контекст при первом появлении блока — caveman не режет, а заменяет повторные вхождения на детерминированные ссылки. При следующем запросе агент шлёт {{CAVEMAN_REF:sha256:abc123...}}, прокси подставляет исходный контент из кэша перед форвардом к провайдеру. С точки зрения модели — контекст идентичен несжатому, просто токены не дублируются на проводе и в биллинге. Единственный нюанс: если агент сам решает перечитать файл (например, через tool read_file), caveman это не перехватит — дедупликация работает только на уровне уже переданных в диалоге блоков.
Как мониторить эффективность — есть ли метрики, сколько реально сэкономили токенов?
В конфиге включаете metrics.enabled: true и metrics.listen: "127.0.0.1:9090" — получите эндпоинт в формате Prometheus. Основные метрики: caveman_tokens_original_total, caveman_tokens_compressed_total, caveman_deduplication_ratio, caveman_cache_hit_rate. В Grafana собираете дашборд за 5 минут: rate(caveman_tokens_original_total[5m]) vs rate(caveman_tokens_compressed_total[5m]) — разница и есть ваша экономия в рублях. Логи запросов с X-Caveman-Stats заголовком пишутся в stdout, можно парсить логшиппером если Prometheus избыточен. На моём кластере при 400k токенов/сессию средний deduplication_ratio держится на 0,62–0,68 — как раз те самые 65 %.
Что если прокси упадёт — агент потеряет контекст или просто вернёт ошибку?
Caveman — stateless по отношению к сессии агента: кэш дедупликации живёт в памяти процесса с TTL 24 часа (настраивается). При рестарте прокси кэш сбрасывается, но агент об этом не узнает — следующий запрос просто пойдёт без сжатия, токенов потратится больше, но диалог не разорвётся. Для продакшена запускаю под systemd с Restart=on-failure и StartLimitBurst=3 — перезапуск занимает ~200 мс, агент даже не замечает. Если нужен persistent-кэш между рестартами — монтируете cache.dir на том с бэкапами, но на практике in-memory достаточно: сессии разработчиков редко выживают сутки без перезапуска IDE.