Skip to main content
O Cost Tracking do ChatCLI monitora o consumo de tokens e estima custos em tempo real durante suas sessões, usando dados reais de uso da API de todos os providers que os reportam. Você inspeciona a sessão viva com /cost, fecha e reinicia o período de contabilidade com /cost reset, revisa sessões passadas com /cost last e /cost sessions, exporta snapshots legíveis por máquina com /cost export, e configura limites de gasto — incluindo um hard stop opcional.

Comando /cost

/cost renderiza o resumo da sessão viva; quatro subcomandos gerenciam o período de contabilidade: O resumo é renderizado como um painel em caixa (os valores abaixo são autoconsistentes para claude-sonnet-5):
A linha Source reporta se a sessão tem uso real de API; cada linha de modelo no breakdown de custo carrega adicionalmente sua própria tag — (API) para contagens reportadas pelo provider, (estimativa) para estimativa por caracteres — porque uma sessão pode misturar as duas. Reasoning tokens (o-series / GPT-5 / thinking do Gemini) ganham uma linha informativa própria quando presentes; já estão cobrados dentro de Output.
Modelos que não casaram com nenhuma entrada da tabela de preços não são descartados em silêncio: o /cost os lista explicitamente (Sem tabela de preço para: provider/modelo — esse gasto NÃO está no total.), então um total sub-reportado sempre fica visível como tal.

Cache de Prompt por Provedor

O ChatCLI monta toda requisição com a parte que não muda entre turnos primeiro (system prompt, contextos anexados, catálogo de tools) e pede ao provedor para cacheá-la onde a API oferece um mecanismo para isso. A linha Cache de prompt do /cost e o cache NN% do rodapé do chat/agent são neutros de provedor: são calculados a partir dos campos de cache que cada provedor reporta, nos dois esquemas de contagem (Anthropic/Bedrock contam leituras de cache além do input; OpenAI/Gemini/Grok/Kimi contam como subconjunto do prompt).
  • requisições — quantas chamadas reportaram atividade de cache.
  • % do input vindo do cache — parcela do input da sessão servida pelo cache.
  • misses instáveis — requisições que perderam o prefixo quando ele deveria estar quente. Cada requisição é julgada contra o que a anterior do mesmo provedor deixou legível: na Anthropic e no Bedrock tudo o que a anterior leu mais o que escreveu; na OpenAI, Gemini, xAI e Kimi o prompt inteiro da anterior. Ler de volta bem menos que isso (abaixo de 90%, ou 80% nos caches por bloco do esquema subconjunto) é a assinatura de um prefixo estável que mudou (troca de modelo/effort, conjunto de tools MCP, anexo atualizado). Uma escrita grande ou um prompt grande sozinhos nunca são miss — é assim que um tool result grande ou um arquivo colado aparecem num prefixo perfeitamente estável. Três misses instáveis seguidos imprimem um aviso único.
  • atribuição — o cache de prompt de todo provider é chaveado por prefixo sobre as mesmas regiões na mesma ordem: definições de tools, blocos system, mensagens. Antes de cada requisição o ChatCLI faz o fingerprint dessas regiões e compara a lista com a da requisição anterior, então um prefixo perdido é creditado à primeira região que mudou: conjunto de tools mudou, bloco system N mudou, mensagem N (tool result / bloco de skills / turn context / mensagem do usuário …) mudou, histórico encolheu. Uma perda numa requisição que só cresceu no fim é reportada como nenhuma mudança na requisição (lado do servidor ou tempo de vida) em vez de culpar o prompt. O /cost imprime a tabela (prefixos perdidos por causa) e os últimos cinco eventos (#7 rebuild esperado · tempo de vida do cache promovido para 1h). Quando o auto promove o tempo de vida para uma hora depois de uma expiração observada, a linha de saúde diz quando (promovido para 1h em +13m), e a única reescrita que os marcadores novos forçam na requisição seguinte é declarada e atribuída à promoção em vez de contada como instabilidade.
  • faixas — a linha de saúde, os contadores de miss e a razão write/read descrevem só a conversa principal. O prompt de extração do memory worker e o loop próprio de um worker de squad compartilham o provider mas não o prefixo, então suas requisições entram em faixas próprias: contam em todo número de tokens e dólares, mas nunca como prefixo perdido da sua conversa. Seus buckets brutos de cache continuam no log por requisição.
  • expirados — o prefixo foi perdido depois de uma pausa que o cache não sobreviveu (mais de 5 minutos ocioso, menos de uma hora). Contado à parte porque não diz nada sobre a estabilidade do prefixo; é também a evidência que a promoção para o TTL de uma hora usa.
  • por provedor — “primeira requisição” e a razão de acertos são por provedor (cada um tem seu cache e seu schema), então trocar de provedor nunca gasta a “primeira” do outro, e a razão mostrada é a do último provedor usado. Nos provedores de schema-subconjunto (OpenAI, Gemini, xAI, Kimi) um prompt no mínimo cacheável ou acima, servido com cached_tokens: 0 depois da primeira requisição daquele provedor, conta como miss — então o alerta de 3 misses funciona neles também. O Bedrock reporta o TTL que o marcador de fato carregou (1h em Claude 4.5+ quando configurado).
  • rebuilds esperados — misses causados pelo próprio ChatCLI ao mudar o prefixo de propósito: reescrever a conversa (auto-compact, /compact, microcompact, skill aging, recovery de overflow, edições de contexto do provedor) ou trocar o que o prefixo contém (/switch de provedor ou modelo, /agent attach|detach, /context attach|detach, /session load|attach, start/stop/restart/reload/login/logout de servidor MCP, pin|unpin de skill), além de reescrever a conversa (auto-compact, /compact, microcompact, envelhecimento de skills). Não é problema.
  • quente / frio — se a última atividade ainda está dentro do TTL do cache.

Prefixo byte-estável e a mensagem de contexto do turno

Todo provider cacheia por prefixo, então um system message que muda entre turnos faz todo breakpoint depois dele errar. O ChatCLI mantém o system message byte-estável na sessão: tudo que muda por turno — a data (resolução de dia), o recall proativo de memória e de sessões, skills auto-ativadas e manuais, pushes de canais MCP, o snapshot do watcher, passagens recuperadas — vai numa única mensagem com papel de usuário marcada turn_context, colocada logo antes do seu turno e persistida com a conversa, então a próxima requisição repete bytes idênticos até o breakpoint anterior. Chat, as superfícies RPC e agent/coder compartilham o mecanismo; a mensagem injetada é rotulada nas exportações e ignorada pela extração de memória e pelos hints de recall. O prompt_cache_key da OpenAI hasheia só as partes estáveis marcadas para cache, e o budget do prefixo congela sua razão chars/token por sessão para que seções cacheadas não dobrem e desdobrem entre turnos.

Recursos explícitos de cache (Gemini)

O cache implícito é gratuito e automático. O Gemini também expõe o cache como recurso (cachedContents), com desconto garantido em toda leitura e cobrado como storage por token-hora (Flash 1,00/M/h,Pro1,00/M/h, Pro 4,50/M/h, 3.x Flash $0,50/M/h). Como essa cobrança nunca aparece na resposta, é opt-in: CHATCLI_PROMPT_CACHE_EXPLICIT=true. A política é conservadora de propósito:
  • O recurso carrega a system instruction inteira; a requisição o referencia e omite system_instruction.
  • Só prompts de cerca de 4K+ tokens se qualificam, e o mesmo prompt precisa aparecer em duas requisições consecutivas antes de criar o recurso — um one-shot nunca paga storage.
  • O tempo de vida é CHATCLI_PROMPT_CACHE_TTL (5m, 1h ou auto), estendido quando resta menos da metade; o recurso é apagado quando o prompt muda e no encerramento.
  • Recurso rejeitado pela API (expirado, apagado, abaixo do piso) é descartado e o turno é reenviado inline — um cache nunca derruba um turno. Recusas colocam o prompt em backoff por 10 minutos, e uma resposta “too small” ensina o piso.
  • /cost mostra o storage comprado (Storage de cache: N recurso(s) · $x) e o soma no total da sessão; o valor é persistido com o snapshot de custo.

Custo de embeddings

Toda chamada de embedding do provedor configurado (retrieval de conhecimento e aquecimentos, vetores de memória, HyDE) é medida: os tokens são estimados por caracteres na tarifa de lista do provedor por milhão de tokens, e o /cost imprime Embeddings: N chamada(s) · ~T tokens (estimados por caracteres) · $x. Ollama mede a $0. O valor entra no total da sessão e persiste com o snapshot.

Gasto em background e atribuição

Toda requisição é contabilizada na chamada que a fez. O usage do sumarizador de Nível 2 é lido da própria chamada (um engine de contexto externo, um retorno antecipado ou uma falha não contabilizam nada; um resumo repetido cobra as duas chamadas). O memory worker (extração, rollups, compactação de memória) roda no próprio cliente — CHATCLI_COMPACT_MODEL quando definido, senão uma instância dedicada do modelo da sessão — e por isso nunca sobrescreve o usage do turno interativo; respeita CHATCLI_BUDGET_HARD_STOP como qualquer outro chamador e o /cost imprime Memory worker: N chamada(s) · $x. Uma resposta em streaming que termina sem bloco de usage é contabilizada como estimativa por caracteres (marcada como tal) em vez de sair de graça, e os providers zeram o usage antes de cada requisição para que uma resposta sem usage nunca re-contabilize a chamada anterior. Tokens de thinking do Gemini (thoughtsTokenCount) são cobrados por cima da contagem de candidates; tokens de reasoning da OpenAI já estão dentro de completion_tokens e ficam só informativos.

Custo de compactação

O /cost também imprime Compactações: N (nível 3: M) · sumarizador $x depois que o histórico foi compactado: quantas vezes, quantas caíram no Nível 3 (truncagem de emergência) e o que o sumarizador do Nível 2 consumiu. A requisição do sumarizador é uma requisição real na rota da sessão (ou em CHATCLI_COMPACT_MODEL), então entra nos totais da sessão como qualquer turno; os contadores persistem com a sessão. Veja context recovery para o back-off que para de pagar o sumarizador quando ele não converge. O context caching da Moonshot/Kimi é totalmente automático na plataforma atual (sem ids de cache nem API de gerenciamento; a requisição anterior precisa passar de 256 tokens de prompt), então nada é criado lá — o split cached_tokens é o que o tracker precifica.

Contagem de tokens por provedor

O ctx % do rodapé, o orçamento de compactação e o /context status compartilham uma única estimativa com quatro categorias — system prompt, histórico, definições de tools nativas e reserva da resposta (max_tokens como enviado, limitado a um quarto da janela) — o mesmo detalhamento que o /context do Claude Code mostra. O rodapé mostra a reserva à parte — ctx 2% (+14% reserva) — para uma conversa nova nunca parecer uma janela capada: o primeiro número é o que já ocupa a janela, o segundo é o espaço que a próxima requisição precisa deixar livre para a resposta (os provedores exigem entrada + max_tokens ≤ janela; um /max-tokens menor encolhe a reserva). O /context status imprime as duas últimas linhas, e o compactador reserva o prompt e as definições de tools ao lado do histórico que mede. O orçamento de passagens recuperadas escala com a janela (no máximo 24K chars, 15% da janela em chars, piso de 4K). A razão chars/token por trás dessa estimativa, da projeção do /context status e do orçamento de compactação é aprendida do usage real de todos os provedores (a mesma cobertura de 14 provedores da tabela abaixo), então é exata após o primeiro turno em qualquer lugar. Provedores que também expõem uma API de contagem, e a família GPT por um tokenizer local, ancoram a razão antes de a requisição sair — uma contagem gratuita a cada 8 turnos de chat e no /context status, que então mostra o tamanho do histórico vivo contado pelo provedor: Uma contagem que falha ou demora (limite de 10 s) nunca afeta um turno: a razão aprendida simplesmente permanece. As razões aprendidas são persistidas em ~/.chatcli/calibration.json (escrita atômica, descarregada na saída) e recarregadas no próximo start, então um processo novo não recomeça em 4 chars por token. Sob o gateway o arquivo fica sob a raiz de cada tenant, então tenants nunca compartilham razões. Toda estimativa da CLI lê a mesma razão aprendida: o budget de processamento de arquivos, /metrics, /context list e o feedback do attach, o rodapé de economia de compressão, o budget de compactação e o chunker, validador e digests do gerenciador de contexto.

Dados Reais de API — Cobertura por Provider

O tracker prefere o uso real da resposta do provider e só cai em estimativa por caracteres (chars/4, marcada IsReal=false) quando o provider não reporta nada:

Modos cobertos

Toda chamada LLM que uma sessão faz é contabilizada e atribuída ao modelo que de fato a serviu (incluindo hints de skill e overrides de rota do @model):
  • Chat — streaming, buffered e o caminho de exceção de tools do /ask
  • Agent / Coder — cada turno ReAct, mais os workers/subagents despachados (o cliente de cada worker registra cada chamada LLM ao vivo, sob o provider+modelo do próprio worker — e o hard stop de orçamento gateia cada chamada)
  • Painéis MoA — caminhos native-tools, XML e plain, atribuídos por participante
  • One-shot (-p), turnos RPC/Gateway/ACP e execuções agendadas (o scheduler roda em client dedicado e recebe de volta números de tokens/custo — uma estimativa autocontida de propósito, imune a corridas com turnos interativos)

Tabelas de Preço

Preços em USD por 1M de tokens, espelhando cli/cost_tracker.go (pinado por cost_tracker_pricing_test.go). O match é por substring do id do modelo, mais específico primeiro.

Anthropic (cache write = 1,25× input, cache read = 10% do input — 2,5% no Fable 5.1)

OpenAI (cache read = 50% do input, sem sobretaxa de write)

Google (cache read = 25% do input)

xAI (Grok) — cached input = 25% no grok-4.7/4.6, 15% no grok-4.5, 16% nos demais; sem sobretaxa de write

Z.AI (GLM)

DeepSeek (preço de pico; cache read = 25% do input)

Moonshot (Kimi) — preço de cache miss, cache read = 17% do input

Outros

Os preços são atualizados nas releases do ChatCLI e pinados por testes. Um modelo não listado é reportado como sem preço no /cost (nunca zero silencioso), e o custo cobrado da OpenRouter cobre sua cauda longa independentemente das tabelas locais.

Contabilidade de Cache

Tokens de cache são precificados por família — e, crucialmente, com a semântica correta por provider:
  • Anthropic / Bedrock Anthropic reportam tokens de cache ao lado de input_tokens (aditivos): write cobrado a 1,25× do input, read a 10% (2,5% no Fable 5.1).
  • OpenAI, xAI, Gemini, DeepSeek, Moonshot reportam cache reads como subconjunto da contagem de prompt: a fatia cacheada é recortada do input e cobrada uma única vez na taxa com desconto — nunca preço cheio mais desconto.
O bloco Economia do cache é o balanço do cache da sessão em dólares, calculado a partir das tarifas reais de cada modelo — não um percentual fixo. Desconto de leitura é o que as leituras de cache pouparam contra o preço de input. Prêmio de escrita é o que as escritas de cache custaram acima do preço de input: 1,25x na Anthropic e no Bedrock, 2x com o TTL de uma hora, nada na OpenAI, Gemini, xAI e Kimi (a linha some quando é zero). Economia líquida é a diferença, e Sem cache é o que os mesmos tokens custariam sem cache nenhum, ao lado do total real. Só as leituras superestimam a economia: uma sessão que reescreveu o prefixo algumas vezes pode ter devolvido um terço do que as leituras pouparam, e é aqui que isso aparece. A linha Processados de tokens é todo token que o provedor tokenizou, cacheado ou não, já que Input nos esquemas aditivos é só a parte não cacheada.

Persistência de Sessão

Os dados de custo são gravados em disco conforme a sessão roda (write-through com throttle, escrita atômica):
Toda superfície finaliza. One-shot (-p), o daemon do gateway e os servidores MCP/ACP persistem o snapshot de custo e o gasto diário e liberam caches pagos do provedor na saída, exatamente como o cleanup do REPL; o gasto com embeddings conta no orçamento diário. Quando o hard stop do orçamento dispara dentro do loop do agent, o run é parkado (retoma à meia-noite local seguinte, ou com /parked resume depois de subir o orçamento) em vez de morrer perdendo o trabalho. Tiers de contexto longo, assinaturas e o orçamento diário. Uma chamada acima do limiar de contexto longo do provedor é precificada no seu tier por chamada — Claude acima de 200K de contexto (2× entrada, 1,5× saída), Gemini 2.5 Pro acima de 200K (2× / 1,5×), Grok 4 acima de 128K (2× / 2×) — e contabilizada como valor cobrado, então o agregado nunca a dilui; a estimativa do rodapé concorda. O GitHub Copilot é assinatura (premium requests, não tokens) e entra como zero conhecido. Todo modelo em zero conhecido que carregou tokens é nomeado abaixo do total (Sem tarifa por token conhecida para: …), para que um $0 nunca pareça grátis quando significa desconhecido, e CHATCLI_MODEL_PRICING atribui uma tarifa a ele. Amazon Nova Micro/Lite/Pro/Premier têm suas linhas de lista on-demand; gerações Nova 2 aparecem como sem preço até a tarifa de lista ser verificada, em vez de chutada. Um orçamento só diário (CHATCLI_DAILY_BUDGET_USD sem limite de sessão) anuncia seus próprios avisos de alerta e de excedido, e quando os dois limites existem o diário fala quando estourou; /cost export caminho.csv grava uma linha CSV por provedor/modelo mais um total. O snapshot (SessionCostData) carrega os records de uso por modelo, totais, timestamps e o nome da sessão nomeada quando houver. Snapshots também são salvos no encerramento e no /cost reset, e podados após 90 dias.
  • /cost last — o gasto da sessão (ou período) anterior
  • /cost sessions — os 10 snapshots mais recentes com data, requests, tokens e total, mais o registro de cache de cada sessão: fração de acerto, razão write/read, misses instáveis, expirados, rebuilds esperados e o TTL em vigor. A telemetria do cache de prompt é persistida com o snapshot de custo e restaurada com a sessão, então a estabilidade do prefixo pode ser comparada entre sessões em vez de morrer com o processo; as mesmas duas figuras vão para o OTLP como chatcli.cache.hit_pct e chatcli.cache.write_read_ratio.
  • /cost export [caminho] — snapshot atual em JSON, para pipelines e auditoria

Orçamento de Sessão

As três são relidas no /reload — não precisa reiniciar para mudar o orçamento no meio da sessão.

Níveis de Orçamento

O aviso é proativo: chat, agent e coder o imprimem no turno em que a sessão cruza um nível — você não precisa lembrar de rodar /cost. Sem CHATCLI_BUDGET_HARD_STOP, exceder o orçamento nunca interrompe a sessão; com ele, novos turnos (chat, loop do agent, workers do squad em pleno voo, participantes MoA, execuções agendadas, RPC/gateway) são bloqueados até o limite subir ou o /cost reset abrir um novo período.

Próximos Passos

Controle de Conversa

Use /compact para reduzir tokens e custos.

Modo One-Shot

Monitore custos em pipelines automatizados.

Eficiência de Tokens

As otimizações que o /cost permite verificar.

Roteamento de Modelos

Atribuição por modelo de cada turno roteado.