AI для початківцівПосібник
Обмежте вартість пакету LLM у Python: журнал і жорсткий бюджет
Логуйте токени та вартість кожного виклику API у файл JSONL, зупиняйте пакет на бюджеті й оцінюйте рахунок до запуску. Код Python додається.

На цій сторінці
Ваш скрипт проходить циклом по 200 промптах, ви закриваєте кришку ноутбука, а вранці дізнаєтеся, скільки коштувала ніч. Дві маленькі звички це виправляють. Після кожного виклику API дописуйте один рядок із токенами та вартістю у файл-журнал. Перед кожним викликом порівнюйте поточний підсумок із бюджетом і зупиняйтеся, якщо його досягнуто. Це менше 50 рядків Python, а пробний запуск на 5 промптів покаже вам рахунок ще до початку справжнього запуску.
Що робить запуск Clocktower, що ви можете скопіювати#
Dwarkesh Patel, ведучий Dwarkesh Podcast, запустив 11 моделей одна проти одної у Blood on the Clocktower, 386 ігор згідно з README, і опублікував код, логи та рейтинги (github.com/dwarkeshsp/clocktower, перевірено 8 листопада 2026). Дві деталі з репозиторію варто запозичити. Кожен каталог гри зберігає лог по кожному виклику з токенами та вартістю, а витрати йдуть у games/. Прапорець --budget обмежує загальні витрати; типове значення — $1,000. Сайт бенчмарку додає третій запобіжник: кожна модель отримує однакову стелю у 8,000 токенів на хід (clocktowerbench.com).
Отже: стеля на кожен виклик, лог кожного виклику, обмеження на загальну суму. Турніру потрібні всі три, і вашому нічному запуску теж, лише менші.
Уявіть журнал як стрічку чеків у касі магазину. Кожен продаж друкує рядок; менеджер може будь-коли підсумувати стрічку й знає, коли денна виручка вичерпана.
Журнал: один файл, один рядок на виклик#
Ось усе целиком. Він використовує пакет openai, налаштований на CheapTokens (https:/, дивіться документацію щодо ключа та ідентифікаторів моделей).
import json
import os
from pathlib import Path
from openai import OpenAI
client = OpenAI(
base_url='https://api.cheaptokens.dev/v1',
api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'
PRICE_IN, PRICE_OUT = 1.00, 2.00 # USD per 1M tokens: example, substitute current ones
class BudgetExceeded(Exception):
pass
class Ledger:
def __init__(self, path, budget_usd):
self.path = Path(path)
self.budget = budget_usd
self.rows = []
if self.path.exists():
with self.path.open() as f:
self.rows = [json.loads(line) for line in f if line.strip()]
@property
def spent(self):
return sum(r['cost_usd'] for r in self.rows)
def done(self):
return {r['unit'] for r in self.rows}
def check(self, unit):
if self.spent >= self.budget:
raise BudgetExceeded(
f'stopped before {unit}: spent {self.spent:.4f} of {self.budget} USD'
)
def add(self, unit, usage, finish_reason):
cost = (usage.prompt_tokens * PRICE_IN
+ usage.completion_tokens * PRICE_OUT) / 1_000_000
row = {
'unit': unit,
'model': MODEL,
'prompt_tokens': usage.prompt_tokens,
'completion_tokens': usage.completion_tokens,
'finish_reason': finish_reason,
'cost_usd': round(cost, 6),
}
self.rows.append(row)
with self.path.open('a') as f:
f.write(json.dumps(row) + '\n')
def ask(ledger, unit, prompt, max_tokens=400):
ledger.check(unit)
resp = client.chat.completions.create(
model=MODEL,
messages=[{'role': 'user', 'content': prompt}],
max_tokens=max_tokens,
)
ledger.add(unit, resp.usage, resp.choices[0].finish_reason)
return resp.choices[0].message.contentКожен виклик проходить через ті самі чотири кроки:

Три рішення тут є навмисними.
- Один JSON-об'єкт на рядок (JSONL), відкритий у режимі дописування. Якщо скрипт впаде на виклику 130, виклики з 1 по 129 вже на диску.
- Підсумок береться з файлу, а не зі змінної. Перезапустіть скрипт, і обмеження все ще діє, бо журнал пам'ятає, скільки витратив попередній запуск.
finish_reasonлогується поруч із токенами. Рядок, що кажеlength, означає, що відповідь досягла вашогоmax_tokensі була обрізана. Вам захочеться знати це пізніше.
Дві константи PRICE_ — це заповнювачі. Візьміть поточні числа з таблиці цін. Для DeepSeek-V4-Flash на момент, коли ви це читаєте, це $0.015 за 1M вхідних токенів і $0.10 за 1M вихідних токенів. Змініть MODEL на zai- або MiniMaxAI/ та дві константи, і той самий журнал працюватиме для них.
Цикл пакетної обробки: пропускайте зроблене, зупиняйтеся на обмеженні#
Цикл короткий. Скажімо, ваші промпти лежать у словнику з ключем за ідентифікатором одиниці — назвою однієї одиниці роботи, наприклад doc-001:
prompts = {
'doc-001': 'Summarize this ticket in one sentence: ...',
'doc-002': 'Summarize this ticket in one sentence: ...',
# ... your 200 prompts
}
ledger = Ledger('ledger.jsonl', budget_usd=0.50)
already = ledger.done()
for unit, prompt in prompts.items():
if unit in already:
continue
try:
answer = ask(ledger, unit, prompt)
except BudgetExceeded as e:
print(e)
break
Path(f'out-{unit}.txt').write_text(answer)Запустіть його, натисніть Ctrl+C на півдорозі, запустіть знову: завершені одиниці пропускаються, тож журнал слугує і вашим файлом прогресу. Дозвольте бюджету вичерпатися, і ви отримаєте один рядок замість сюрпризу, щось на кшталт stopped before doc-.
Зверніть увагу на слово перед. Перевірка відбувається до кожного виклику, тож останній виклик може трохи перевищити бюджет. Наскільки — обмежено: один виклик не може коштувати більше, ніж ваш найдовший промпт плюс max_tokens. Для промпту на 1,500 токенів і max_tokens=400 це щонайбільше $0.000063. Ось чому кожен виклик в обгортці має max_tokens: це перетворює необмежений ризик на число, навколо якого можна планувати.

Оцініть рахунок, перш ніж натиснути запуск#
Тепер інша половина: знати вартість до запуску. Проженіть перші п'ять промптів через ask() з невеликим бюджетом і прочитайте журнал. Скажімо, п'ять рядків у середньому мають 1,500 вхідних токенів і 400 вихідних токенів. Це ваші числа для всього пакету.
Для 200 промптів це 200 × 1,500 = 300,000 вхідних токенів і 200 × 400 = 80,000 вихідних токенів. Ось це навантаження за живими цінами:
| Модель | Вхід $/1М | Вихід $/1М | За запит | 200 запитів | OpenRouter, 200 запитів |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.000046 | $0.0092 | $0.0167 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.000063 | $0.0125 | $0.0322 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.00014 | $0.027 | $0.324 |
1 500 вхідних і 400 вихідних токенів на запит. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.
У таблиці є також стовпець OpenRouter. Якщо рахувати вхідні та вихідні разом, DeepSeek-V4-Flash виходить дешевшим за OpenRouter для цього навантаження, і саме ця сума — те, що пакет реально сплачує.
Промпти різняться за довжиною, тож п'ять зразків — це здогад, а не обіцянка. Використовуйте найдовший із п'яти, коли хочете безпечну верхню межу, а решту хай ловить обмеження бюджету. Хочете порівняти дві моделі на тих самих 200 промптах? Дайте кожній моделі власний файл-журнал, наприклад Ledger(f'ledger-, і два підсумки потім стоятимуть поруч.
Спробуйте власний пакет тут: задайте токени на запит із вашого зразка та скільки запитів ви плануєте.
Калькулятор вартості
Ваш пакет: токени з вашого зразка на 5 промптів
Вхідні токени на запит: 1 500 · Вихідні токени на запит: 400 · Запитів на день: 200
| Модель | За день | За місяць |
|---|---|---|
| GLM-5.3-Flash$0.02$0.04 | $0.0092 | $0.276 |
| DeepSeek-V4-Flash$0.015$0.10 | $0.0125 | $0.375 |
| MiniMax-M2.7$0.05$0.15 | $0.027 | $0.81 |
Докладніше про підрахунок токенів перед надсиланням дивіться у підрахунок токенів за допомогою tiktoken.
Коли рахунок більший, ніж очікувалося: читайте журнал#
Третій сценарій читача — найнеприємніший. Запуск агента йде не так і зациклюється, а дашборд показує лише підсумок. Журнал показує, де саме. Відсортуйте рядки за вартістю:
rows = [json.loads(line) for line in open('ledger.jsonl')]
for r in sorted(rows, key=lambda r: r['cost_usd'], reverse=True)[:5]:
print(r['unit'], r['prompt_tokens'], r['completion_tokens'], r['finish_reason'])Для агента називайте одиницю за кроком (step-17), щоб бачити форму запуску. Кілька патернів з'являються знову й знову; зіставте кожен із його звичайною причиною:
З'єднайте пари
Зіставте кожен патерн журналу з його звичайною причиною
Торкніться елемента ліворуч, потім його пари
- prompt_tokens трохи зростає на кожному рядкуЦикл повторно надсилає всю історію на кожному кроці
- completion_tokens дорівнює max_tokens, а finish_reason — lengthВідповіді обрізаються на вашій стелі, і ви платите за обрізаний текст
- Та сама одиниця з'являється багато разів підрядЦикл повторних спроб або агент, що повторює один крок
- Один рядок коштує набагато більше за всі іншіОдин завеликий вхід, наприклад цілий файл, вставлений у промпт
Кожен патерн видно в журналі задовго до рахунку. Перший висвітлено в чому агент перечитує свою історію.
Якщо ви також хочете витрати за проєктом і сповіщення поза скриптом, цей посібник зі сповіщень про бюджет за проєктом охоплює цю сторону. Журнал тут — це частина, що живе у вашому коді й може сказати «стоп».
Перед запуском#
Чек-лист
Перед першим справжнім запуском
Розмір контексту та ліміт відповіді DeepSeek-V4-Flash, що стануть у пригоді, коли ви обираєте max_tokens, є на його сторінці: DeepSeek-V4-Flash на CheapTokens.



