pxpipe: сжатие контекста LLM через рендеринг текста в изображения
📂 Исходный код на GitHubЛокальный прокси для Claude Code, который рендерит bulky-части запроса в PNG, чтобы снизить число input-токенов на 59–70%.
Что это
pxpipe — локальный прокси, который подменяет у Claude Code самые тяжёлые куски входящего запроса PNG-картинками. Системный промпт, описание инструментов и старая история уходят в визуальный канал, а сам ответ модели и последние ходы остаются текстом. На текущей Fable 5 заявлено ~59–70% снижения end-to-end счёта, но цифра зависит от нагрузки — устойчивая величина это сам cut по токенам, который замеряется per-request через бесплатный count_tokens контрфактик и пишется в ~/.pxpipe/events.jsonl.
Ключевая идея
Стоимость изображения зависит от пикселей, а не от количества символов внутри. На реальном трафике Claude Code плотный контент (код, JSON, вывод утилит) даёт ~3.1 символа на image-token против ~1 символа на text-token. В роли читателя выступает тот же vision-канал, на который Anthropic уже опирается в computer use. pxpipe эксплуатирует этот канал: перехватывает /v1/messages, переписывает bulky-блоки в компактные PNG, склеивает обратно cache-friendly (статический префикс сохраняется, prompt caching продолжает работать) и форвардит дальше.
Архитектура
tool_result string ──► wrap at 1928px-wide columns ──► pack ~92,000 chars/page ──► PNG[]
Картинка 1928×1928 стоит ~4 761 vision-токен и вмещает ~92 000 символов. Точка перехода: текст выгоднее картинки только при плотности выше ~19 символов на токен; в реальном трафике Claude Code медиана ~1.91 символа/токен, так что почти всё уходит в изображения. Per-request estimator на каждом запросе решает, что оставить текстом — например, разреженную прозу — а что перевести в PNG. Все события логируются в ~/.pxpipe/events.jsonl.
Что сжимается
Три категории входящих блоков, каждая за profitability gate:
- Крупные
tool_result(чтения файлов, вывод команд, логи) свыше ~6k символов плотного контента. - Старая свёрнутая история: ходы за живым хвостом рендерятся в image-страницы, последние ходы остаются текстом.
- Статический system prompt + slab tool-документации.
Остальное проходит byte-identical: сообщения пользователя, последние ходы, ответ модели (это response, прокси его не трогает), разреженная проза, всё слишком мелкое. Модели вне allowlist проходят полностью — по умолчанию это только Fable 5 и GPT 5.6. Opus 4.8 и GPT 5.5 читают изображения заметно хуже, поэтому они opt-in через дашборд или PXPIPE_MODELS, никогда не подключаются молча. PXPIPE_MODELS=off полностью выключает imaging.
Запуск за 30 секунд
npx pxpipe-proxy # proxy on 127.0.0.1:47821
ANTHROPIC_BASE_URL=http://127.0.0.1:47821 claude # point Claude Code at it
Дашборд на http://127.0.0.1:47821/ показывает сэкономленные токены, side-by-side сравнение text→image, kill-switch и активные model chips. Ответы стримятся как обычно — pxpipe сжимает только request, никогда output. Последние ходы остаются текстом; system prompt, tool docs и старая bulk-история уходят в изображения.
Бенчмарки
Тесты на свежих случайных числах, которые модель не могла запомнить в обучающих данных.
| test | N | text | pxpipe (image) | tokens |
|---|---|---|---|---|
novel arithmetic, claude-fable-5 |
100 | 100% | 100% | −38% |
novel arithmetic, claude-opus-4-8 |
100 | 100% | 93% | −38% |
| gist recall A/B (решения, значения, пути, имена, отрицания; с дистракторами; сессии 15k–45k символов), Fable 5 | 98/arm | 98/98 | 98/98 | - |
| state tracking (значение мутировано 3 раза: финальное/первое/count), Fable 5 | 18/arm | 18/18 | 18/18 | - |
| confabulation на не-заявленных фактах (меньше — лучше), Fable 5 | 16/arm | 0/16 | 0/16 | - |
| verbatim 12-char hex recall, dense render, Opus | 15 | 15/15 | 0/15 | - |
| verbatim 12-char hex recall, dense render, Fable 5 | 15 | - | 13/15 | - |
На реальной работе: SWE-bench Lite pilot 10/10 оба рукава при −65% размера запроса; SWE-bench Pro 14/19 ON против 15/19 OFF при −60%, вердикты совпали в 18/19, единственный сплит повторился 3/3 — это run-to-run variance, а не компрессия. Маленькая выборка; чеки в eval/.
Ограничения и честная часть
- Сжатие с потерями. Точные 12-символьные hex-строки в плотном imaged-контенте: 13/15 на Fable 5, 0/15 на Opus — и промахи тихие конфабуляции, а не ошибки. Байт-точные значения (ID, хэши, секреты) должны оставаться текстом; последние ходы остаются. Специализированный verbatim-risk guard пока не построен.
- Escape hatch: subagents на не-allowlisted моделях проходят как текст — направляйте байт-точную работу туда (
CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-4-6илиmodel: sonnetв frontmatter агента). - Workload-dependent. Выигрыш на плотном контенте (~1 символ/токен), проигрыш на разреженной прозе (~3.5 символа/токен); profitability gate (калиброван на N=391 production row) превращает в картинку только там, где математика сходится.
- Real-world провал. За недели ежедневного использования один раз модель вспомнила имя человека из imaged истории чата и уверенно ошиблась. Никакой ошибки, просто правдоподобно неправильное имя. Это задокументированный failure mode: точные строки в imaged-контенте не byte-safe. Кодинг-сессии это переносят, потому что агент перечитывает файл перед редактированием; чистый chat recall такой проверки лишён.
- Почему промахи — тихие конфабуляции, а не ошибки чтения. Потому что vision модели — это не OCR: изображение становится patch-эмбеддингами, не дискретными символами, и показывать per-glyph confidence нечего. Когда пиксели недоопределяют глиф, языковая априорная заполняет пробел правдоподобным значением. Механизм и рецепты — в
docs/NOT-OCR.md. - Latency. PNG-кодирование добавляет задержку к большим запросам до их отправки.
- ASCII/Latin-1 хорошо протестирован; CJK работает, но консервативно.
Как измеряется экономия
Обе стороны одного запроса в один момент. На каждый POST /v1/messages прокси параллельно с реальным форвардом шлёт бесплатный count_tokens к оригинальному uncompressed body (контрфактик) и читает из ответа фактически билинговый usage block Anthropic. Оба попадают в одну строку ~/.pxpipe/events.jsonl, без turn-count или run-to-run confound. Перевод в доллары по Fable 5 list ratios: input ×1.0, cache write ×1.25, cache read ×0.1, output ×5. Кэш-тарификация применяется одинаково к обеим сторонам, так что скидка кэширования сокращается и не может задвоиться как «savings». Пере-выведите самостоятельно из лога событий: формула и имена полей в src/core/baseline.ts.
Headline — это end-to-end?
Да. Большинство инструментов компрессии рапортуют экономию только по input-срезу, который сами трогали, что делает цифру приятнее. End-to-end знаменатель — это каждый production request: мелкие, которые pxpipe корректно не трогал, все cache write и read, и все output-токены (прокси их никогда не сжимает). На снимке в 13 709 запросов это 59% ($100 → ~$41); позже на 8 904 compressed-request трейсе — ~70%. Compressed-only показывает выше (~72–74%) и квотируется отдельно, никогда не как headline. Точная цифра workload-dependent — воспроизведите на своём логе.
Library use без прокси
import { renderTextToImages, transformAnthropicMessages } from "pxpipe-proxy";
const { pages } = await renderTextToImages(toolResultText); // pages[i].png: Uint8Array
const { body, applied, info } = await transformAnthropicMessages({
body: requestBytes,
model: "claude-fable-5",
});
options.keepSharp(block) фиксирует блоки как текст; options.emitRecoverable возвращает оригиналы imaged-блоков. Pure-JS runtime (Node и edge/Workers); @napi-rs/canvas нужен только на этапе сборки. Полный API — в src/core/index.ts.
Development
pnpm install && pnpm test
pnpm run build # regenerates dist/
Roadmap
Rendering research припаркован с 2026-07-05: verbatim misreads ограничены ёмкостью канала, а не трюками, и никакие изменения шрифта/цвета/раскладки не починят exact-string recall на прибыльной плотности. Почему — в docs/NOT-OCR.md; датированный анализ и три задокументированных follow-up треда (glyph-style A/B с banked-страницами, runtime canary + re-fetch, surrogate-reader pre-flight) — в FINDINGS.md, запись 2026-07-05. Watch condition: пересобирать resolution sweep на каждый релиз модели; readable density сдвинулся ~4x в glyph area от Opus 4.8 к Fable 5, и модель, читающая production cells около 100%, автоматически поднимет экономию.
Всё ещё открыто, без изменений: тянет ли imaged bulk эффективный контекст (~2x реального контента в том же 1M окне), и улучшает ли меньший активный контекст точность на длинных задачах. Гипотезы, не утверждения — они выходят как числа с n или вырезаются.
Лицензия
MIT. Исходный репозиторий: https://github.com/teamchong/pxpipe.
Источник: https://github.com/teamchong/pxpipe