- Дата публикации
Как заменить JSON на TOON в промптах LLM и срезать до 60% токенов
Что нового
TOON (Token-Oriented Object Notation) — текстовый формат для структурированных данных, который автор использует вместо JSON в промптах к LLM и за счёт этого снижает расход токенов.
Ключевые факты из реальных замеров:
- Тест на 50 лог-записях по 7 полей каждая.
- JSON с форматированием: 379 токенов.
- Компактный JSON в одну строку: 236 токенов (экономия ~37,7% относительно форматированного).
- TOON: 150 токенов (экономия ~60,4% относительно форматированного JSON и ~36% относительно компактного).
Финансовый эффект на GPT-4o при 10 000 запросов в день, массив по 100 объектов в каждом:
- JSON: ~3 200 токенов на запрос, 32 млн токенов в день, $80 в день, $2 400 в месяц, $28 800 в год.
- TOON: ~1 850 токенов на запрос, 18,5 млн токенов в день, $46,25 в день, $1 387 в месяц, $16 650 в год.
- Экономия: примерно 42% токенов и $12 150 в год только по input для одного эндпоинта.
На Claude Opus 4.6 (input $5 за 1 млн токенов):
- JSON: $4 800 в месяц, $57 600 в год.
- TOON: $2 775 в месяц, $33 300 в год.
- Экономия: $2 025 в месяц и $24 300 в год.
TOON кодирует те же данные, что и JSON, без потерь и с сохранением структуры. Формат уже доступен как библиотеки для Python и TypeScript.
Как это работает
В чём проблема JSON в промптах
При работе с GPT-4o, GPT-4.1, Claude Sonnet 4.6 или Claude Opus 4.6 многие разработчики отправляют в контекст большие куски JSON: списки товаров, логи, пользователей, метрики. JSON удобен для кода, но неэкономичен для токенов.
Например, одна запись лога в JSON:
{"id": 2001, "timestamp": "2025-11-18T08:14:23Z", "level": "error", "service": "auth-api", "ip": "172.16.4.21", "message": "Auth failed for user", "code": "AUTH_401"}
У каждой записи повторяются ключи "id", "timestamp", "level", "service", "ip", "message", "code". На массиве из 50–100 записей это сотни повторяющихся слов и символов: кавычки, фигурные скобки, запятые.
Токенизатор LLM превращает всё это в токены. Модель не нуждается в каждом повторении имён полей, но вы за них платите.
Как устроен TOON
TOON (Token-Oriented Object Notation) решает именно эту проблему: избавляет от повторяющихся ключей и лишнего синтаксиса.
Пример массива логов в TOON:
logs[3]{id,timestamp,level,service,ip,message,code}:
2001,2025-11-18T08:14:23Z,error,auth-api,172.16.4.21,Auth failed for user,AUTH_401
2002,2025-11-18T08:14:24Z,warn,payment,172.16.4.22,Timeout on payment gateway,PAY_TIMEOUT
2003,2025-11-18T08:14:25Z,info,user-svc,172.16.4.23,User profile updated,USR_200
Что здесь происходит:
logs[3]{id,timestamp,level,service,ip,message,code}:— заголовок.logs— имя массива.[3]— количество элементов.- В фигурных скобках — список полей, которые будут использоваться для всех строк.
- Далее — по одной строке на запись, только значения через запятую.
Ключи объявляются один раз в заголовке, затем повторяются только значения. Это резко уменьшает количество символов и, как следствие, токенов.
Для обычных объектов TOON использует синтаксис, похожий на YAML:
name: Alice
age: 30
role: admin
settings:
theme: dark
lang: ru
Под капотом TOON — это просто другой способ записать ту же структуру данных, что и в JSON. Конвертация JSON → TOON → JSON происходит без потерь: формат не меняет типы и не режет поля, только кодировку.
Как замеряли токены
Автор взял датасет из 50 лог-записей и прогнал три варианта строки через токенизатор:
- JSON с отступами (
indent=2). - Компактный JSON в одну строку.
- Строку в формате TOON.
Код для Python (GPT-4o через tiktoken):
import json
import toon_format # pip install toon-format
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
with open("logs.json") as f:
data = json.load(f)
json_str = json.dumps(data, indent=2)
json_compact = json.dumps(data)
toon_str = toon_format.encode(data)
print(f"JSON (formatted): {len(enc.encode(json_str))} токенов")
print(f"JSON (compact): {len(enc.encode(json_compact))} токенов")
print(f"TOON: {len(enc.encode(toon_str))} токенов")
Код для TypeScript (токенизатор gpt-3-encoder):
import { encode as toToon } from "@toon-format/toon";
import { encode as tokenize } from "gpt-3-encoder";
import fs from "fs";
const data = JSON.parse(fs.readFileSync("./logs.json", "utf8"));
const jsonFormatted = JSON.stringify(data, null, 2);
const jsonCompact = JSON.stringify(data);
const toonStr = toToon(data);
console.log(`JSON (formatted): ${tokenize(jsonFormatted).length} токенов`);
console.log(`JSON (compact): ${tokenize(jsonCompact).length} токенов`);
console.log(`TOON: ${tokenize(toonStr).length} токенов`);
Результаты:
| Формат | Токены | Экономия vs JSON (formatted) | |------------------|--------|-------------------------------| | JSON (formatted) | 379 | — | | JSON (compact) | 236 | −37,7% | | TOON | 150 | −60,4% |
Что это значит для вас
Когда TOON даёт максимальный эффект
TOON особенно полезен, если вы:
- Строите RAG-систему, которая подмешивает в контекст длинные списки документов, логов или записей БД.
- Делаете аналитических ассистентов, которые переваривают массивы метрик, логов и событий.
- Часто отправляете в LLM большие массивы однотипных объектов: товары, транзакции, заявки, пользователи.
Во всех этих сценариях данные имеют одинаковую схему, а JSON постоянно повторяет одни и те же ключи. TOON убирает это дублирование и снижает расход токенов.
Практические плюсы:
- Меньше расходов на API. На GPT-4o, GPT-4.1, Claude Sonnet 4.6 и Claude Opus 4.6 экономия легко уходит в тысячи долларов в год, если вы работаете в проде с десятками тысяч запросов в день.
- Больше данных в том же контексте. При фиксированном лимите контекста вы можете отправить больше записей.
- Более предсказуемые промпты. Меньше служебного текста, больше "полезных" данных.
Где TOON может не подойти
TOON — это не замена JSON в API между сервисами, а прослойка именно для общения с LLM.
Формат может быть не лучшим выбором, если:
- У вас маленькие запросы по 1–2 объекта, где экономия на токенах будет минимальной.
- Важнее совместимость с чужими сервисами, которые ожидают строго JSON.
- Команда не готова добавлять ещё один формат в стек и поддерживать его в логировании и отладке.
Также нужно учитывать, что OpenAI и Anthropic не знают про TOON. Модели видят просто текст. Поэтому в промпте имеет смысл коротко объяснить, что за формат вы используете, если структура нетривиальная.
Доступность
TOON — это текстовый формат и несколько библиотек. Подключение не зависит от региона. Если вы уже используете GPT-4o, GPT-4.1, Claude Sonnet 4.6 или Claude Opus 4.6 через API, вы можете внедрить TOON без VPN и без смены инфраструктуры.
Место на рынке
TOON конкурирует не с LLM, а с форматами сериализации, которые вы используете в промптах.
Фактическое сравнение по одному тесту (50 логов, 7 полей):
- JSON с отступами — удобен для человека, но дороже по токенам: 379 токенов.
- Компактный JSON — уже ощутимо экономнее: 236 токенов (−37,7%).
- TOON — ещё на шаг дальше: 150 токенов (−60,4% от форматированного JSON и примерно −36% от компактного).
По трудозатратам TOON ближе к JSON, чем к бинарным форматам вроде Protobuf: вы всё равно работаете с текстом, который можно вставить прямо в промпт.
Если вы уже перешли с форматированного JSON на компактный, TOON даёт дополнительный выигрыш. Если вы пока отправляете в модель красиво отформатированный JSON, переход на TOON может сразу сократить счёт за LLM почти вдвое для тяжёлых запросов.
Установка
Python
Для Python TOON доступен как пакет toon_format:
pip install toon-format
Пример использования для подсчёта токенов уже был выше. Ниже — пример интеграции с OpenAI GPT-4o.
TypeScript / Node.js
Для TypeScript используется пакет @toon-format/toon:
npm install @toon-format/toon gpt-3-encoder
gpt-3-encoder нужен только для локальных замеров токенов. Для реальных запросов к GPT-4o, GPT-4.1, Claude Sonnet 4.6 или Claude Opus 4.6 он не обязателен.
Как запустить: примеры интеграции
Python + GPT-4o (OpenAI)
Пример функции, которая принимает список словарей, кодирует его в TOON и отправляет в GPT-4o:
import openai
import json
import toon_format
def analyze_with_llm(data: list[dict]) -> str:
toon_str = toon_format.encode({"records": data}) # JSON -> TOON
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "user",
"content": f"Проанализируй эти данные и найди аномалии:\n\n{toon_str}"
}]
)
return response.choices[0].message.content
Здесь TOON выступает как прослойка: вы продолжаете работать с Python-объектами, а перед отправкой в OpenAI превращаете их в более компактный текст.
TypeScript + Claude Sonnet 4.6 (Anthropic)
Аналогичный пример для TypeScript и Claude Sonnet 4.6:
import Anthropic from "@anthropic-ai/sdk";
import { encode as toToon } from "@toon-format/toon";
async function analyzeData(records: any[]) {
const toonData = toToon({ records });
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6-20250514",
max_tokens: 1024,
messages: [{
role: "user",
content: `Проанализируй эти данные и найди аномалии:\n\n${toonData}`
}]
});
return response.content[0].text;
}
В обоих примерах интеграция сводится к одной строке кодирования toon_format.encode(...) или toToon(...). Архитектуру сервисов, логирование и контракты между микросервисами можно оставить на JSON, а TOON использовать только на границе с LLM.
Что попробовать прямо сейчас
- Найдите самый тяжёлый по токенам промпт в вашем проекте: RAG, аналитика, ассистент для логов.
- Сохраните входные данные в JSON и прогоните через токенизатор.
- Закодируйте те же данные в TOON и посчитайте токены ещё раз.
- Подставьте свои объёмы запросов и тарифы GPT-4o, GPT-4.1, Claude Sonnet 4.6 или Claude Opus 4.6 и посчитайте годовую экономию.
Если у вас есть длинные массивы однотипных объектов, TOON даёт простой способ уменьшить чек за LLM без смены модели и без переработки бизнес-логики.