Bumalik sa blog

API at SDKGabay

Subaybayan ang token cost ng AI agent sa Python: usage, budget, stop

I-log ang usage field ng bawat request, idagdag ito sa bawat gawain at itigil ang agent sa isang dollar budget. Gumaganang Python example para sa isang OpenAI-compatible API.

Ni CheapTokens TeamNai-publish noong 7 min na pagbabasa

Terminal-style code card: isang per-step usage log at isang budget check na humihinto sa isang AI agent
Sa pahinang ito
  1. Kung saan matatagpuan ang mga numero: ang usage object
  2. Isang meter na humihinto sa agent
  3. Ano ang kabuuan ng isang 40-step run
  4. Kapag nag-iisip ang modelo
  5. Streaming: huli ang usage
  6. Kung mali ang mga numero

Ang iyong agent ay tumakbo ng sampung minuto at ang bayarin ay mas malaki kaysa sa gawain. Upang makita kung bakit, basahin ang usage object sa bawat API response, idagdag ito sa bawat gawain at i-multiply sa presyo ng modelo kada 1M tokens. Upang mapigilan ito, ihambing ang running total na iyon sa budget pagkatapos ng bawat hakbang at itigil ang loop kapag nalampasan na. Nasa ibaba ang isang maliit na Python class na eksaktong gumagawa nito.

Gumagamit ang halimbawa ng OpenAI-compatible endpoint na https://api.cheaptokens.dev/v1 at ang model id na deepseek-ai/DeepSeek-V4-Flash-0731 (380K context, hanggang 32,768 output tokens bawat sagot). Kung gusto mong tantiyahin ang isang run bago ito simulan, ibang trabaho iyon, at saklaw ito ng How to estimate the cost of an AI agent. Ang artikulong ito ay tungkol sa pagsukat at paglilimita habang tumatakbo ang run.

Kung saan matatagpuan ang mga numero: ang usage object#

Bawat non-streaming chat completion ay bumabalik na may usage object. Inililista ng API reference ng DeepSeek (sinuri noong 26 Oktubre 2026) ang mga field na ito:

  • prompt_tokens — lahat ng ipinadala mo sa pagkakataong ito: system prompt, history, tool results.
  • completion_tokens — ang isinulat pabalik ng modelo.
  • total_tokens — ang dalawa na pinagsama.

Nagdodokumento rin ang DeepSeek ng mga opsyonal na dagdag: prompt_cache_hit_tokens at prompt_cache_miss_tokens (nagdadagdag sila sa prompt_tokens), at completion_tokens_details.reasoning_tokens para sa thinking models. Walang petsa ng publikasyon ang pahina, at ang mga dagdag na ito ay mga extension ng OpenAI format, kaya tinatrato sila ng code sa ibaba bilang opsyonal at umaasa lamang sa unang dalawang field.

Bakit i-log ang bawat hakbang at hindi lamang ang huli? Dahil ang isang agent ay muling nagpapadala ng lumalaking context nito sa bawat pagkakataon. Ang hakbang 1 ay maaaring magpadala ng 2,000 input tokens, ang hakbang 30 ay maaaring magpadala ng 9,000, at ang bayarin ay ang kabuuan ng lahat ng ito. Ang dahilan ay ipinaliwanag sa why a long chat costs more, at ang input/output split sa input vs output tokens.

Isang meter na humihinto sa agent#

Narito ang buong bagay: isang meter na nagdadagdag ng tokens, nagpi-print ng isang linya bawat hakbang at nagtataas ng error kapag nalampasan ang budget, kasama ang isang bare agent loop sa paligid nito. I-install ang SDK gamit ang pip install openai at ilagay ang iyong key sa 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)

Tatlong pagpipilian doon ang sulit kopyahin.

Mag-log bago magdesisyon. Itinatala muna ng meter.add ang hakbang at pagkatapos lamang suriin ang budget, kaya ang isang tumigil na run ay nag-iiwan pa rin ng kumpletong trace. Ang pagsusuri ay nangyayari pagkatapos ng tawag na lumampas sa linya, kaya maaari kang lumampas nang hindi hihigit sa isang hakbang. Nililimitahan ng max_tokens=1024 ang output ng huling hakbang na iyon.

Dalawang hard stop, hindi isa. Nahuhuli ng max_steps ang isang agent na umiikot nang hindi masyadong gumagastos; nahuhuli ng dollar budget ang isa na malaki ang gastos bawat hakbang. Kailangan mo ang pareho.

Nasa isang lugar lang ang mga presyo. Ang PRICE_IN at PRICE_OUT ay placeholder na mga numero. Kopyahin ang kasalukuyang mga halaga para sa iyong modelo mula sa pricing page; sa ngayon ang DeepSeek-V4-Flash ay $0.015 kada 1M input tokens at $0.10 kada 1M output tokens.

Ano ang kabuuan ng isang 40-step run#

Sabihin nating ang iyong agent ay tumatagal ng 40 hakbang, at dahil lumalaki ang context, ang isang average na hakbang ay nagpapadala ng 6,000 input tokens at nakakakuha ng 400 tokens pabalik. Iyon ay 40 × 6,000 = 240,000 input tokens at 40 × 400 = 16,000 output tokens para sa gawain.

Step 1 sends 2,000 input tokens, step 30 sends 9,000; the task total is the sum of all steps
Illustration with the article's assumptions, not a run result

Sa DeepSeek-V4-Flash nagkakahalaga iyon ng $0.0052. Ang talahanayan ay nagpapatakbo ng parehong workload sa iba pang mga modelo at inilalagay ang reference price ng OpenRouter sa tabi ng sa amin:

Modelo Gastos 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 input at 16,000 output na token sa kabuuan. Ang mga presyo kada 1M token ay kasalukuyan at nagbabago kasabay ng aming mga taripa; ang column ng OpenRouter ay nagpapakita ng reference na presyo para sa parehong workload.

Sa kabuuan, kasama ang input at output na binibilang nang magkasama, ang DeepSeek-V4-Flash ay lumalabas na mas mura kaysa sa OpenRouter para sa gawaing ito. Pansinin din kung nasaan ang mga token: 240,000 sa 256,000 ay input. Ang context na hinihila ng iyong agent ay ang bayarin, kaya ang per-step na in= na numero sa iyong log ang dapat bantayan. Ang budget na $0.50 bawat gawain sa halimbawa sa itaas ay isang cap sa isang runaway, hindi isang target.

Ang pagpapalit ng modelo ay nangangahulugan lamang ng pagpapalit ng MODEL. Piliin ang id mula sa CheapTokens docs: nagkakaiba ang mga pangalan sa pagitan ng mga provider, at ang isang maikling id mula sa isang price label ay hindi gagana sa API. Para sa mahabang agent loops, ang DeepSeek-V4-Flash ay may isang presyo sa anumang oras ng araw, kaya ang isang gawain ay nagkakahalaga ng pareho sa tanghali at sa 3 a.m. Tingnan ang DeepSeek page para sa mga limitasyon nito.

Kapag nag-iisip ang modelo#

Kung ang isang sagot ay mas mahaba sa tokens kaysa sa tekstong nakikita mo, tingnan ang reasoning_tokens. Ang mga thinking token ay binibilang bilang output, kaya ang isang reasoning-heavy na hakbang ay maaaring gumawa ng mas maraming output tokens kaysa sa iminumungkahi ng nakikitang teksto, at sinisingil sila bilang output. Kinokolekta sila ng meter sa itaas sa meter.reasoning kapag ibinalik ng API ang field at iniiwan ito sa 0 kapag hindi, kaya hindi ito nag-crash sa nawawalang key. I-print ito sa tabi ng out= kung gusto mong makita kung gaano karami sa output ang pag-iisip. Ipinaliwanag ng Reasoning effort and thinking tokens kung bakit.

Streaming: huli ang usage#

Kung nag-stream ang iyong agent, walang usage sa mga normal na chunk. Sa OpenAI format, hinihiling mo ito gamit ang stream_options={'include_usage': True}. Sinasabi ng reference ng DeepSeek (sinuri noong 26 Oktubre 2026) na ang opsyon ay gumagana lamang kasama ng stream=True (kung hindi, ang API ay sumasagot ng 400), na ang usage ay null sa bawat chunk maliban sa isa, at na ang huling chunk bago ang data: [DONE] ay nagdadala ng mga total para sa buong request. Kaya sa isang streaming loop, tawagin ang meter.add nang isang beses, sa chunk kung saan ang usage ay hindi walang laman. Ang non-streaming loop sa itaas ang pinakasimpleng lugar upang magsimula; ang streaming in Python ay may loop mismo.

Kung mali ang mga numero#

  • Ang usage ay None. Nag-stream ka nang walang include_usage. Idagdag ito, o lumipat sa isang non-streaming na tawag.
  • Ang total ay mas mababa kaysa sa pagbaba ng balanse. Suriin na ang bawat tawag ay dumadaan sa meter: ang mga retry, isang summarising call o isang tool-picking call na ginawa sa labas ng run_task ay madaling makalimutan. I-wrap ang client.chat.completions.create sa isang function at i-route ang lahat sa pamamagitan nito.
  • Patuloy na tumataas ang gastos bawat hakbang. Normal iyon para sa isang loop na nagdadagdag ng lahat sa messages. Putulin ang lumang tool output o i-summarize ang history; ipinapakita ng tool output out of context ang ideya.
  • Ang BudgetExceeded ay nag-trigger sa hakbang 2. Ang iyong budget ay mas maliit kaysa sa isang hakbang. Suriin na ang PRICE_IN ay kada 1M tokens, hindi kada 1K.

Upang iimbak ang mga numero, isulat ang meter.steps, meter.tokens_in, meter.tokens_out at meter.cost bilang isang JSON line bawat gawain. Ang isang linggo ng mga linyang iyon ay nagsasabi sa iyo ng tunay na tokens bawat hakbang para sa iyong agent, na siyang numerong ilalagay sa bawat pagtatantya pagkatapos. Ang mga modelo, presyo at ang unang request ay nasa docs; ang mga tool call ay sakop sa function calling in Python.