Назад до блогу

AI для початківцівПосібник

Обмежте вартість пакету LLM у Python: журнал і жорсткий бюджет

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

Автор: CheapTokens TeamОпубліковано 5 хв читання

Термінал із пакетним запуском, що зупиняється при досягненні бюджету
На цій сторінці
  1. Що робить запуск Clocktower, що ви можете скопіювати
  2. Журнал: один файл, один рядок на виклик
  3. Цикл пакетної обробки: пропускайте зроблене, зупиняйтеся на обмеженні
  4. Оцініть рахунок, перш ніж натиснути запуск
  5. Коли рахунок більший, ніж очікувалося: читайте журнал
  6. Перед запуском

Ваш скрипт проходить циклом по 200 промптах, ви закриваєте кришку ноутбука, а вранці дізнаєтеся, скільки коштувала ніч. Дві маленькі звички це виправляють. Після кожного виклику API дописуйте один рядок із токенами та вартістю у файл-журнал. Перед кожним викликом порівнюйте поточний підсумок із бюджетом і зупиняйтеся, якщо його досягнуто. Це менше 50 рядків Python, а пробний запуск на 5 промптів покаже вам рахунок ще до початку справжнього запуску.

Що робить запуск Clocktower, що ви можете скопіювати#

Dwarkesh Patel, ведучий Dwarkesh Podcast, запустив 11 моделей одна проти одної у Blood on the Clocktower, 386 ігор згідно з README, і опублікував код, логи та рейтинги (github.com/dwarkeshsp/clocktower, перевірено 8 листопада 2026). Дві деталі з репозиторію варто запозичити. Кожен каталог гри зберігає лог по кожному виклику з токенами та вартістю, а витрати йдуть у games/ledger.jsonl. Прапорець --budget обмежує загальні витрати; типове значення — $1,000. Сайт бенчмарку додає третій запобіжник: кожна модель отримує однакову стелю у 8,000 токенів на хід (clocktowerbench.com).

Отже: стеля на кожен виклик, лог кожного виклику, обмеження на загальну суму. Турніру потрібні всі три, і вашому нічному запуску теж, лише менші.

Уявіть журнал як стрічку чеків у касі магазину. Кожен продаж друкує рядок; менеджер може будь-коли підсумувати стрічку й знає, коли денна виручка вичерпана.

Журнал: один файл, один рядок на виклик#

Ось усе целиком. Він використовує пакет openai, налаштований на CheapTokens (https://api.cheaptokens.dev/v1, дивіться документацію щодо ключа та ідентифікаторів моделей).

python
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

Кожен виклик проходить через ті самі чотири кроки:

Чотири кроки навколо кожного виклику API: перевірити бюджет, викликати, прочитати usage, дописати в журнал
Цикл, що стоїть за кожним викликом у обгортці

Три рішення тут є навмисними.

  • Один JSON-об'єкт на рядок (JSONL), відкритий у режимі дописування. Якщо скрипт впаде на виклику 130, виклики з 1 по 129 вже на диску.
  • Підсумок береться з файлу, а не зі змінної. Перезапустіть скрипт, і обмеження все ще діє, бо журнал пам'ятає, скільки витратив попередній запуск.
  • finish_reason логується поруч із токенами. Рядок, що каже length, означає, що відповідь досягла вашого max_tokens і була обрізана. Вам захочеться знати це пізніше.

Дві константи PRICE_ — це заповнювачі. Візьміть поточні числа з таблиці цін. Для DeepSeek-V4-Flash на момент, коли ви це читаєте, це $0.015 за 1M вхідних токенів і $0.10 за 1M вихідних токенів. Змініть MODEL на zai-org/GLM-5.3-Flash або MiniMaxAI/MiniMax-M2.7 та дві константи, і той самий журнал працюватиме для них.

Цикл пакетної обробки: пропускайте зроблене, зупиняйтеся на обмеженні#

Цикл короткий. Скажімо, ваші промпти лежать у словнику з ключем за ідентифікатором одиниці — назвою однієї одиниці роботи, наприклад doc-001:

python
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-142: spent 0.5003 of 0.5 USD.

Зверніть увагу на слово перед. Перевірка відбувається до кожного виклику, тож останній виклик може трохи перевищити бюджет. Наскільки — обмежено: один виклик не може коштувати більше, ніж ваш найдовший промпт плюс 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 вихідних токенів. Ось це навантаження за живими цінами:

Модель За запит 200 запитів OpenRouter, 200 запитів
GLM-5.3-Flash $0.000046 $0.0092 $0.0167
DeepSeek-V4-Flash $0.000063 $0.0125 $0.0322
MiniMax-M2.7 $0.00014 $0.027 $0.324

1 500 вхідних і 400 вихідних токенів на запит. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.

У таблиці є також стовпець OpenRouter. Якщо рахувати вхідні та вихідні разом, DeepSeek-V4-Flash виходить дешевшим за OpenRouter для цього навантаження, і саме ця сума — те, що пакет реально сплачує.

Промпти різняться за довжиною, тож п'ять зразків — це здогад, а не обіцянка. Використовуйте найдовший із п'яти, коли хочете безпечну верхню межу, а решту хай ловить обмеження бюджету. Хочете порівняти дві моделі на тих самих 200 промптах? Дайте кожній моделі власний файл-журнал, наприклад Ledger(f'ledger-{MODEL.split("/")[-1]}.jsonl', 0.50), і два підсумки потім стоятимуть поруч.

Спробуйте власний пакет тут: задайте токени на запит із вашого зразка та скільки запитів ви плануєте.

Калькулятор вартості

Ваш пакет: токени з вашого зразка на 5 промптів

Вхідні токени на запит: 1 500 · Вихідні токени на запит: 400 · Запитів на день: 200

МодельЗа деньЗа місяць
GLM-5.3-Flash$0.0092$0.276
DeepSeek-V4-Flash$0.0125$0.375
MiniMax-M2.7$0.027$0.81

Докладніше про підрахунок токенів перед надсиланням дивіться у підрахунок токенів за допомогою tiktoken.

Коли рахунок більший, ніж очікувалося: читайте журнал#

Третій сценарій читача — найнеприємніший. Запуск агента йде не так і зациклюється, а дашборд показує лише підсумок. Журнал показує, де саме. Відсортуйте рядки за вартістю:

python
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.