Powrót do bloga

API i SDKPrzewodnik

Śledź koszt tokenów agenta AI w Pythonie: usage, budżet, stop

Loguj pole usage każdego żądania, sumuj je na zadanie i zatrzymaj agenta przy budżecie dolarowym. Działający przykład w Pythonie dla API zgodnego z OpenAI.

Autor: CheapTokens TeamOpublikowano 5 min czytania

Karta kodu w stylu terminala: log usage na krok i sprawdzenie budżetu, które zatrzymuje agenta AI
Na tej stronie
  1. Gdzie są liczby: obiekt usage
  2. Licznik, który zatrzymuje agenta
  3. Ile sumuje się przebieg 40 kroków
  4. Gdy model myśli
  5. Strumieniowanie: usage przychodzi na końcu
  6. Jeśli liczby wyglądają źle

Twój agent działał przez dziesięć minut, a rachunek jest większy niż samo zadanie. Aby zobaczyć dlaczego, odczytaj obiekt usage w każdej odpowiedzi API, zsumuj go na zadanie i pomnóż przez cenę modelu za 1M tokenów. Aby go ograniczyć, porównuj tę narastającą sumę z budżetem po każdym kroku i zatrzymaj pętlę, gdy zostanie przekroczony. Poniżej znajduje się mała klasa w Pythonie, która robi dokładnie to.

Przykład używa endpointu zgodnego z OpenAI https://api.cheaptokens.dev/v1 i identyfikatora modelu deepseek-ai/DeepSeek-V4-Flash-0731 (kontekst 380K, do 32 768 tokenów wyjściowych na odpowiedź). Jeśli chcesz oszacować przebieg przed jego rozpoczęciem, to inne zadanie, które omawia Jak oszacować koszt agenta AI. Ten artykuł dotyczy pomiaru i ograniczania w trakcie przebiegu.

Gdzie są liczby: obiekt usage#

Każde nieprzesyłane strumieniowo ukończenie czatu zwraca obiekt usage. Dokumentacja API DeepSeek (sprawdzona 26 października 2026) wymienia te pola:

  • prompt_tokens — wszystko, co wysłałeś tym razem: prompt systemowy, historia, wyniki narzędzi.
  • completion_tokens — to, co model napisał w odpowiedzi.
  • total_tokens — oba dodane do siebie.

DeepSeek dokumentuje również opcjonalne dodatki: prompt_cache_hit_tokens i prompt_cache_miss_tokens (sumują się do prompt_tokens) oraz completion_tokens_details.reasoning_tokens dla modeli myślących. Strona nie podaje daty publikacji, a te dodatki są rozszerzeniami formatu OpenAI, więc poniższy kod traktuje je jako opcjonalne i opiera się tylko na dwóch pierwszych polach.

Dlaczego logować każdy krok, a nie tylko ostatni? Ponieważ agent ponownie wysyła swój rosnący kontekst za każdym razem. Krok 1 może wysłać 2000 tokenów wejściowych, krok 30 może wysłać 9000, a rachunek to suma wszystkich. Powód wyjaśniono w dlaczego długa rozmowa kosztuje więcej, a podział wejście/wyjście w tokeny wejściowe vs wyjściowe.

Licznik, który zatrzymuje agenta#

Oto całość: licznik, który sumuje tokeny, wypisuje jedną linię na krok i zgłasza błąd, gdy budżet zostanie przekroczony, plus naga pętla agenta wokół niego. Zainstaluj SDK za pomocą pip install openai i umieść swój klucz w CHEAPTOKENS_API_KEY.

python
import os
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'

# example prices, USD per 1M tokens: use the current ones from the pricing table
PRICE_IN = 0.10
PRICE_OUT = 0.40


class BudgetExceeded(Exception):
    pass


class TaskMeter:
    def __init__(self, budget_usd):
        self.budget = budget_usd
        self.steps = 0
        self.tokens_in = 0
        self.tokens_out = 0
        self.reasoning = 0

    @property
    def cost(self):
        return (self.tokens_in * PRICE_IN + self.tokens_out * PRICE_OUT) / 1_000_000

    def add(self, usage):
        self.steps += 1
        self.tokens_in += usage.prompt_tokens
        self.tokens_out += usage.completion_tokens
        details = getattr(usage, 'completion_tokens_details', None)
        self.reasoning += getattr(details, 'reasoning_tokens', 0) or 0
        print(f'step {self.steps}: in={usage.prompt_tokens} '
              f'out={usage.completion_tokens} task=${self.cost:.4f}')
        if self.cost > self.budget:
            raise BudgetExceeded(
                f'budget ${self.budget:.2f} crossed at step {self.steps}: '
                f'${self.cost:.4f}')


def run_task(goal, budget_usd=0.50, max_steps=40):
    meter = TaskMeter(budget_usd)
    messages = [
        {'role': 'system', 'content': 'Work step by step. Reply DONE when finished.'},
        {'role': 'user', 'content': goal},
    ]
    for _ in range(max_steps):
        resp = client.chat.completions.create(
            model=MODEL, messages=messages, max_tokens=1024)
        meter.add(resp.usage)  # log first, then decide
        reply = resp.choices[0].message.content or ''
        messages.append({'role': 'assistant', 'content': reply})
        if 'DONE' in reply:
            break
        messages.append({'role': 'user', 'content': 'Continue.'})
    return meter


try:
    meter = run_task('Summarise the main risks of a database migration.')
    print(f'finished: {meter.steps} steps, ${meter.cost:.4f}')
except BudgetExceeded as err:
    print('stopped:', err)

Trzy wybory w tym kodzie warto skopiować.

Loguj, zanim zdecydujesz. meter.add najpierw rejestruje krok, a dopiero potem sprawdza budżet, więc zatrzymany przebieg nadal pozostawia kompletny ślad. Sprawdzenie następuje po wywołaniu, które przekroczyło linię, więc możesz przekroczyć budżet o co najwyżej jeden krok. max_tokens=1024 ogranicza wyjście tego ostatniego kroku.

Dwa twarde zatrzymania, nie jedno. max_steps wychwytuje agenta, który zapętla się bez dużego wydatku; budżet dolarowy wychwytuje takiego, który wydaje dużo na krok. Potrzebujesz obu.

Ceny żyją w jednym miejscu. PRICE_IN i PRICE_OUT to liczby zastępcze. Skopiuj aktualne wartości dla swojego modelu ze strony cenowej; obecnie DeepSeek-V4-Flash kosztuje $0.015 za 1M tokenów wejściowych i $0.10 za 1M tokenów wyjściowych.

Ile sumuje się przebieg 40 kroków#

Załóżmy, że twój agent wykonuje 40 kroków, a ponieważ kontekst rośnie, przeciętny krok wysyła 6000 tokenów wejściowych i otrzymuje 400 tokenów z powrotem. To daje 40 × 6000 = 240 000 tokenów wejściowych i 40 × 400 = 16 000 tokenów wyjściowych na zadanie.

Krok 1 wysyła 2000 tokenów wejściowych, krok 30 wysyła 9000; suma zadania to suma wszystkich kroków
Ilustracja z założeniami artykułu, nie wynik przebiegu

Na DeepSeek-V4-Flash kosztuje to $0.0052. Tabela uruchamia to samo obciążenie na pozostałych modelach i umieszcza cenę referencyjną OpenRouter obok naszej:

Model Koszt OpenRouter
GLM-5.3-Flash $0.0054 $0.0111
DeepSeek-V4-Flash $0.0052 $0.0139
MiniMax-M2.7 $0.0144 $0.0912

240 000 tokenów wejściowych i 16 000 tokenów wyjściowych łącznie. Ceny za 1 mln tokenów są aktualne i zmieniają się wraz z naszymi taryfami; kolumna OpenRouter pokazuje ceny referencyjne dla tego samego obciążenia.

W sumie, licząc wejście i wyjście razem, DeepSeek-V4-Flash wypada taniej niż OpenRouter dla tego zadania. Zwróć też uwagę, gdzie są tokeny: 240 000 z 256 000 to wejście. Kontekst, który twój agent ciągnie za sobą, to rachunek, dlatego liczba in= na krok w twoim logu jest tą, na którą warto patrzeć. Budżet 0,50 USD na zadanie w powyższym przykładzie to ograniczenie na wypadek wymknięcia się spod kontroli, a nie cel.

Zmiana modelu oznacza tylko zmianę MODEL. Wybierz identyfikator z dokumentacji CheapTokens: nazwy różnią się między dostawcami, a krótki identyfikator z etykiety cenowej nie zadziała w API. W przypadku długich pętli agenta DeepSeek-V4-Flash ma jedną cenę o każdej porze dnia, więc zadanie kosztuje tyle samo w południe i o 3 w nocy. Zobacz stronę DeepSeek, aby poznać jego limity.

Gdy model myśli#

Jeśli odpowiedź ma znacznie więcej tokenów niż widoczny tekst, spójrz na reasoning_tokens. Tokeny myślenia liczą się jako wyjście, więc krok mocno oparty na rozumowaniu może wygenerować znacznie więcej tokenów wyjściowych, niż sugeruje widoczny tekst, i są one rozliczane jako wyjście. Powyższy licznik zbiera je w meter.reasoning, gdy API zwraca to pole, i pozostawia je na 0, gdy tego nie robi, więc nigdy nie zawodzi przy brakującym kluczu. Wypisz je obok out=, jeśli chcesz zobaczyć, jaka część wyjścia była myśleniem. Wysiłek rozumowania i tokeny myślenia wyjaśnia dlaczego.

Strumieniowanie: usage przychodzi na końcu#

Jeśli twój agent strumieniuje, w normalnych fragmentach nie ma usage. W formacie OpenAI prosisz o to za pomocą stream_options={'include_usage': True}. Dokumentacja DeepSeek (sprawdzona 26 października 2026) mówi, że opcja działa tylko razem z stream=True (w przeciwnym razie API odpowiada 400), że usage wynosi null w każdym fragmencie oprócz jednego, a ostatni fragment przed data: [DONE] zawiera sumy dla całego żądania. Więc w pętli strumieniowej wywołaj meter.add raz, na fragmencie, w którym usage nie jest puste. Powyższa pętla nieprzesyłana strumieniowo to najprostsze miejsce na start; strumieniowanie w Pythonie zawiera samą pętlę.

Jeśli liczby wyglądają źle#

  • usage to None. Strumieniujesz bez include_usage. Dodaj to lub przełącz się na wywołanie nieprzesyłane strumieniowo.
  • Suma jest niższa niż spadek salda. Sprawdź, czy każde wywołanie przechodzi przez licznik: ponowienia, wywołanie podsumowujące lub wywołanie wybierające narzędzie wykonane poza run_task łatwo przeoczyć. Owiń client.chat.completions.create w jedną funkcję i przeprowadź przez nią wszystko.
  • Koszt na krok stale rośnie. To normalne dla pętli, która dopisuje wszystko do messages. Przytnij stare wyniki narzędzi lub podsumuj historię; wyniki narzędzi poza kontekstem pokazuje ten pomysł.
  • BudgetExceeded uruchamia się na kroku 2. Twój budżet jest mniejszy niż jeden krok. Sprawdź, czy PRICE_IN jest za 1M tokenów, a nie za 1K.

Aby przechowywać liczby, zapisz meter.steps, meter.tokens_in, meter.tokens_out i meter.cost jako jedną linię JSON na zadanie. Tydzień takich linii powie ci, ile naprawdę tokenów na krok zużywa twój agent, co jest liczbą, którą należy wpisać do każdego późniejszego oszacowania. Modele, ceny i pierwsze żądanie są w dokumentacji; wywołania narzędzi omówiono w wywoływanie funkcji w Pythonie.