Function calling no GPT-6 Astra: como o modelo chama as suas funções e o que muda no seu código

fluxo de function calling no GPT-6 Astra entre tools, function_call e function_call_output
Resposta rápida

Function calling no GPT-6 Astra funciona num ciclo simples: você declara suas funções no parâmetro tools, o modelo devolve um item function_call, a SUA aplicação executa o código e devolve o resultado como function_call_output usando o mesmo call_id. O modelo nunca roda nada, ele só pede. No GPT-6 Astra, tool calling exige a Responses API (Chat Completions é suportado no modelo, mas não para tool calling), e a documentação recomenda sempre ativar strict: true no JSON Schema dos parâmetros. Preço da API no padrão standard: US$ 10 por milhão de tokens de entrada e US$ 50 de saída.

Fala aí, beleza? A OpenAI lançou o GPT-6 Astra em 3 de setembro de 2026 e tem um mal-entendido que aparece toda vez que alguém conecta um modelo a funções próprias pela primeira vez

O modelo NÃO executa a sua função

Ele só pede

Ele lê a descrição que você escreveu, decide que aquela função resolve o pedido do usuário, monta os argumentos e devolve isso pra você em forma de dado. Quem abre a conexão com o banco, quem chama a API do correio, quem trata o erro de timeout é o seu código, sempre

Essa fronteira é o post inteiro. Se ela ficar clara na sua cabeça, o resto é encanamento 🙂

O que você precisa antes de escrever a primeira função

Quatro coisas, e nenhuma delas é mágica

  • Acesso ao GPT-6 Astra pela Responses API: aqui tem uma pegadinha importante, o modelo suporta Chat Completions, mas NÃO para tool calling. Pra chamar função é Responses, ponto
  • Entender que entrada e saída são arrays de Items tipados: além do <code>message</code> que você já conhece, existem items de tipo <code>reasoning</code>, <code>function_call</code> e <code>function_call_output</code>. Não é mais uma lista de mensagens com role e content e acabou
  • Saber escrever JSON Schema: é assim que o modelo descobre quais argumentos a sua função aceita. Schema ruim vira argumento ruim
  • A função real já pronta e testada: sério, testa ela sozinha antes. Se ela quebra quando VOCÊ chama, ela vai quebrar quando o modelo chamar também

Já usa tools no Chat Completions e vai trazer pro Astra? Então passa pelo guia oficial de migração pra Responses API antes de sair copiando payload antigo. E se o projeto já roda em produção, vale montar um checklist antes de trocar o modelo pra não descobrir o que quebrou pelo suporte

Sobre custo pra dimensionar os testes: no padrão standard o preço é US$ 10 por milhão de tokens de entrada e US$ 50 por milhão de tokens de saída. Cada volta do ciclo de ferramenta manda contexto de novo, então rodar o ciclo várias vezes seguidas não é de graça, se liga nisso

Formação Claude Code
Formação Recomendada

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 45min

Passo a passo: o ciclo completo de uma chamada de função

1. Comece pela função que você já tem pronta

É aquela do pré-requisito, a que já roda sozinha no seu projeto. Nada muda nela pra virar ferramenta: continua sendo uma função normal, da sua linguagem, que faz UMA coisa

// nada de especial aqui, é código comum
function calcularFrete(cep, pesoKg, transportadora) {
  // consulta a sua API, aplica sua regra de negócio
  return { valor: "...", prazoDias: "..." }
}

O erro comum deste passo: começar a adaptar a função pra ela "conversar" com o modelo, tratando texto solto, adivinhando intenção. Não. Ela recebe argumentos e devolve resultado, igual sempre foi

2. Declare a função no parâmetro <code>tools</code>

As ferramentas que o modelo pode chamar vão no parâmetro <code>tools</code> da requisição. Uma function tool tem <code>type: "function"</code>, <code>name</code>, <code>description</code>, <code>parameters</code> (um JSON Schema) e <code>strict</code>

{
  "model": "gpt-6-astra",
  "tools": [
    {
      "type": "function",
      "name": "calcular_frete",
      "description": "Calcula valor e prazo de frete para um CEP de destino e um peso em quilos",
      "strict": true,
      "parameters": {
        "type": "object",
        "properties": {
          "cep": { "type": "string", "description": "CEP de destino, apenas números" },
          "peso_kg": { "type": "number", "description": "Peso do pacote em quilos" },
          "transportadora": {
            "type": ["string", "null"],
            "description": "Transportadora preferida, null quando não houver"
          }
        },
        "required": ["cep", "peso_kg", "transportadora"],
        "additionalProperties": false
      }
    }
  ]
}

O erro comum deste passo: escrever <code>description</code> preguiçosa tipo "calcula frete". Essa descrição é literalmente o manual que o modelo lê pra decidir quando pedir a função. Descrição vaga, chamada na hora errada

3. Ative o <code>strict: true</code> e cumpra as regras dele

Com <code>strict: true</code>, as chamadas de função aderem de forma confiável ao schema em vez de best effort. A documentação recomenda SEMPRE ativar o strict mode, e é um conselho barato de seguir

Mas ele cobra três coisas do seu schema:

  • <code>additionalProperties: false</code> em cada objeto dentro de <code>parameters</code>
  • todos os campos de <code>properties</code> marcados como <code>required</code>
  • campo opcional se denota adicionando <code>null</code> como opção de tipo (repare no <code>transportadora</code> do exemplo acima)

E se você simplesmente omitir o <code>strict</code>? A Responses API tenta normalizar o schema pra strict mode quando dá, e cai pra function calling best effort não estrito se o schema não for compatível

O erro comum deste passo: marcar campo como "opcional" tirando ele do <code>required</code>, do jeito que a gente faz em JSON Schema normal. No strict mode isso não passa, opcional é via <code>null</code> no type 😀

4. Envie a requisição e leia a resposta procurando um <code>function_call</code>

Aqui é onde muita gente tropeça de cara: você não pode assumir que veio texto

A saída é um array de Items. Pode vir <code>reasoning</code>, pode vir <code>message</code>, e pode vir um item <code>function_call</code> com o nome da função e os argumentos que o modelo montou, junto de um <code>call_id</code>

{
  "type": "function_call",
  "call_id": "call_123",
  "name": "calcular_frete"
}

O erro comum deste passo: código que pega o primeiro item da saída e joga direto na tela. Aí o usuário recebe silêncio, ou recebe um pedaço de raciocínio, e você acha que o modelo "não respondeu". Ele respondeu, só que pedindo ferramenta

5. Execute a função na sua aplicação

Aqui começa e termina a sua responsabilidade

Você pega os argumentos que vieram no item, valida, chama a sua função, trata exceção, aplica permissão, aplica limite. O modelo pediu, você decide se executa

O erro comum deste passo: tratar argumento vindo do modelo como se fosse confiável por natureza. O <code>strict</code> garante aderência ao schema, não garante que o CEP existe nem que aquele usuário pode consultar aquele pedido. Validação é sua, autorização é sua

6. Devolva o resultado como <code>function_call_output</code>

O resultado volta pro modelo como um item de tipo <code>function_call_output</code>, referenciando a chamada original pelo <code>call_id</code>. A saída pode ser JSON estruturado ou texto puro

{
  "type": "function_call_output",
  "call_id": "call_123",
  "output": "{\"valor\": \"...\", \"prazo_dias\": \"...\"}"
}

O erro comum deste passo: perder o <code>call_id</code> no meio do caminho, ou inventar um. É ele que amarra o resultado à chamada certa. Sem o <code>call_id</code> original, o modelo não sabe do que você tá falando

7. Continue a conversa com <code>previous_response_id</code>

Em vez de remontar o histórico inteiro e reenviar tudo na próxima requisição, dá pra encadear com <code>previous_response_id</code>

{
  "model": "gpt-6-astra",
  "previous_response_id": "resp_123",
  "input": [
    {
      "type": "function_call_output",
      "call_id": "call_123",
      "output": "..."
    }
  ]
}

O erro comum deste passo: reenviar o histórico completo POR CIMA do encadeamento, duplicando contexto e pagando token à toa

Duas notas de parâmetro que vão te poupar tempo de debug: o <code>reasoning.effort</code> do GPT-6 Astra aceita <code>low</code>, <code>medium</code>, <code>high</code>, <code>xhigh</code> e <code>max</code>, e o modelo não suporta o effort <code>none</code>. E ele não aceita valores customizados de <code>temperature</code> ou <code>top_p</code>, nem <code>logprobs</code>, então se você tem um wrapper antigo mandando temperature fixa, é ali que o seu 400 vai nascer

A função que o modelo pede é uma função comum (e isso muda como você escreve)

Agora a parte que ninguém fala

Não existe "função de IA". A função que o GPT-6 Astra pede é a mesma função que você escreveria pra um botão da tela chamar

Função é bloco de código criado pra agrupar uma lógica que se repete: escrita uma vez e reutilizada quantas vezes precisar. Quando a mesma lógica aparece repetida, espalhada por vários pontos do programa, esse é o gatilho clássico pra extrair uma função: o programa fica menor, mais fácil de manter e de ler

E tem um detalhe que combina demais com o assunto deste post: criar a função sozinha não produz saída nenhuma

Nada acontece

Só quando eu chamo a função, escrevendo o nome dela seguido de parênteses, é que o código de dentro roda. E essa chamada pode ser repetida quantas vezes o programador quiser

Troque "programador" por "modelo" e você entendeu function calling 😀

Agora junta isso com argumento: ao declarar a função com argumento, você se comprometeu a passar esse valor na chamada, senão a função não funciona direito. E trocar o valor passado entre uma chamada e outra é a mesma função atendendo situações diferentes

É isso que o JSON Schema faz por você: ele é o contrato que diz ao modelo quais valores ele precisa passar

Por isso três boas práticas velhas viram obrigação aqui:

  • Parâmetro bem nomeado: <code>peso_kg</code> é lido pelo modelo, <code>p2</code> não diz nada pra ninguém
  • Responsabilidade única: função que faz três coisas gera descrição confusa e chamada errada
  • Retorno previsível: a função devolve alguma coisa, e esse valor você usa direto ou guarda numa variável. No ciclo de ferramenta, esse retorno é o que vira <code>output</code>, então formato instável do seu lado é ruído do lado do modelo

Vídeo: funções na prática, do zero

Se parâmetro e retorno ainda são terreno movediço pra você, firma a base antes de expor qualquer função ao modelo

tool_choice, parallel e async: quando cada um resolve o seu problema

Esses três vivem sendo confundidos, então bora separar

tool_choice controla ou orienta o comportamento de uso de ferramenta. Dá pra guiar ou forçar o uso, e o valor <code>"none"</code> força o modelo a não usar nenhuma função. Uso clássico: aquele endpoint que só deve resumir texto, sem encostar em ferramenta nenhuma

Parallel tool calls é o modelo pedir várias ferramentas no mesmo turno. Pense em "consulta o estoque E calcula o frete E busca o cupom", tudo de uma vez

Async tool calling é outra história: você define <code>async: true</code> na function tool ou custom tool, e o modelo continua raciocinando, chamando outras ferramentas ou respondendo partes independentes do pedido enquanto a sua aplicação executa aquela ferramenta lenta. Quando o resultado ficar pronto, você devolve usando o <code>call_id</code> original

{
  "type": "function",
  "name": "gerar_relatorio_pesado",
  "async": true,
  "strict": true,
  "parameters": { "type": "object", "properties": {}, "required": [], "additionalProperties": false }
}

Parallel e async resolvem problemas diferentes, são capacidades complementares e não equivalentes

Recurso O que resolve Cuidado
tool_choice guia ou força o comportamento de uso de ferramenta o valor "none" força não usar nenhuma função
parallel tool calls o modelo pede várias ferramentas no mesmo turno não é substituto de async
async: true adia o resultado da ferramenta demorada enquanto o modelo segue trabalhando não se aplica às ferramentas hospedadas (built-in) da OpenAI

E tem uma orientação explícita da documentação que vale colar na parede: em modo multi-agente, não combine async tools com parallel tool calls

O async vale pras function tools e custom tools executadas pela SUA aplicação. Ferramenta hospedada da OpenAI não entra nessa

Por onde começar hoje

Recapitulando a fronteira, que é o que importa de verdade: o modelo decide e pede, o seu código executa

Tudo que está antes do <code>function_call</code> é responsabilidade do modelo. Tudo que está entre o <code>function_call</code> e o <code>function_call_output</code> é 100% seu: validar, autorizar, executar, tratar erro, formatar retorno

O próximo passo concreto é pequeno de propósito

Pega UMA função que você já tem no projeto, dessas curtinhas que já funcionam. Descreve ela em JSON Schema com <code>strict: true</code>, respeitando <code>additionalProperties: false</code> e todos os campos em <code>required</code>. Manda pela Responses API, lê a saída procurando o item <code>function_call</code>, executa, devolve o <code>function_call_output</code> com o <code>call_id</code> certo e encadeia com <code>previous_response_id</code>

Ciclo completo, uma vez, ponta a ponta

Depois que isso rodar limpo, aí sim você pensa em async, multi-agente e o resto do circo. E se a dúvida ainda for qual modelo usar em cada tarefa, essa decisão vem antes de escrever schema nenhum…

Bora testar? 😀

até o próximo post!

Perguntas frequentes

Function calling no GPT-6 Astra funciona pelo Chat Completions?

O modelo até aceita Chat Completions, mas pra tool calling isso não vale. A OpenAI é direta nisso: pra function calling GPT-6 Astra é Responses API, sem alternativa. Se o seu projeto ainda chama tools pelo Chat Completions, o caminho é seguir o guia oficial de migração antes de trocar o modelo.

Pra que serve o tool_choice e ele substitui o strict mode?

São coisas diferentes. O tool_choice controla SE e QUAL ferramenta o modelo deve considerar usar, e dá pra forçar nenhuma chamada passando "none". Já o strict, ativado com strict: true dentro da definição da função, controla a aderência dos argumentos ao JSON Schema, não se a ferramenta é pedida

Qual a diferença entre parallel tool calls e async tool calling?

Parallel deixa o modelo pedir várias ferramentas no mesmo turno. Async deixa a sua aplicação demorar pra devolver o resultado de UMA ferramenta enquanto o modelo segue raciocinando ou respondendo outra parte do pedido. São capacidades complementares, não a mesma coisa com nome diferente

Dá pra combinar async tool calling com parallel tool calls em modo multi-agente?

A própria OpenAI orienta não combinar as duas coisas em modo multi-agente. E vale lembrar que async só vale pra function tools e custom tools que a sua aplicação executa, não pras ferramentas hospedadas (built-in) da OpenAI, tipo web search

Quanto custa usar function calling com o GPT-6 Astra pela API?

No padrão standard, US$ 10 por milhão de tokens de entrada e US$ 50 por milhão de tokens de saída. Repara que cada volta do ciclo function_call para function_call_output manda contexto de novo pro modelo, então um fluxo com várias chamadas encadeadas pesa mais do que parece

Pra que serve o previous_response_id no ciclo de function calling?

Ele permite encadear a próxima chamada sem reenviar todo o histórico de Items na requisição seguinte. Isso ajuda bastante quando o ciclo de function_call e function_call_output já foi longo e você não quer montar o array inteiro de novo a cada volta




Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares