APIs de LLM — anatomia de uma chamada

TL;DR

Uma chamada de API de LLM é um POST HTTP com um array de mensagens (system, user, assistant), parâmetros de controle (temperature, max_tokens) e opcionalmente definições de tools. A resposta vem com o texto gerado e metadados de uso (tokens consumidos). Entender essa anatomia é fundamental para debugar problemas, otimizar custos e construir agentes — porque cada “conversa” com um LLM é na verdade uma série de chamadas HTTP stateless.

O que ninguém te conta sobre “memória” de conversa

Você abre o Claude, faz uma pergunta, e na segunda mensagem pergunta “pode expandir o ponto 3?“. O modelo responde como se lembrasse o que acabou de dizer. Parece memória. Não é.

Cada chamada de API é completamente stateless. O servidor não guarda nada entre requests. Quando você envia a segunda mensagem, sua ferramenta (Claude.ai, Cursor, API direta) está enviando toda a conversa desde o início de volta ao servidor: a mensagem 1, a resposta 1, a mensagem 2 — tudo num único array messages. O modelo vê o histórico completo a cada vez porque você reenviu tudo.

Isso tem duas consequências que afetam custo e arquitetura:

  1. Custo cresce com o número de turns. Numa conversa de 10 messages com sistema de 2k tokens, a décima chamada já envia ~15-20k tokens de input antes mesmo de você digitar sua pergunta. É custo acumulativo, não fixo.
  2. Você controla o que “o modelo lembra”. Como você monta o array messages é uma decisão de engenharia. Pode truncar histórico antigo, resumir turnos, excluir mensagens irrelevantes. “Memória” de LLM é sempre implementada, nunca mágica.

Essa compreensão é o que separa quem usa uma API de LLM de quem entende o que está fazendo — e o segundo tipo constrói sistemas mais baratos, mais rápidos e mais confiáveis.

O que é

A API de um LLM é a interface HTTP que permite enviar prompts e receber respostas programaticamente. O formato Chat Completions (padronizado pela OpenAI e adotado por quase todos os providers) é o padrão da indústria em 2026.

Cada chamada é stateless — o modelo não “lembra” interações anteriores. O “histórico de conversa” é reenviado integralmente a cada request.

Por que importa

  • Custo — cada campo do request consome tokens. Campos desnecessários desperdiçam dinheiro.
  • Debugging — 90% dos problemas com LLMs estão no request (contexto mal formado, roles errados, temperature inadequada)
  • Agentes — ferramentas como Claude Code e Cursor constroem requests complexos por baixo dos panos. Entender a anatomia ajuda a configurá-los melhor.

Como funciona

A jornada completa de uma chamada (~400ms)

Antes de detalhar request e response, vale entender o que acontece do lado do servidor entre o POST sair da sua máquina e a resposta começar a streamar de volta. Toda chamada percorre sete estágios — e ~95% do tempo de espera está concentrado em apenas um deles.

EstágioTempo típicoO que rola
1. API Gateway~5msTLS termination, auth (API key), rate limiting — onde o billing começa
2. Load Balancer~2msRouting geográfico, health checks, escolha do cluster de GPU
3. Tokenization~3msTexto vira IDs de tokens (BPE/SentencePiece). É aqui que Token Count × $/1k é calculado
4. Model Router~1msEscolhe qual cluster atende (small/large/MoE/embedding) — model-version routing acontece aqui
5. Inference Engine300–800msPrefill (paralelo) + Decode (autoregressivo, token a token) + Attention + GPU compute
6. Post-Processing~5msSafety filters, format validation, stop sequences
7. Response & Billing~5msSerialização JSON (ou stream SSE), cálculo final de custo, logging

Três implicações práticas pro que vem a seguir neste artigo:

  • Tokenização (estágio 3) acontece antes da inferência — por isso tools e mensagens longas no messages aumentam o custo antes mesmo do modelo gerar qualquer coisa.
  • Inference engine (estágio 5) domina a latência — otimizações como prompt caching e streaming atacam exatamente essa fase.
  • Billing começa no estágio 1 — toda request autenticada conta, mesmo que falhe depois. Por isso usage no response é o seu único termômetro confiável.

Anatomia do Request

{
  "model": "claude-sonnet-4.6",
  "max_tokens": 4096,
  "temperature": 0.3,
  "system": "Você é um engenheiro de software sênior...",
  "messages": [
    {
      "role": "user",
      "content": "Refatore este código para usar async/await"
    },
    {
      "role": "assistant",
      "content": "Vou analisar o código..."
    },
    {
      "role": "user",
      "content": "Agora adicione tratamento de erros"
    }
  ],
  "tools": [
    {
      "name": "read_file",
      "description": "Lê conteúdo de um arquivo",
      "input_schema": {
        "type": "object",
        "properties": {
          "path": {"type": "string"}
        }
      }
    }
  ]
}

Campos do Request

CampoObrigatórioDescriçãoImpacto em tokens
modelSimQual modelo usarNenhum (metadata)
messagesSimArray de mensagens com rolesPrincipal consumidor de input tokens
systemNão*Instruções de sistemaInput tokens (cacheable)
max_tokensSim†Limite máximo de outputLimita output tokens (e custo)
temperatureNãoCriatividade (0=determinístico, 1=criativo)Nenhum
top_pNãoNucleus samplingNenhum
toolsNãoDefinições de ferramentasConsumidor oculto — schemas JSON são tokens
tool_choiceNãoForçar ou sugerir uso de toolNenhum
stopNãoSequências que param a geraçãoNenhum
streamNãoHabilitar streaming SSENenhum

*Em Anthropic é campo separado; em OpenAI é uma mensagem com role: "system". †Anthropic exige; OpenAI tem default.

Roles e sua função

RoleQuem falaConsumoCacheable?
systemO desenvolvedor (instruções)Input tokens✅ Sim (prompt caching)
userO humano (perguntas, contexto)Input tokens⚠️ Parcial
assistantO modelo (respostas anteriores)Input tokens (no histórico)⚠️ Parcial
toolResultado de uma ferramentaInput tokens❌ Geralmente não

Anatomia do Response

{
  "id": "msg_01XF...",
  "model": "claude-sonnet-4.6",
  "content": [
    {
      "type": "text",
      "text": "Aqui está o código refatorado..."
    }
  ],
  "usage": {
    "input_tokens": 2847,
    "output_tokens": 1253,
    "cache_read_input_tokens": 1500,
    "cache_creation_input_tokens": 0
  },
  "stop_reason": "end_turn"
}

O campo usage — seu monitor de custos

MétricaSignificado
input_tokensTotal de tokens de input (prompt + histórico + tools)
output_tokensTokens gerados pelo modelo
cache_read_input_tokensTokens lidos do cache (mais baratos)
cache_creation_input_tokensTokens escritos no cache (custo normal)

O custo acumulativo de uma conversa de agente

Cada turn de uma conversa de agente reenvia o histórico inteiro. O custo de input cresce quase linearmente com os turns:

xychart-beta
    title "Tokens de input acumulados por turn (system 2k + tool schemas 3k)"
    x-axis ["Turn 1", "Turn 5", "Turn 10", "Turn 20", "Turn 50"]
    y-axis "Input tokens (k)" 0 --> 300
    line [6, 22, 50, 110, 280]

Turn 50 de uma sessão de agente envia ~280k tokens de input — só em histórico acumulado, antes de qualquer work novo. Por isso sessões longas de agente são desproporcionalmente caras, e por isso técnicas como prompt caching (cachear o system prompt e início do histórico) e context compression (resumir turnos antigos) são essenciais em agentes de produção.

Temperature e suas consequências

TemperatureComportamentoQuando usar
0.0Determinístico, sempre a mesma respostaCódigo, refactoring, dados estruturados
0.1–0.3Quase determinístico, pequenas variaçõesCoding geral, análise
0.5–0.7Criativo mas controladoEscrita de docs, brainstorming
0.8–1.0Altamente variávelGeração criativa, exploração

O ciclo de um agente

O que uma ferramenta como Claude Code faz por baixo dos panos:

sequenceDiagram
    participant U as Usuário
    participant A as Agente
    participant API as LLM API
    participant T as Tools

    U->>A: "Corrija o bug no auth"
    A->>API: Request 1 (system + user + tools)
    API->>A: "Preciso ler o arquivo auth.ts"
    A->>T: read_file("auth.ts")
    T->>A: conteúdo do arquivo
    A->>API: Request 2 (system + histórico + tool_result)
    API->>A: "Encontrei o bug, vou corrigir..."
    A->>T: write_file("auth.ts", novo_conteúdo)
    A->>API: Request 3 (sistema + histórico completo)
    API->>A: "Correção aplicada. Rodando testes..."
    A->>U: Resultado final

Cada seta para a API é uma chamada HTTP completa, com o histórico inteiro reenviado. É por isso que sessões longas de agentes ficam caras.

Armadilhas

"A API lembra o que falei antes"

Não. Cada chamada é stateless. O histórico é reenviado integralmente — a “memória” é uma ilusão mantida pelo cliente, não pelo servidor.

Ignorar tool definitions nos tokens

Schemas JSON de ferramentas consomem 500-2000 tokens facilmente. 10 ferramentas com descriptions verbosas podem consumir 5k+ tokens de input em cada chamada — antes mesmo do modelo ler sua mensagem.

max_tokens muito alto

Não custa nada configurar um valor alto “por segurança”, mas se o modelo gerar até o limite, você paga por isso. Defina o mínimo razoável para a tarefa.

Temperature 0 para tudo

Temperature 0 é boa para código, mas pode causar repetição em texto longo. Para documentação e prosa, prefira 0.2-0.4.

Não monitorar usage

Se você não loga os tokens consumidos por chamada, não tem como identificar onde está o desperdício — é o único termômetro confiável de custo real.

Exemplo: erro 400 por omitir max_tokens

Diferente da OpenAI (que tem um default), a Anthropic exige max_tokens em toda chamada. Uma request sem esse campo falha antes mesmo de chegar ao modelo:

// Request — falha
POST https://api.anthropic.com/v1/messages
{
  "model": "claude-sonnet-4.6",
  "messages": [
    { "role": "user", "content": "Explique o que é prompt caching" }
  ]
}
// Response — 400 Bad Request
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "max_tokens: Field required"
  }
}

O erro é rejeitado no estágio de validação da request (antes da tokenização e da inferência) — por isso é barato de errar, mas também fácil de não perceber em testes rápidos onde a resposta “parece” ter funcionado até então. Outro erro comum da mesma família: enviar messages com roles fora de ordem (dois user seguidos sem assistant entre eles) — a API rejeita com invalid_request_error porque a conversa precisa alternar user/assistant.

Como explicar em inglês

An LLM API call is a stateless HTTP POST containing a messages array (system, user, assistant roles), control parameters (temperature, max_tokens), and optional tool definitions. The server holds no state between requests — “conversation memory” is an illusion maintained by the client resending the full message history on every call. This has a critical cost implication: in an agentic session, turn 10 sends 10× the tokens of turn 1, because it includes the entire prior conversation as input tokens. The usage field in the response is the authoritative cost meter — input_tokens counts everything you sent (messages + system prompt + tool definitions), output_tokens counts what the model generated. Tool definitions are a hidden input cost: 10 tools with verbose descriptions can consume 5k+ input tokens per call before the model even reads your message.

PTEN
Chamada de APIAPI call
Sem estadoStateless
MensagensMessages
Papel / funçãoRole
Prompt de sistemaSystem prompt
Tokens de entradaInput tokens
Tokens de saídaOutput tokens
Definições de ferramentasTool definitions
Esquema de ferramentaTool schema
Motivo de paradaStop reason / finish reason
Streaming de eventos enviados pelo servidorServer-Sent Events (SSE) streaming

Ver mais

O que vem a seguir

Entender a anatomia do request e do response responde “o que é enviado e o que volta”. Mas o campo usage só mostra a contagem de tokens — não diz quanto isso custa em dinheiro, nem por que o mesmo request pode custar 10x mais dependendo do modelo escolhido. É esse o assunto de 12 - Pricing de APIs — como calcular custos: como traduzir input_tokens e output_tokens em reais/dólares, e por que cache_read_input_tokens é a alavanca de custo mais subestimada em agentes de produção.

Veja também

Referências

  • OpenAIChat Completions API Reference (2026). Documentação canônica do formato.
  • AnthropicMessages API Reference (2026). Formato com variações (system separado, tool use).
  • GoogleGemini API Reference (2026). Formato com integrações multimodais.