Voltar ao blog

API e SDKGuia

Rastreie o custo de tokens de agentes de IA em Python: usage, orçamento, parada

Registre o campo usage de cada requisição, some por tarefa e pare o agente em um orçamento em dólares. Exemplo funcional em Python para uma API compatível com OpenAI.

Por CheapTokens TeamPublicado em 6 min de leitura

Cartão de código estilo terminal: um log de usage por passo e uma verificação de orçamento que para um agente de IA
Nesta página
  1. Onde estão os números: o objeto usage
  2. Um medidor que para o agente
  3. Quanto soma uma execução de 40 passos
  4. Quando o modelo está pensando
  5. Streaming: o usage vem por último
  6. Se os números parecerem errados

Seu agente rodou por dez minutos e a conta é maior que a tarefa. Para ver por quê, leia o objeto usage em cada resposta da API, some por tarefa e multiplique pelo preço do modelo por 1M de tokens. Para limitar, compare esse total acumulado com um orçamento após cada passo e pare o loop quando for ultrapassado. Abaixo está uma pequena classe Python que faz exatamente isso.

O exemplo usa o endpoint compatível com OpenAI https://api.cheaptokens.dev/v1 e o id do modelo deepseek-ai/DeepSeek-V4-Flash-0731 (contexto de 380K, até 32.768 tokens de saída por resposta). Se você quer estimar uma execução antes de iniciá-la, isso é outra tarefa, e Como estimar o custo de um agente de IA cobre isso. Este artigo é sobre medir e limitar enquanto a execução está acontecendo.

Onde estão os números: o objeto usage#

Toda conclusão de chat não-streaming retorna com um objeto usage. A referência da API do DeepSeek (verificada em 26 de outubro de 2026) lista estes campos:

  • prompt_tokens — tudo que você enviou desta vez: prompt de sistema, histórico, resultados de ferramentas.
  • completion_tokens — o que o modelo escreveu de volta.
  • total_tokens — os dois somados.

O DeepSeek também documenta extras opcionais: prompt_cache_hit_tokens e prompt_cache_miss_tokens (eles somam prompt_tokens), e completion_tokens_details.reasoning_tokens para modelos de raciocínio. A página não mostra data de publicação, e esses extras são extensões do formato OpenAI, então o código abaixo os trata como opcionais e depende apenas dos dois primeiros campos.

Por que registrar cada passo e não apenas o último? Porque um agente reenvia seu contexto crescente a cada vez. O passo 1 pode enviar 2.000 tokens de entrada, o passo 30 pode enviar 9.000, e a conta é a soma de todos eles. O motivo é explicado em por que um chat longo custa mais, e a divisão entrada/saída em tokens de entrada vs saída.

Um medidor que para o agente#

Aqui está tudo: um medidor que soma tokens, imprime uma linha por passo e lança um erro quando o orçamento é ultrapassado, além de um loop de agente simples em volta dele. Instale o SDK com pip install openai e coloque sua chave em 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)

Três escolhas ali valem a pena copiar.

Registre antes de decidir. meter.add registra o passo primeiro e só então verifica o orçamento, então uma execução interrompida ainda deixa um rastro completo. A verificação acontece após a chamada que cruzou a linha, então você pode ultrapassar em no máximo um passo. max_tokens=1024 limita a saída desse último passo.

Duas paradas rígidas, não uma. max_steps captura um agente que faz loop sem gastar muito; o orçamento em dólares captura um que gasta muito por passo. Você quer ambos.

Preços vivem em um só lugar. PRICE_IN e PRICE_OUT são números de exemplo. Copie os valores atuais para o seu modelo da página de preços; agora mesmo o DeepSeek-V4-Flash é $0.015 por 1M de tokens de entrada e $0.10 por 1M de tokens de saída.

Quanto soma uma execução de 40 passos#

Digamos que seu agente leva 40 passos, e como o contexto cresce, um passo médio envia 6.000 tokens de entrada e recebe 400 tokens de volta. Isso é 40 × 6.000 = 240.000 tokens de entrada e 40 × 400 = 16.000 tokens de saída para a tarefa.

Passo 1 envia 2.000 tokens de entrada, passo 30 envia 9.000; o total da tarefa é a soma de todos os passos
Ilustração com as premissas do artigo, não um resultado de execução

No DeepSeek-V4-Flash isso custa $0.0052. A tabela executa a mesma carga de trabalho nos outros modelos e coloca o preço de referência do OpenRouter ao lado do nosso:

Modelo Custo 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 tokens de entrada e 16.000 tokens de saída no total. Os preços por 1M de tokens são atuais e mudam junto com nossas tarifas; a coluna OpenRouter mostra preços de referência para a mesma carga de trabalho.

No total, com entrada e saída contadas juntas, o DeepSeek-V4-Flash sai mais barato que o OpenRouter para esta tarefa. Observe também onde estão os tokens: 240.000 dos 256.000 são de entrada. O contexto que seu agente carrega junto é a conta, e é por isso que o número de in= por passo no seu log é o que deve ser observado. Um orçamento de $0,50 por tarefa no exemplo acima é um limite para uma execução descontrolada, não uma meta.

Trocar de modelo significa apenas mudar MODEL. Escolha o id na documentação do CheapTokens: os nomes diferem entre provedores, e um id curto de um rótulo de preço não funcionará na API. Para loops longos de agente, o DeepSeek-V4-Flash tem um preço a qualquer hora do dia, então uma tarefa custa o mesmo ao meio-dia e às 3 da manhã. Veja a página do DeepSeek para seus limites.

Quando o modelo está pensando#

Se uma resposta for muito mais longa em tokens do que o texto que você vê, olhe reasoning_tokens. Tokens de raciocínio contam como saída, então um passo com muito raciocínio pode produzir muito mais tokens de saída do que o texto visível sugere, e eles são cobrados como saída. O medidor acima os coleta em meter.reasoning quando a API retorna o campo e o deixa em 0 quando não retorna, então nunca quebra por uma chave ausente. Imprima-o ao lado de out= se quiser ver quanto da saída foi raciocínio. Esforço de raciocínio e tokens de pensamento explica por quê.

Streaming: o usage vem por último#

Se seu agente faz streaming, não há usage nos chunks normais. No formato OpenAI você o solicita com stream_options={'include_usage': True}. A referência do DeepSeek (verificada em 26 de outubro de 2026) diz que a opção funciona apenas junto com stream=True (caso contrário, a API responde 400), que usage é null em todos os chunks menos um, e que o último chunk antes de data: [DONE] carrega os totais de toda a requisição. Então, em um loop de streaming, chame meter.add uma vez, no chunk onde usage não estiver vazio. O loop não-streaming acima é o lugar mais simples para começar; streaming em Python tem o loop em si.

Se os números parecerem errados#

  • usage é None. Você está fazendo streaming sem include_usage. Adicione-o, ou mude para uma chamada não-streaming.
  • O total é menor que a queda do saldo. Verifique se toda chamada passa pelo medidor: retentativas, uma chamada de sumarização ou uma chamada de escolha de ferramenta feita fora de run_task são fáceis de esquecer. Envolva client.chat.completions.create em uma função e encaminhe tudo por ela.
  • O custo por passo continua subindo. Isso é normal para um loop que anexa tudo a messages. Corte saídas antigas de ferramentas ou resuma o histórico; saída de ferramenta fora do contexto mostra a ideia.
  • BudgetExceeded dispara no passo 2. Seu orçamento é menor que um passo. Verifique se PRICE_IN é por 1M de tokens, não por 1K.

Para armazenar os números, escreva meter.steps, meter.tokens_in, meter.tokens_out e meter.cost como uma linha JSON por tarefa. Uma semana dessas linhas lhe diz os tokens reais por passo para o seu agente, que é o número a colocar em toda estimativa depois. Modelos, preços e a primeira requisição estão na documentação; chamadas de ferramentas são abordadas em function calling em Python.