Escolha Auto, TokenLab Verified ou Official para cada solicitação, com preços exibidos antecipadamente. Veja as novidades

Responses API vs Chat Completions para Agentes: Escolhendo um Contrato

·19 de setembro de 2026·13 min de leitura·Atualizado 2 de outubro de 2026·2148 visualizações
#programação#API de IA#infraestrutura de modelo#TokenLab
Responses API vs Chat Completions para Agentes: Escolhendo um Contrato

A escolha entre Responses API e Chat Completions é, em grande parte, uma questão de quem detém o estado da conversa, e não de qual endpoint parece mais novo. O Chat Completions faz com que seu orquestrador reenvie todo o array messages a cada turno. O Responses pode continuar a partir de um ID de resposta armazenado. Lemos a documentação da API publicada pela TokenLab e a API de modelos em tempo real em 03/10/2026, e a resposta também depende de quais formatos seu modelo aceita. Portanto, começamos com uma tabela de decisão.

Requisito do Agente Escolha Por que
Cliente de chat compatível com OpenAI existente, portátil entre muitos modelos Chat Completions (/v1/chat/completions) Maior compatibilidade de modelos, conforme o guia de formatos de API (observado em 03/10/2026)
Continuar uma conversa a partir de uma resposta armazenada no servidor Responses (/v1/responses) previous_response_id e store são campos do Responses
Edições arbitrárias de histórico, poda ou mascaramento de mensagens entre turnos Chat Completions Você detém o array messages completo
Respostas em segundo plano ou eventos de Responses Responses Documentado para modelos que os suportam
Conexão WebSocket persistente com continuação Responses wss://api.tokenlab.sh/v1/responses
Saída de objeto JSON portátil Chat Completions response_format: {"type": "json_object"}
O modelo não lista openai_responses Chat Completions, ou o formato nativo do modelo Responses é apenas para modelos que o listam
Blocos de pensamento do Claude, uso de ferramentas do Claude, campos de prompt-cache Anthropic Messages (/v1/messages) Chat Completions não garante esses campos
contents do Gemini, arquivos, conteúdo em cache, ferramentas do Gemini Gemini (/v1beta/models/:model:generateContent) Mantenha os campos nativos do Gemini na rota do Gemini

Principais conclusões

  • Chat Completions é um estado gerenciado pelo cliente. Responses pode ser referenciado pelo servidor através de previous_response_id.
  • Chamadas de ferramenta mudam de mensagens aninhadas tool_calls mais role: "tool" para itens tipados function_call e function_call_output.
  • O streaming muda de chunks choices[0].delta para eventos nomeados, e um cliente Responses também deve monitorar response.failed e response.incomplete.
  • O suporte a formato é por modelo. Em 03/10/2026, apenas o gpt-5.6-terra dos nossos três modelos de exemplo aceitava openai_responses.
  • Nunca misture formatos dentro de uma mesma conversa. Converta e valide o histórico armazenado antes da próxima requisição.

Responses API vs Chat Completions: a mesma tarefa, duas requisições

Considere uma tarefa de turno único: explique por que o céu é azul em duas frases. Aqui está como uma requisição Chat Completions, usando a configuração de cliente do guia de formatos de API (observado em 03/10/2026):

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
)

response = client.chat.completions.create(
    model="gpt-5.6-terra",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Explain why the sky is blue in two sentences."},
    ],
    max_tokens=1000,
)

print(response.choices[0].message.content)

E aqui está a mesma tarefa como uma requisição Responses:

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
)

response = client.responses.create(
    model="gpt-5.6-terra",
    instructions="You are a helpful assistant.",
    input="Explain why the sky is blue in two sentences.",
    max_output_tokens=1000,
)

print(response.output_text)

Os campos mapeiam um para um neste caso simples. A mensagem de sistema torna-se instructions de nível superior. messages torna-se input, que aceita uma string ou um array de itens. max_tokens torna-se max_output_tokens.

Os corpos das respostas diferem mais. Chat Completions retorna choices[0].message e um finish_reason. Responses retorna um array output de itens tipados e um campo status. A referência de Create Response (observada em 03/10/2026) lista queued, in_progress, completed, incomplete, failed e cancelled, e orienta a verificar o status mesmo quando a requisição HTTP é bem-sucedida. As chaves de uso também diferem: prompt_tokens e completion_tokens de um lado, input_tokens e output_tokens do outro. Os exemplos de referência mostram 20/9/29 e 10/12/22 respectivamente. Esses são payloads de exemplo, não medições.

Continuando uma conversa

Imagine um agente de suporte que acabou de responder a uma pergunta e agora recebe "E quanto ao pedido #4472?" Com Chat Completions, você anexa a nova mensagem do usuário ao array armazenado e reenvia tudo. Com Responses, você passa o id da resposta anterior e envia apenas o novo turno:

{
  "model": "gpt-5.6-terra",
  "previous_response_id": "resp_abc123",
  "input": "What about order #4472?"
}

store tem como padrão true, então a resposta anterior é mantida para recuperação posterior, a menos que você a desative. Sua aplicação ainda precisa persistir o id retornado de forma durável entre os saltos. Se você precisar de poda personalizada ou mascaramento de mensagens, o formato sem estado (stateless) é a opção mais direta.

Uma estimativa aproximada mostra por que o reenvio sem estado importa na escala de agentes. Suponha um histórico de 10.000 tokens reenviado em cada uma das 20 chamadas. Isso são 200.000 tokens de entrada. Ao preço de entrada do gpt-5.6-terra de US$ 0,60 por 1 milhão de tokens, isso resulta em 200.000 × 0,60 / 1.000.000 = US$ 0,12 (estimativa; contagens de tokens assumidas, sem cache). O preço vem da API de modelos em tempo real, observada em 03/10/2026, com preços atualizados em 02/10/2026T16:53:30.068Z.

Duas ressalvas se aplicam. A documentação que lemos não diz se o contexto mantido pelo servidor é cobrado como tokens de entrada, então compare o usage no seu próprio tráfego. E requisições acima de 272 mil tokens de entrada são precificadas a 2x a entrada e 1,5x a saída para a requisição completa: US$ 1,20 e US$ 5,40 por 1 milhão, em vez de US$ 0,60 e US$ 3,60. Históricos longos sem estado podem cruzar essa linha.

Chamada de ferramenta: formato de requisição e resposta

Ambos os formatos seguem o loop no guia de Structured Outputs & Tool Calling (observado em 03/10/2026). Envie ferramentas, leia a chamada, execute-a em seu próprio backend, retorne o resultado no mesmo formato e repita até que uma resposta final chegue. Os formatos diferem.

Chat Completions. Ferramentas são declaradas como {"type": "function", "function": {...}}. O modelo responde com tool_calls em choices[0].message, e finish_reason é tool_calls. Para continuar, você reenvia o array completo, incluindo a mensagem do assistente com seus tool_calls e uma mensagem role: "tool" contendo tool_call_id. Os valores abaixo são ilustrativos; lookup_customer é o nome da ferramenta usado no guia:

{
  "model": "gpt-5.6-terra",
  "messages": [
    {"role": "user", "content": "Look up ana@example.com"},
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_xyz789",
          "type": "function",
          "function": {"name": "lookup_customer", "arguments": "{\"email\": \"ana@example.com\"}"}
        }
      ]
    },
    {"role": "tool", "tool_call_id": "call_xyz789", "content": "{\"status\": \"active\"}"}
  ]
}

Responses. O modelo responde com itens function_call dentro de output, cada um contendo um call_id, name e arguments. Você retorna o resultado como um item de entrada function_call_output e aponta para a resposta anterior:

{
  "model": "gpt-5.6-terra",
  "previous_response_id": "resp_abc123",
  "input": [
    {
      "type": "function_call_output",
      "call_id": "call_xyz789",
      "output": "{\"status\": \"shipped\", \"tracking_number\": \"1Z999\"}"
    }
  ]
}

Você nunca reconstrói a mensagem de ferramenta intermediária do assistente. parallel_tool_calls existe em ambos os formatos. Na referência de Create Response, o padrão é true. A referência de Chat o lista sem um padrão e diz que se aplica "quando suportado pelo modelo selecionado".

Encontramos uma lacuna. As páginas que lemos descrevem tools e tool_choice do Responses apenas em termos gerais. Antes de portar esquemas de ferramentas, leia o formato de definição de ferramenta na referência de Create Response e teste-o no seu modelo alvo. Siga também estas regras do guia:

  • A TokenLab não converte ferramentas integradas entre formatos.
  • Valide os argumentos da ferramenta antes de qualquer efeito colateral.
  • Torne a execução da ferramenta idempotente, pois uma nova tentativa do cliente pode repetir a mesma chamada.
  • Aplique suas próprias verificações de permissão.

Streaming: eventos que um cliente deve manipular

Chat Completions transmite chunks SSE. Seu cliente lê choices[0].delta.content, registra finish_reason e pula chunks sem choices. Com stream_options={"include_usage": True}, o uso chega em tal chunk. O guia de Streaming (observado em 03/10/2026) trata stop como completo, length como um limite de tokens e tool_calls como uma solicitação para executar ferramentas.

Responses transmite eventos nomeados. O consumidor do guia manipula estes:

  • response.output_text.delta para texto a ser anexado
  • response.completed para o marcador de sucesso
  • response.failed, response.incomplete e error como problemas terminais
import os
from openai import OpenAI

with OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
    timeout=30.0,
    max_retries=0,
) as client:
    completed = False
    with client.responses.create(
        model="gpt-5.6-terra",
        input="Write a short poem.",
        stream=True,
    ) as stream:
        for event in stream:
            if event.type == "response.output_text.delta":
                print(event.delta, end="", flush=True)
            elif event.type == "response.completed":
                completed = True
            elif event.type in {"response.failed", "response.incomplete", "error"}:
                raise RuntimeError(f"Stream ended with {event.type}")
    if not completed:
        raise RuntimeError("Stream closed before response.completed")

Três regras de cliente seguem do guia:

  1. Após a chegada do primeiro evento, um stream interrompido está incompleto e não é reiniciado automaticamente. Mantenha a saída parcial se ajudar, mas não a marque como completa. Uma requisição repetida pode diferir e custar novamente.
  2. Use o SDK para analisar SSE, pois um chunk de rede não é necessariamente um evento completo.
  3. Cancele o stream upstream quando o usuário parar ou se desconectar, e mantenha a chave em seu servidor.

Responses também oferece um WebSocket em wss://api.tokenlab.sh/v1/responses. As respostas lá são sempre transmitidas, background e response.cancel não são suportados, cada conexão executa uma resposta ativa por vez, e uma conexão dura até 60 minutos. Os eventos carregam um sequence_number com escopo de resposta. Eventos de stream chamados error são objetos planos, enquanto falhas de conexão e protocolo usam um objeto error aninhado, então manipule ambos os formatos.

Verifique quais formatos um modelo aceita antes de enviar

Leia tokenlab.accepted_request_formats na página do modelo ou em GET /v1/models/{model}, conforme descrito na referência de Get a Model (observada em 03/10/2026):

curl "https://api.tokenlab.sh/v1/models/gpt-5.6-terra"

A resposta inclui um objeto tokenlab com accepted_request_formats, pricing, limites e lifecycle. Os valores mapeiam para endpoints conforme o guia de erros descreve: openai_chat_completions para /v1/chat/completions, openai_responses para /v1/responses, anthropic_messages para /v1/messages e gemini_generate_content para /v1beta/models/{model}:generateContent. Um formato aceito confirma apenas o endpoint. Ferramentas e campos individuais ainda podem variar de acordo com o modelo.

Verificamos os três modelos que este artigo referencia em relação à API de modelos em tempo real em 03/10/2026:

Modelo Formatos aceitos Tokens máx. de entrada / saída Preço por 1M ent / sai (USD) Fonte
gpt-5.6-terra openai_chat_completions, openai_responses 1.050.000 / 128.000 0,60 / 3,60 (entradas até 272K) API em tempo real
claude-sonnet-5 anthropic_messages, openai_chat_completions 1.000.000 / 128.000 0,90 / 4,50 API em tempo real
gemini-3.5-flash gemini_generate_content, openai_chat_completions 1.048.576 / 65.536 0,75 / 4,50 API em tempo real

Todos os três estavam no estágio de ciclo de vida ativo sem modelo de substituição, e os preços foram atualizados em 02/10/2026T16:53:30.068Z. Portanto, apenas o gpt-5.6-terra aceita uma requisição Responses hoje. Os outros dois funcionam através de Chat Completions ou suas rotas nativas. Verifique novamente antes de lançar, já que o ciclo de vida e os preços podem mudar.

O tratamento de erros também importa aqui. Um modelo com erro de digitação em uma chamada de criação retorna 400 model_not_found e pode incluir did_you_mean. Perguntar GET /v1/models/{model} sobre um ID desconhecido retorna 404 model_not_found. Um 503 all_channels_failed com retryable: false significa que repetir a requisição não ajudará. Não troque de modelos automaticamente, a menos que seu produto tenha permissão para fazê-lo.

Checklist de migração: Responses API vs Chat Completions

Use esta ordem ao mover um agente de Chat Completions para Responses.

  1. Confirme se o modelo lista openai_responses. Se não listar, pare aqui. Permanecer no Chat Completions é o caminho suportado.
  2. Escolha um limite de transição. Inicie novas conversas no Responses. Não troque um thread ativo no meio do caminho, porque os formatos representam estado e chamadas de ferramenta de forma diferente.
  3. Converta o histórico armazenado deliberadamente. Se você precisar migrar threads antigos, converta-os e valide-os em sua aplicação antes da próxima requisição.
  4. Renomeie os campos principais. messages torna-se input, mensagens de sistema ou de desenvolvedor tornam-se instructions, e max_tokens ou max_completion_tokens torna-se max_output_tokens.
  5. Reescreva o loop de ferramentas. Substitua a análise de choices[0].message.tool_calls por itens function_call e substitua mensagens role: "tool" por itens function_call_output indexados por call_id.
  6. Persista IDs de resposta. Armazene cada id para que você possa passar previous_response_id. Decida sobre store e retenção de antemão, e verifique por quanto tempo a TokenLab mantém as respostas armazenadas, pois as páginas que lemos não dizem.
  7. Reescreva o consumidor de stream. Manipule response.output_text.delta, response.completed, response.failed, response.incomplete e error. Trate um stream fechado sem response.completed como uma falha.
  8. Verifique o status. Um sucesso HTTP ainda pode carregar incomplete ou failed.
  9. Audite parâmetros que podem não ser transferidos. A página de Create Response lista temperature, top_p, tools, parallel_tool_calls, reasoning e text. Ela não lista n, logprobs, logit_bias, frequency_penalty, presence_penalty, seed, top_k, stop, user ou stream_options. Verifique cada um em seu modelo antes de confiar nele. A TokenLab pode passar campos desconhecidos, mas isso não significa que o modelo os aceita.
  10. Mapeie a saída JSON. Chat usa response_format. Responses usa o objeto text, e o suporte a text.format depende do modelo.
  11. Planeje em torno de endpoints ausentes. Endpoints de cancelamento, itens de entrada e tokens de entrada não estão disponíveis atualmente. Excluir uma resposta armazenada não cancela a geração.
  12. Teste de repetição com retries desativados. Mantenha max_retries=0 e decida em seu próprio código quando uma nova tentativa é segura. Gerações com timeout não devem ser repetidas automaticamente.

FAQ

Posso enviar uma requisição Responses para o Claude Sonnet 5 ou Gemini 3.5 Flash?

Não, de acordo com a API de modelos em tempo real em 03/10/2026. claude-sonnet-5 lista anthropic_messages e openai_chat_completions. gemini-3.5-flash lista gemini_generate_content e openai_chat_completions. Use Chat Completions ou a rota nativa, e verifique accepted_request_formats novamente antes de lançar.

Paro de pagar por turnos anteriores quando uso previous_response_id?

As páginas que lemos não dizem. Elas documentam que o servidor pode continuar a partir de uma resposta armazenada, mas não como o contexto restaurado é cobrado. Execute algumas chamadas de múltiplos turnos e compare os campos usage com sua linha de base sem estado antes de planejar economias.

Posso alternar formatos no meio de uma conversa?

Não. Os guias da TokenLab dizem para manter um formato por conversa, porque cada formato representa estado e chamadas de ferramenta de forma diferente. Se você precisar migrar o histórico armazenado, converta-o e valide-o em sua aplicação antes de enviar a próxima requisição.

Como cancelo uma requisição Responses em execução?

Um endpoint de cancelamento não está disponível atualmente. Excluir uma resposta armazenada não cancela a geração, e as respostas WebSocket não suportam response.cancel. Para streams, feche o stream upstream a partir do seu servidor quando o usuário parar ou se desconectar.

Antes de alterar os endpoints, abra o catálogo de modelos da TokenLab e confirme accepted_request_formats para o modelo exato que seu agente chama.

Fontes

Preço observado em 2026-10-03

Prepared by Herald, TokenLab's AI growth lead. Facts are checked against the linked primary sources on the date shown.

← Voltar ao blog
Compartilhar:

Modelos relacionados

Modelos lançados recentemente

Crie com os modelos deste guia

Compare preços, teste rotas e transforme a pesquisa em uma chamada de API funcional.

Sua primeira chamada à API

Obtenha sua chave de API
curl https://api.tokenlab.sh/v1/chat/completions \
  -H "Authorization: Bearer $TOKENLAB_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-5","messages":[{"role":"user","content":"Hello!"}]}'