Зачем это нужно
Каждый запрос к нейросети через API стоит денег: цена считается не «за сообщение», а по числу токенов — кусочков текста размером примерно в ¾ слова. Если запросов несколько подряд, например когда агент сначала разбирает вопрос, потом ищет информацию и затем формирует ответ, прикинуть общую стоимость на глаз почти нереально.
CostTracker решает эту задачу — одной строкой после каждого запроса вы сообщаете ему число потраченных токенов, а он сам считает стоимость каждого шага и в конце показывает таблицу с итогом.
Как это устроено
Инструмент не вмешивается в ваш запрос к нейросети и не заменяет его. Он ничего не знает про Anthropic, OpenAI или любой другой сервис и не пытается быть посредником между вами и API. Вы вызываете модель ровно так, как вызывали раньше, а сразу после ответа одной строкой сообщаете трекеру, сколько токенов потрачено. Это значение сервис и так возвращает вместе с ответом.
Что нужно, чтобы начать
- Python (подойдёт практически любая версия, начиная с 3.7)
- Свой код, который уже обращается к нейросети через Anthropic, OpenAI или любой другой сервис
Если своего кода с обращением к нейросети пока нет, можно всё равно скопировать инструмент и попробовать на готовом примере из шага 5, чтобы увидеть, как это работает.
1 Создайте папку и два файла
В своём проекте создайте папку, например cost_tracker, и положите в неё два файла: cost_tracker.py и pricing.json.
2 Скопируйте это в файл cost_tracker.py
"""
CostTracker — простой счётчик стоимости вызовов LLM.
"""
import json
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional
PRICING_FILE = Path(__file__).parent / "pricing.json"
@dataclass
class CallRecord:
label: str
model: str
input_tokens: int
output_tokens: int
cost: Optional[float] # None, если цена модели неизвестна
class CostTracker:
def __init__(self, pricing_file: Path = PRICING_FILE):
self._pricing = self._load_pricing(pricing_file)
self._calls: List[CallRecord] = []
@staticmethod
def _load_pricing(path: Path) -> dict:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
data.pop("_meta", None)
return data
def log_call(
self,
model: str,
input_tokens: int,
output_tokens: int,
label: str = "",
) -> Optional[float]:
"""Записывает один вызов модели и возвращает его стоимость (или None)."""
cost = self._calculate_cost(model, input_tokens, output_tokens)
record = CallRecord(
label=label or f"вызов {len(self._calls) + 1}",
model=model,
input_tokens=input_tokens,
output_tokens=output_tokens,
cost=cost,
)
self._calls.append(record)
return cost
def _calculate_cost(self, model, input_tokens, output_tokens):
price = self._pricing.get(model)
if price is None:
return None
input_cost = input_tokens / 1_000_000 * price["input"]
output_cost = output_tokens / 1_000_000 * price["output"]
return input_cost + output_cost
def total_cost(self) -> float:
return sum(c.cost for c in self._calls if c.cost is not None)
@staticmethod
def _fit(text: str, width: int) -> str:
if len(text) > width:
return text[: width - 1] + "…"
return text.ljust(width)
def report(self) -> str:
"""Читаемый построчный отчёт по шагам + итог."""
if not self._calls:
return "Пока нет ни одного зафиксированного вызова."
label_w, model_w, tokens_w = 30, 22, 16
header = (
f"{self._fit('Шаг', label_w)}{self._fit('Модель', model_w)}"
f"{self._fit('Токены in/out', tokens_w)}{'Стоимость':>16}"
)
lines = [header, "-" * len(header)]
for c in self._calls:
tokens = f"{c.input_tokens}/{c.output_tokens}"
cost_str = f"${c.cost:.4f}" if c.cost is not None else "цена неизвестна"
lines.append(
f"{self._fit(c.label, label_w)}{self._fit(c.model, model_w)}"
f"{self._fit(tokens, tokens_w)}{cost_str:>16}"
)
lines.append("-" * len(header))
has_unknown = any(c.cost is None for c in self._calls)
total_line = f"ИТОГО: ${self.total_cost():.4f}"
if has_unknown:
total_line += " (шаги с неизвестной ценой не учтены)"
lines.append(total_line)
return "\n".join(lines)
3 Скопируйте это в файл pricing.json
Это справочник цен — во сколько обходится каждая нейросеть.
Как читать эти числа
У каждой модели два числа: input и output.
- input — во сколько обходится то, что вы отправляете нейросети: вопрос, инструкции, документы. Это то, что модель читает.
- output — во сколько обходится ответ нейросети, то есть то, что она написала. Он почти всегда дороже вопроса.
Оба числа — доллары за миллион токенов, как их публикует сам сервис на странице цен. Число токенов считать самим не нужно: сервис возвращает его вместе с каждым ответом, вы просто передаёте это значение трекеру.
{
"_meta": {
"note": "Цены за 1 000 000 токенов, в долларах. Официального API 'текущая цена' ни у одного провайдера нет — эти цифры сверены вручную по документации на дату ниже.",
"checked_on": "2026-07-02",
"sources": [
"https://platform.claude.com/docs/en/about-claude/pricing",
"https://developers.openai.com/api/docs/pricing"
]
},
"claude-opus-4-6": { "input": 5.0, "output": 25.0 },
"claude-sonnet-4-6": { "input": 3.0, "output": 15.0 },
"claude-haiku-4-5": { "input": 1.0, "output": 5.0 },
"gpt-5": { "input": 1.25, "output": 10.0 },
"gpt-5-mini": { "input": 0.25, "output": 2.0 },
"gpt-4.1": { "input": 2.0, "output": 8.0 },
"gpt-4.1-mini": { "input": 0.40, "output": 1.60 },
"gpt-4o-mini": { "input": 0.15, "output": 0.60 },
"local-model": { "input": 0.0, "output": 0.0 }
}
Пример расчёта: один вызов с 1200 входными и 180 выходными токенами на модели claude-sonnet-4-6 ($3 / $15 за миллион):
input_cost = 1200 / 1_000_000 * 3.0 = $0.0036
output_cost = 180 / 1_000_000 * 15.0 = $0.0027
итого = $0.0036 + $0.0027 = $0.0063
Если модели, которую вы используете, нет в списке, ничего страшного: программа не сломается, а в отчёте у этого шага будет написано «цена неизвестна» вместо суммы. Можно дописать свою строку с моделью и её ценой по такому же образцу.
4 Как это использовать в своём коде
Ваш обычный код, где вы обращаетесь к нейросети, менять не нужно. Добавляется только одна строка сразу после ответа: вы берёте из ответа количество потраченных токенов, которое сервис и так возвращает, и передаёте его трекеру.
from cost_tracker import CostTracker
tracker = CostTracker()
# ваш обычный запрос к нейросети — как и раньше, без изменений
response = client.messages.create(...)
# одна новая строка сразу после ответа:
tracker.log_call(
model="claude-sonnet-4-6",
input_tokens=response.usage.input_tokens,
output_tokens=response.usage.output_tokens,
label="разбор вопроса пользователя", # необязательно, просто подпись для отчёта
)
# когда захотите посмотреть, сколько всё стоило:
print(tracker.report())
При работе с OpenAI всё то же самое, только поля с токенами называются иначе:
response = client.chat.completions.create(...)
tracker.log_call(
model="gpt-4.1",
input_tokens=response.usage.prompt_tokens,
output_tokens=response.usage.completion_tokens,
)
tracker.report() можно вызывать где угодно и когда угодно, хоть после каждого ответа, хоть в самом конце. Сам он ничего никуда не выводит и не сохраняет, а печатает то, что вы попросите и когда попросите.
log_call() нужно вставлять после каждого такого обращения, а не один раз в начале. Инструмент не отслеживает ваш код сам: он видит ровно столько шагов, сколько раз вы вызвали log_call(). Если забыть вставить эту строку после одного из вызовов, этот вызов просто не попадёт в отчёт, без какого-либо предупреждения об ошибке.5 Проверьте, что всё работает
Если хотите сразу увидеть, как выглядит результат, не делая реального запроса к нейросети, создайте рядом третий файл try_it.py с таким содержимым:
from cost_tracker import CostTracker
tracker = CostTracker()
tracker.log_call(
model="claude-haiku-4-5",
input_tokens=450,
output_tokens=60,
label="шаг 1: разобрать запрос пользователя",
)
tracker.log_call(
model="claude-sonnet-4-6",
input_tokens=1200,
output_tokens=180,
label="шаг 2: вызвать инструмент поиска",
)
print(tracker.report())
И запустите его в терминале:
python3 try_it.py
Вы увидите готовую таблицу вроде такой:
Шаг Модель Токены in/out Стоимость
------------------------------------------------------------------------------------
шаг 1: разобрать запрос польз…claude-haiku-4-5 450/60 $0.0008
шаг 2: вызвать инструмент пои…claude-sonnet-4-6 1200/180 $0.0063
------------------------------------------------------------------------------------
ИТОГО: $0.0070
Если такая таблица появилась, всё работает. Можно убирать пробный файл и подключать реальные вызовы своей нейросети из шага 4.
Что этот инструмент не делает
- Не вызывает нейросеть сам, а только считает то, что ему передали
- Не знает актуальные цены «из коробки»: справочник приходится обновлять вручную
- Не хранит историю между запусками: это счётчик на время жизни одного объекта
tracker, а не постоянное хранилище - Не отвечает сам на вопросы в чате вроде «посчитай мне цену» — это инструмент для разработчика, а не «скилл» агента. Чтобы агент сам мог вызвать отчёт по просьбе пользователя, нужно отдельно зарегистрировать
report()как один из вызываемых им инструментов через function calling. Сам трекер этого не делает