Сколько токенов съедает сама обвязка агента: Claude Code, Codex, Cline и OpenCode на одной задаче
Когда агент решает задачу, счёт растёт не только от вашего кода и вопросов. На каждом шаге в модель снова уходят системный промпт самого агента и описания всех его инструментов, и это часто весит больше, чем ваши файлы. Мы прогнали одну и ту же задачу на одном проекте и одной модели, anthropic/claude-sonnet-5.5, в четырёх агентах: Claude Code, Codex, Cline и OpenCode. Все запросы шли через RouterAI, поэтому токены и рубли считались по одному прайсу и по полю usage из ответов API.
Эта статья дополняет сравнение моделей на одном агенте: там менялась модель, здесь модель одна, а меняется агент.
Коротко
- Модель, проект и задача одинаковые, все проверки пройдены, а счёт отличался в 4,6 раза: от 2,49 ₽ (OpenCode) до 11,52 ₽ (Codex) в повторных прогонах.
- На обвязку (системный промпт и описания инструментов) приходится от 73% до 92% байтов запросов за сессию. Один «пустой» запрос стоит от 4,4 тыс. токенов у Cline до 18 тыс. у Claude Code.
- Кэш промпта влияет на счёт сильнее, чем размер обвязки: Claude Code с отключённым кэшем потратил 5,60 ₽ вместо 2,75 ₽.
- Три стандартных MCP-сервера добавили в OpenCode 6,9 тыс. токенов к каждому запросу. В Claude Code те же серверы добавили 0,4 тыс.: он держит схемы инструментов в стороне и подгружает по требованию.
- Через Responses API с моделью Claude мы увидели две странности: MCP-инструменты Codex не доходят до модели, а к каждому запросу добавляется 1,05 ₽. Подробности и оговорки в разделе про то, что мы не смогли измерить честно.
Методика
Дата замеров: 31 октября 2026 года. Окружение: одноразовый контейнер node:24-bookworm, агенты ставились из npm, права и подтверждения отключены флагами, потому что контейнер изолирован.
| Агент | Версия | Как запускали |
|---|---|---|
| Claude Code | 2.1.316 |
claude -p, Anthropic Messages API |
| Codex | 0.166.0 |
codex exec, Responses API |
| Cline | CLI 3.0.74 (пакет cline) |
cline -y, OpenAI-совместимый провайдер |
| OpenCode | 1.19.5 |
opencode run, OpenAI-совместимый провайдер |
Cline здесь консольный, а не расширение для VS Code, поэтому его системный промпт может отличаться от того, что вы видите в редакторе. Kilo Code и Hermes Agent в замеры не вошли.
Модели. Основная: anthropic/claude-sonnet-5.5. Для сравнения: anthropic/claude-haiku-5.5. Цены из каталога RouterAI на 31 октября 2026 года, ₽ за 1M токенов: Sonnet 5.5 — вход 209,45, выход 1047,24, чтение из кэша 10,47, запись в кэш 261,81; Haiku 5.5 — 10,47, 52,36, 1,05 и 13,09. Актуальные значения смотрите на карточках моделей.
Проект и задача. Демо-проект Invoices: три файла на Node.js и тест, 42 строки. Промпт одинаков для всех агентов:
В этом Node.js-проекте добавь в invoices.js функцию overdueInvoices(), которая возвращает счета со статусом overdue, экспортируй её и добавь тест в invoices.test.js. Запусти node --test и убедись, что тесты проходят. В конце ответь одной фразой.
Результат считается верным, если node --test проходит и overdueInvoices() возвращает ровно один счёт, с id 3. Качество кода мы не оценивали, только эту проверку.
Как считали. Между агентом и RouterAI стоял прозрачный прокси. Он пересылал запросы без изменений и записывал для каждого из них размер тела, число инструментов, размер системного промпта и поле usage из ответа. Один запрос к модели считаем одним шагом. Цену задачи берём как разницу баланса до и после (GET /api/v1/credits); для Codex и Cline она совпала с суммой usage.cost из ответов. Sonnet прогонялся по три раза на агента, Haiku по два, везде берём медиану. Код прокси, скрипт прогона и сводки приведены в конце статьи.
Результаты
Задача на Sonnet 5.5, медиана трёх повторов; во всех 12 прогонах проверка прошла.
| Агент | Запросов | Вход за задачу, токенов | Выход, токенов | Цена в повторах, ₽ | Цена первого прогона, ₽ | Время, с | Обвязка в запросах |
|---|---|---|---|---|---|---|---|
| Claude Code | 6 | ≈114 тыс. (оценка) | 1 131 | 2,75 | 7,08 | 25,7 | 92% |
| Codex | 3 | 37 378 | 522 | 11,52 | 11,53 | 16,5 | 90% |
| Cline | 7 | ≈40 тыс. (оценка) | 1 291 | 4,24 | 8,60 | 30,7 | 73% |
| OpenCode | 7 | ≈70 тыс. (оценка) | 1 481 | 2,49 | 5,44 | 27,6 | 90% |
Как читать таблицу:
-
«Вход за задачу» считается по размеру тела всех запросов, делённому на число байтов на токен. Этот коэффициент мы сняли с первого запроса каждого агента, где
usageточен: от 2,55 до 3,36 байта на токен. Для Codex это значение прямо изusage. Почему не изusageу остальных, объясняем ниже. - «Обвязка в запросах» — доля системного промпта и описаний инструментов в байтах тела запросов за всю сессию. Остальное — история диалога, файлы и результаты команд. У Cline доля ниже, потому что у него самая маленькая обвязка, а история за семь шагов выросла с 13,8 до 22,3 КБ.
- Первый прогон и повторы. Системный промпт и инструменты одинаковы от запуска к запуску, и провайдер кэширует эту часть. В первом, холодном прогоне те же задачи стоили 7,08 / 11,53 / 8,60 / 5,44 ₽, в повторах — 2,75 / 11,52 / 4,24 / 2,49 ₽. У Codex цена не менялась ни в одном прогоне: попаданий в кэш мы у него не увидели.
- OpenCode делает семь запросов, один из них служебный, без инструментов, на 0,8 тыс. токенов.
Больше всего вход у Claude Code, а в повторах он вдвое дешевле Cline, у которого вход втрое меньше. Больше всего платит Codex, хотя шагов у него меньше всех. Размер обвязки сам по себе счёт не объясняет, дальше разберём почему.
В других замерах порядок тот же. Systima в июле 2026 года на Claude Sonnet 4.5 насчитала около 33 тыс. токенов в первом запросе Claude Code против около 7 тыс. у OpenCode. Agentic Control Plane в сентябре сравнили первые запросы по байтам: 68 КБ у Claude Code, 37 КБ у Codex, 29 КБ у OpenCode. У нас те же агенты в более новых версиях: 46, 39 и 31 КБ, и 13,5 КБ у Cline.
Сколько стоит «пустой» запрос
Чтобы отделить обвязку от задачи, мы просили агента ответить одним словом: «готово». Это первый запрос с холодным кэшем, токены здесь точные.
| Агент | Токенов в запросе | Инструментов | Схемы инструментов | Системный промпт | Цена, ₽ |
|---|---|---|---|---|---|
| Claude Code | 18 029 | 12 | 29,5 КБ | 15,0 КБ | 3,7 |
| Codex | 11 744 | 9 | 17,3 КБ | 19,8 КБ | 3,5 |
| OpenCode | 10 507 | 10 | 20,8 КБ | 9,5 КБ | 2,9 |
| Cline | 4 388 | 4 | 8,4 КБ | 4,6 КБ | 2,0 (два запроса) |
Токены Claude Code измерены с DISABLE_PROMPT_CACHING=1, иначе часть запроса уходит в запись кэша и считается отдельно. Для Sonnet 5.5 один такой запрос в 18 тыс. токенов по цене входа 209,45 ₽ за 1M даёт 18 029 × 209,45 / 1 000 000 = 3,78 ₽. Мы заплатили 3,78 ₽. Та же обвязка при чтении из кэша (10,47 ₽ за 1M) стоила бы около 0,19 ₽: поэтому кэш и важнее размера.
Что убирается и что нет
Те же замеры для Claude Code с разными флагами (первый запрос без кэша, токены из usage):
| Конфигурация | Токенов в запросе | Цена слова «готово», ₽ |
|---|---|---|
| По умолчанию | 18 029 | 3,78 |
+ 3 MCP-сервера (filesystem, memory, sequential-thinking) |
18 434 | 3,87 |
--disable-slash-commands |
21 696 | 4,55 |
--tools=Bash,Read,Edit |
3 359 | 0,72 |
--bare |
2 282 | 0,48 |
Три вывода из этой таблицы:
-
Ограничение набора инструментов даёт минус 81% токенов первого запроса (
--tools=Bash,Read,Edit). Режим--bareпо справкеclaude --helpотключает хуки и другие необязательные части и урезает запрос до 2,3 тыс. токенов. Задача с--bareпрошла проверку все три раза. -
--disable-slash-commandsзапрос не облегчил, а утяжелил: схемы инструментов выросли с 29,5 до 44,7 КБ. Мы измерили это на 2.1.316 и не проверяли причину. Не выключайте скиллы «для экономии», не проверив на своём агенте. -
MCP в Claude Code почти ничего не добавил: в теле запроса остались только имена 24 инструментов, а схемы подгружаются через
ToolSearch. В OpenCode такие же три сервера добавили 27 инструментов и подняли первый запрос с 10 507 до 17 402 токенов, то есть на 66%. Для Cline мы MCP не измеряли.
В повторных прогонах основной задачи с включённым кэшем выигрыш скромнее: Claude Code с тремя инструментами — 2,51 ₽ вместо 2,75 ₽, в режиме --bare — 2,12 ₽ и 13,8 с вместо 25,7 с.
Что не удалось измерить честно
usage занижен при попадании в кэш. На POST /api/v1/messages мы отправили три одинаковых запроса с cache_control. Первый вернул cache_creation_input_tokens: 8494, а второй и третий — cache_read_input_tokens: 37, хотя читали из кэша те же 8,5 тыс. токенов; списали при этом 2,23 ₽, 0,014 ₽ и 0,006 ₽. В агентных прогонах Claude Code на втором шаге input_tokens равен сотням при теле запроса в 46–52 КБ. Похожее мы увидели у OpenCode и Cline на тёплом кэше: в первом прогоне usage у OpenCode показывал 62 тыс. токенов, в повторе с тем же размером запросов — 16 тыс. Через Chat Completions и Responses на холодных запросах usage корректен. Поэтому токены за задачу мы оценили по размеру запросов, а цену взяли по балансу.
Codex и MCP. Codex при включённых MCP-серверах прислал 15 инструментов, четыре из них — группы (namespace): три сервера и multi_agent_v1. Мы спросили у самой модели, какие инструменты она видит. anthropic/claude-sonnet-5.5 перечислила 11 — группы до неё не дошли. Тот же набор на openai/gpt-5.4-mini дошёл полностью, включая mcp__filesystem.read_file. Поэтому «+610 токенов от MCP в Codex» в наших замерах означает не дешёвые MCP, а то, что инструменты потерялись по дороге. Если вы запускаете Codex с моделью Claude, проверьте, видит ли она ваши MCP-инструменты.
Доплата 1,05 ₽ за запрос в Codex. Расхождение с ценой токенов у Codex постоянно: 1,0475 ₽ на каждый запрос, ровно столько стоит web_search в каталоге. Мы нашли условие: доплата появляется, когда в запросе одновременно есть web_search и любая группа инструментов; с функциями и web_search по отдельности её нет. За три шага задачи это 3,14 ₽ из 11,52 ₽, то есть 27%. Отключение web_search в Codex проблему не решило: агент перестал выполнять задачу (3 из 3 прогонов не прошли проверку). Мы фиксируем это как наблюдение за конкретной связкой Codex, Responses API и Claude, причину нужно уточнять у RouterAI.
Малая выборка. Три повтора на Sonnet и два на Haiku, одна задача, проект на 42 строки. На большой кодовой базе история диалога вырастет, и доля обвязки упадёт. Разброс в повторах заметный: у Claude Code от 2,62 до 4,19 ₽, у Cline от 3,54 до 5,16 ₽, у OpenCode от 2,41 до 2,55 ₽.
Модель или обвязка
Те же четыре агента на anthropic/claude-haiku-5.5, медиана двух прогонов, все 8 проверок прошли:
| Агент | Sonnet 5.5, ₽ | Haiku 5.5, ₽ | Запросов на Haiku | Время на Haiku, с |
|---|---|---|---|---|
| Claude Code | 2,75 | 0,41 | 3 | 10,3 |
| Codex | 11,52 | 3,59 | 3 | 16,8 |
| Cline | 4,24 | 0,29 | 3,5 | 11,1 |
| OpenCode | 2,49 | 0,27 | 5 | 17,8 |
Разница между агентами на Haiku достигает 13 раз (0,27 против 3,59 ₽), на Sonnet — 4,6 раза. Из 3,59 ₽ у Codex 3,14 ₽ приходится на ту самую доплату за web_search, токены стоят около 0,45 ₽. Поэтому Sonnet в OpenCode (2,49 ₽) и в Claude Code (2,75 ₽) обошёлся дешевле, чем Haiku в Codex (3,59 ₽).
Это ровно то, что вы получаете, не глядя на агента. Но обобщать осторожно: дешёвая модель в нашей выборке выигрывает в каждом агенте, а «дорогая в бережливой обвязке дешевле дешёвой в жирной» верно только для пары с Codex, где виновата не размер контекста, а доплата.
Практические выводы
-
Ограничьте инструменты. В Claude Code это
--tools=Bash,Read,Editили--bare. Для задач без поиска по вебу, подагентов и планировщика остальные инструменты просто лежат в каждом запросе. Проверяйте результат по прокси: например,--disable-slash-commandsу нас сделал хуже. -
Не подключайте MCP-серверы «на всякий случай». В OpenCode три сервера добавили 27 инструментов и 6,9 тыс. токенов к каждому запросу. У каждого сервера в секции
mcpконфига OpenCode есть полеenabled: выключайте то, чем не пользуетесь в этой сессии. Claude Code прячет схемы заToolSearch, там цена ниже, но имена всё равно уходят в запрос. -
Следите за кэшем. Смотрите в
usageполеcached_tokens(илиcache_read_input_tokens). Claude Code с кэшем стоил 2,75 ₽ против 5,60 ₽ без него, Sonnet 5.5 читает кэш за 10,47 ₽ вместо 209,45 ₽ за 1M. Если у агентаcached_tokensвсегда ноль, он, скорее всего, не размечает кэш. Но помните: на тёплом кэшеusageзанижает токены, цену проверяйте по балансу. Как устроено кэширование, описано в глоссарии. - Одна задача — одна сессия. История растёт с каждым шагом: от первого шага к последнему тело запроса выросло у Cline на 62% (7 шагов), у OpenCode на 22% (7 шагов), у Claude Code на 5% (3 шага), у Codex на 8% (3 шага). Число шагов в этих прогонах разное, поэтому цифры сравнивайте осторожно.
- Выбирайте агента и модель вместе. Прогоните свою типичную задачу в двух-трёх агентах на одной модели, прежде чем сравнивать модели между собой.
Как заранее оценить бюджет сессии
Для одного запроса:
цена ≈ токены_из_кэша × цена_чтения + новые_токены × цена_входа + выход × цена_выхода
Один раз за сессию к этому добавляется запись префикса в кэш (у Sonnet 5.5 она дороже входа: 261,81 против 209,45 ₽ за 1M). Сессия — сумма по шагам. Практический порядок такой:
- Запустите агента на пустой задаче («ответь одним словом») через прокси или посмотрите
usageпервого запроса: это ваш базовый размер обвязкиH. - Прогоните одну типичную задачу и запишите число шагов
Nи размер запроса на последнем шаге. - Умножьте: без кэша счёт сессии порядка
N × (H + средняя история) × цена_входа, с кэшем та же частьHдешевле в 10–20 раз.
После сессии разницу с планом покажет GET https://routerai.ru/api/v1/credits: этот способ описан в статье про Claude Code и используется в статус-баре RouterAI.
Как повторить замеры
Нужны Docker, Node.js 22+ и ключ из личного кабинета. Агенты подключаются к RouterAI по инструкциям: Claude Code, Codex, Cline, OpenCode. Отличается только базовый адрес: он указывает на прокси, а первый сегмент пути (/claude, /codex, /cline, /opencode) прокси отрезает и пересылает остальное на routerai.ru.
npm install -g @anthropic-ai/claude-code@2.1.316 @openai/codex@0.166.0 opencode-ai@1.19.5 cline@3.0.74
export ROUTERAI_API_KEY=sk-xxxxxxxxxxxxxxxx
export MODEL=anthropic/claude-sonnet-5.5
# Claude Code: переменные окружения (их выставляет bench.sh)
# ANTHROPIC_BASE_URL=http://127.0.0.1:8787/claude/api
# ANTHROPIC_AUTH_TOKEN=$ROUTERAI_API_KEY ANTHROPIC_API_KEY=
# Cline: один раз настроить провайдера
cline auth openai -k "$ROUTERAI_API_KEY" -m "$MODEL" -b http://127.0.0.1:8787/cline/api/v1
~/.codex/config.toml для Codex:
model_provider = "routerai"
model = "anthropic/claude-sonnet-5.5"
[model_providers.routerai]
name = "RouterAI"
base_url = "http://127.0.0.1:8787/codex/api/v1"
wire_api = "responses"
supports_websockets = false
[model_providers.routerai.auth]
command = "sh"
args = ["-c", "echo $ROUTERAI_API_KEY"]
~/.config/opencode/opencode.json для OpenCode:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"routerai": {
"npm": "@ai-sdk/openai-compatible",
"name": "RouterAI",
"options": {
"baseURL": "http://127.0.0.1:8787/opencode/api/v1",
"apiKey": "{env:ROUTERAI_API_KEY}"
},
"models": { "anthropic/claude-sonnet-5.5": { "name": "Sonnet 5.5" } }
}
}
}
Прокси proxy.mjs (запуск: LOG_DIR=/work/logs node proxy.mjs; с SAVE_BODY=1 сохраняет тела запросов):
import http from 'node:http';
import https from 'node:https';
import fs from 'node:fs';
const LOG = process.env.LOG_DIR || '/work/logs';
const NL = String.fromCharCode(10);
fs.mkdirSync(LOG, { recursive: true });
function usageOf(text) {
const u = {};
const objs = [];
if (text.trimStart().startsWith('{')) {
try { objs.push(JSON.parse(text)); } catch {}
} else {
for (const l of text.split(NL)) {
if (!l.startsWith('data:')) continue;
const s = l.slice(5).trim();
if (!s || s === '[DONE]') continue;
try { objs.push(JSON.parse(s)); } catch {}
}
}
u.raw = objs.map(o => o.usage || (o.message && o.message.usage) || (o.response && o.response.usage)).filter(Boolean).pop();
return u;
}
let CUR = '';
http.createServer((req, res) => {
if (req.url === '/health') { res.writeHead(200); res.end('ok'); return; }
if (req.url.startsWith('/_label/')) { CUR = req.url.slice(8); res.writeHead(200); res.end(CUR); return; }
const chunks = [];
req.on('data', c => chunks.push(c));
req.on('end', () => {
const agent = req.url.split('/')[1];
const path = req.url.slice(agent.length + 1);
const label = CUR || agent;
const body = Buffer.concat(chunks);
let info = { bodyBytes: body.length };
try {
const j = JSON.parse(body.toString());
const msgs = j.messages || j.input || [];
const sysMsgs = Array.isArray(msgs) ? msgs.filter(m => m.role === 'system' || m.role === 'developer') : [];
info = { bodyBytes: body.length, model: j.model, stream: !!j.stream, tools: (j.tools || []).length, toolsBytes: JSON.stringify(j.tools || []).length, sysBytes: JSON.stringify([j.system || '', j.instructions || '', sysMsgs]).length, msgs: Array.isArray(msgs) ? msgs.length : 1 };
} catch {}
if (process.env.SAVE_BODY) fs.writeFileSync(`${LOG}/${label}-req-${Date.now()}.json`, body);
const headers = { ...req.headers, host: 'routerai.ru', 'accept-encoding': 'identity', 'content-length': body.length };
delete headers.connection;
const t0 = Date.now();
const up = https.request({ host: 'routerai.ru', path, method: req.method, headers }, r => {
res.writeHead(r.statusCode, r.headers);
r.setEncoding('utf8');
let text = '';
r.on('data', c => { text += c; res.write(c); });
r.on('end', () => {
res.end();
const rec = { agent, ts: new Date().toISOString(), path, status: r.statusCode, ms: Date.now() - t0, ...info, usage: usageOf(text) };
if (r.statusCode !== 200) rec.err = text.slice(0, 300);
fs.appendFileSync(`${LOG}/${label}.jsonl`, JSON.stringify(rec) + NL);
});
});
up.on('error', e => { res.writeHead(502); res.end(String(e)); });
up.end(body);
});
}).listen(8787, '127.0.0.1');
Скрипт прогона bench.sh <агент> <метка>; эталонный проект должен лежать в /work/repo-orig:
#!/usr/bin/env bash
set -u
AGENT=$1; LABEL=$2; shift 2
M=${MODEL:?set MODEL}
PROXY=http://127.0.0.1:8787
PROMPT=${PROMPT:-'В этом Node.js-проекте добавь в invoices.js функцию overdueInvoices(), которая возвращает счета со статусом overdue, экспортируй её и добавь тест в invoices.test.js. Запусти node --test и убедись, что тесты проходят. В конце ответь одной фразой.'}
balance() {
curl -s -m 10 -H "Authorization: Bearer $ROUTERAI_API_KEY" https://routerai.ru/api/v1/credits |
python3 -c 'import json,sys; d=json.load(sys.stdin)["data"]; print(d.get("credits", d.get("total_credits", 0)))' 2>/dev/null
}
WORK=/work/runs/$LABEL
rm -rf "$WORK" && mkdir -p "$WORK" && cp -r /work/repo-orig "$WORK/repo" && cd "$WORK/repo" || exit 1
curl -s "$PROXY/_label/$LABEL" > /dev/null
B0=$(balance); B0=${B0:-0}
T0=$(date +%s.%N)
case $AGENT in
claude)
ANTHROPIC_BASE_URL=$PROXY/claude/api ANTHROPIC_AUTH_TOKEN=$ROUTERAI_API_KEY ANTHROPIC_API_KEY= \
ANTHROPIC_DEFAULT_SONNET_MODEL=$M ANTHROPIC_DEFAULT_HAIKU_MODEL=$M ANTHROPIC_DEFAULT_OPUS_MODEL=$M \
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 IS_SANDBOX=1 \
timeout 900 claude -p "$PROMPT" --model "$M" --dangerously-skip-permissions --no-session-persistence \
--output-format json "$@" > "$WORK/out.txt" 2>&1 ;;
codex)
timeout 900 codex exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox -m "$M" "$@" "$PROMPT" > "$WORK/out.txt" 2>&1 ;;
opencode)
timeout 900 opencode run -m "routerai/$M" --dir "$WORK/repo" "$@" "$PROMPT" > "$WORK/out.txt" 2>&1 ;;
cline)
timeout 900 cline -P openai -m "$M" -y -c "$WORK/repo" "$@" "$PROMPT" > "$WORK/out.txt" 2>&1 ;;
esac
T1=$(date +%s.%N)
sleep 3
B1=$(balance); B1=${B1:-0}
PASS=n/a
if [ "${CHECK:-1}" = 1 ]; then PASS=no
if node --test > "$WORK/test.txt" 2>&1 &&
node -e 'const r=require("./invoices").overdueInvoices(); process.exit(r.length===1&&r[0].id===3?0:1)' 2> /dev/null; then PASS=yes; fi; fi
python3 -c "import json,sys; print(json.dumps({'label':'$LABEL','agent':'$AGENT','seconds':round($T1-$T0,1),'balance_delta':round(float('$B0')-float('$B1'),2),'check':'$PASS'}))" >> /work/logs/_runs.jsonl
tail -n 1 /work/logs/_runs.jsonl
Дополнительные флаги агента передаются третьим аргументом и дальше, например bench.sh claude trimmed --tools=Bash,Read,Edit. Сводка по логам, python3 summary.py <метка>:
import json, sys
for label in sys.argv[1:]:
rows = [json.loads(l) for l in open(f"/work/logs/{label}.jsonl")]
rows = [r for r in rows if r.get("status") == 200 and r.get("model")]
body = sum(r["bodyBytes"] for r in rows)
harness = sum(r["toolsBytes"] + r["sysBytes"] for r in rows)
usage = [r["usage"].get("raw") or {} for r in rows]
prompt = sum(u.get("prompt_tokens", u.get("input_tokens", 0)) for u in usage)
out = sum(u.get("completion_tokens", u.get("output_tokens", 0)) for u in usage)
cost = sum(u.get("cost") or 0 for u in usage)
print(f"{label}: шагов {len(rows)}, тело запросов {body} Б, "
f"обвязка {harness / body:.0%}, prompt по usage {prompt}, "
f"выход {out}, cost по usage {cost:.2f} ₽")
Три предостережения при повторе. Во-первых, prompt по usage у агентов с тёплым кэшем занижен, как описано выше: сверяйте с изменением баланса. Во-вторых, замеры нужно делать по очереди, а не параллельно, иначе дельта баланса смешает разные прогоны. В-третьих, эндпоинт /credits изредка отвечает не JSON: в двух наших прогонах дельта вышла абсурдной (около 53 тыс. ₽), мы такие прогоны исключили.
Итог
На одной модели и одной задаче агент менял счёт в 4,6 раза, а на Haiku — в 13 раз. Но объяснение не сводится к «у кого обвязка толще»: у Claude Code она самая тяжёлая, а в повторах он вдвое дешевле Cline, потому что префикс читается из кэша. Практичнее всего оказались три шага: сократить набор инструментов, не держать лишние MCP-серверы и проверять в usage, что кэш действительно срабатывает. Цены, usage и баланс в RouterAI в рублях, работает из России без VPN, поэтому такие замеры можно повторить на своих задачах за вечер.
Все модели из статьи доступны в каталоге RouterAI: оплата в рублях, без VPN. Создайте аккаунт, прогоните скрипт на своей задаче и сравните агентов по реальному счёту.