Phase 13 - Lesson 02
Deep Dive em Function Calling — OpenAI, Anthropic, Gemini
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Os três principais provedores convergiram para o mesmo loop de chamada de ferramenta em 2024 e depois divergiram em todo o resto. A OpenAI usa
toolsetool_calls. A Anthropic usa blocostool_useetool_result. O Gemini usafunctionDeclarationse correlação por ID único. Esta lição compara os três lado a lado para que o código executado em um provedor não quebre quando você o portar para outro.
Tipo: Build Linguagens: Python (stdlib, schema translators) Pré-requisitos: Phase 13 · 01 (the tool interface) Tempo: ~75 minutos
Objetivos de Aprendizado
- Apresentar as três diferenças de formato entre as cargas úteis (payloads) de chamada de função da OpenAI, Anthropic e Gemini (declaração, chamada, resultado).
- Traduzir uma declaração de ferramenta entre os formatos dos três provedores e prever onde as restrições do modo estrito (strict-mode) serão diferentes.
- Usar
tool_choiceem cada provedor para forçar, proibir ou selecionar automaticamente chamadas de ferramentas. - Conhecer os limites rígidos de cada provedor (quantidade de ferramentas, profundidade do esquema, tamanho dos argumentos) e as assinaturas de erro que cada um emite quando esses limites são violados.
O Problema
O formato de uma requisição de chamada de função difere conforme o provedor. Três exemplos concretos de pilhas de produção de 2026:
OpenAI Chat Completions / Responses API. Você envia tools: [{type: "function", function: {name, description, parameters, strict}}]. A resposta do modelo contém choices[0].message.tool_calls: [{id, type: "function", function: {name, arguments}}] onde arguments é uma string JSON que você deve analisar (parse). O modo estrito (strict: true) impõe a conformidade do esquema por meio de decodificação restrita.
Anthropic Messages API. Você envia tools: [{name, description, input_schema}]. A resposta retorna como content: [{type: "text"}, {type: "tool_use", id, name, input}]. input já vem analisado (um objeto, não uma string). Você responde com uma nova mensagem de user contendo um bloco {type: "tool_result", tool_use_id, content}.
Google Gemini API. Você envia tools: [{functionDeclarations: [{name, description, parameters}]}] (aninhado sob functionDeclarations). A resposta chega como candidates[0].content.parts: [{functionCall: {name, args, id}}] onde id é único no Gemini 3 ou superior para correlação de chamadas paralelas. Você responde com {functionResponse: {name, id, response}}.
O mesmo loop. Nomes de campos diferentes, aninhamento diferente, convenções diferentes de string vs. objeto, mecanismos de correlação diferentes. Uma equipe que escreve um agente de clima na OpenAI leva dois dias para portá-lo para a Anthropic e outro dia para o Gemini apenas para ajustar a infraestrutura de comunicação.
Esta lição constrói um tradutor que unifica os três formatos em uma única declaração de ferramenta canônica e faz o roteamento na borda (edge). A Phase 13 · 17 generaliza esse mesmo padrão em um gateway de LLM.
O Conceito
A estrutura comum
Cada provedor precisa de cinco coisas:
- Lista de ferramentas. Nome, descrição e esquema de entrada para cada ferramenta.
- Escolha de ferramenta. Forçar uma ferramenta específica, proibir ferramentas ou deixar o modelo decidir.
- Emissão da chamada. Saída estruturada especificando o nome da ferramenta e os argumentos.
- ID da chamada. Correlacionar a resposta com a chamada correta (importante para chamadas paralelas).
- Injeção de resultado. Uma mensagem ou bloco que vincula o resultado de volta à chamada correspondente.
Diferenças de formato, campo por campo
| Aspecto | OpenAI | Anthropic | Gemini |
|---|---|---|---|
| Envelope de declaração | {type: "function", function: {...}} |
{name, description, input_schema} |
{functionDeclarations: [{...}]} |
| Campo do esquema | parameters |
input_schema |
parameters |
| Contêiner de resposta | tool_calls[] na mensagem do assistente |
content[] do tipo tool_use |
parts[] de tipo functionCall |
| Tipo dos argumentos | JSON em formato de string | objeto analisado (parsed) | objeto analisado (parsed) |
| Formato de ID | call_... (gerado pela OpenAI) |
toolu_... (Anthropic) |
UUID (Gemini 3+) |
| Bloco de resultado | papel (role) tool, tool_call_id |
user com tool_result, tool_use_id |
functionResponse com id correspondente |
| Forçar uma ferramenta | tool_choice: {type: "function", function: {name}} |
tool_choice: {type: "tool", name} |
tool_config: {function_calling_config: {mode: "ANY"}} |
| Proibir ferramentas | tool_choice: "none" |
tool_choice: {type: "none"} |
mode: "NONE" |
| Esquema estrito (strict) | strict: true |
esquema-é-esquema (sempre imposto) | responseSchema no nível da requisição |
Limites que você realmente vai atingir
- OpenAI. 128 ferramentas por requisição. Profundidade do esquema de 5 níveis. String de argumentos <= 8192 bytes. O modo estrito exige que não haja
$ref, nem sobreposição deoneOf/anyOf/allOf, e que todas as propriedades sejam listadas emrequired. - Anthropic. 64 ferramentas por requisição. Profundidade do esquema efetivamente ilimitada, mas com limite prático de 10. Sem flag de modo estrito; o esquema é um contrato e o modelo tende a cumpri-lo.
- Gemini. 64 funções por requisição. Os tipos de esquema são um subconjunto da OpenAPI 3.0 (pequena divergência em relação ao JSON Schema 2020-12). Chamadas paralelas usam IDs únicos desde o Gemini 3.
Comportamento do tool_choice
Três modos que todos os provedores suportam, com nomes diferentes.
- Auto. O modelo escolhe entre a ferramenta ou texto. Padrão.
- Required / Any. O modelo deve chamar pelo menos uma ferramenta.
- None. O modelo não deve chamar nenhuma ferramenta.
Além disso, há um modo exclusivo para cada provedor:
- OpenAI. Força uma ferramenta específica pelo nome.
- Anthropic. Força uma ferramenta específica pelo nome; a flag
disable_parallel_tool_usesepara o uso de ferramenta única de uso múltiplo. - Gemini. O
mode: "VALIDATED"direciona todas as respostas por um validador de esquema, independentemente da intenção do modelo.
Chamadas paralelas
O parallel_tool_calls: true da OpenAI (padrão) emite múltiplas chamadas em uma única mensagem do assistente. Você executa todas elas e responde com uma mensagem em lote com papel (role) tool contendo uma entrada para cada tool_call_id. Historicamente, a Anthropic fazia chamadas únicas; disable_parallel_tool_use: false (padrão a partir do Claude 3.5) habilita chamadas múltiplas. O Gemini 2 permitia chamadas paralelas mas não fornecia IDs estáveis; o Gemini 3 adiciona UUIDs para que respostas fora de ordem se correlacionem de forma limpa.
Streaming
Todos os três suportam chamadas de ferramenta via streaming. O formato de transmissão difere:
- OpenAI. Fragmentos delta de
tool_calls[i].function.argumentschegam incrementalmente. Você os acumula até que ocorrafinish_reason: "tool_calls". - Anthropic. Eventos de início de bloco (block-start), delta de bloco (block-delta) e fim de bloco (block-stop). Fragmentos de
input_json_deltatrazem os argumentos parciais. - Gemini.
streamFunctionCallArguments(novo no Gemini 3) emite fragmentos com umfunctionCallIdpara que múltiplas chamadas paralelas possam ser intercaladas.
A Phase 13 · 03 aprofunda-se na remontagem de chamadas paralelas + streaming. Esta lição foca nos formatos de declaração e de chamada única.
Erros e correção
Erros de argumentos inválidos também se apresentam de forma diferente.
- OpenAI (não estrito). O modelo retorna
arguments: "{bad json}", sua análise (parse) de JSON falha, você injeta uma mensagem de erro e faz uma nova chamada. - OpenAI (estrito). A validação acontece durante a decodificação; JSON inválido é impossível, mas um
refusal(recusa) pode surgir. - Anthropic. O
inputpode conter campos inesperados; o esquema é consultivo. Valide no servidor. - Gemini. Particularidade da OpenAPI 3.0:
enumem campos de objeto é ignorado silenciosamente; faça a validação você mesmo.
O padrão tradutor
Uma declaração de ferramenta canônica no seu código se parece com esta (você escolhe o formato):
Tool(
name="get_weather",
description="Use when ...",
input_schema={"type": "object", "properties": {...}, "required": [...]},
strict=True,
)
Três pequenas funções traduzem isso para os formatos dos três provedores. O código de teste em code/main.py faz exatamente isso e, em seguida, simula um ciclo completo (round-trip) de uma chamada de ferramenta fictícia em cada formato de resposta de provedor. Não é necessária conexão de rede — esta lição ensina os formatos, não o protocolo HTTP.
Equipes de produção envolvem esse tradutor em AbstractToolset (Pydantic AI), UniversalToolNode (LangGraph) ou BaseTool (LlamaIndex). A Phase 13 · 17 entrega um gateway que expõe uma API no formato da OpenAI à frente de qualquer um dos três.
Como Usar
O code/main.py define uma classe de dados canônica Tool e três tradutores que emitem o JSON de declaração para OpenAI, Anthropic e Gemini. Em seguida, ele analisa (parse) uma resposta de provedor construída manualmente para cada formato no mesmo objeto de chamada canônico, demonstrando que a semântica é idêntica nos bastidores. Execute-o e compare as três declarações lado a lado.
O que observar:
- Os três blocos de declaração diferem apenas no envelope e nos nomes dos campos.
- Os três blocos de resposta diferem no local onde a chamada reside (nível superior de
tool_calls, blococontent[], entradaparts[]). - Uma única função
canonical_call()extrai{id, name, args}de todos os três formatos de resposta.
Como Entregar
Esta lição produz o arquivo outputs/skill-provider-portability-audit.md. Dada uma integração de chamada de função feita para um provedor, a habilidade produz uma auditoria de portabilidade: em quais limites do provedor ela se apoia, quais campos precisam ser renomeados e o que quebra quando portada para cada outro provedor.
Exercícios
Execute
code/main.pye verifique se os três JSONs de declaração dos provedores serializam o mesmo objetoToolsubjacente. Modifique a ferramenta canônica para adicionar um parâmetro enum e confirme que apenas o tradutor do Gemini precisa tratar a particularidade da OpenAPI.Adicione um analisador
ListToolsResponsepara cada provedor que extraia a lista de ferramentas que um modelo retorna após uma chamada delist_toolsou de descoberta. A OpenAI não possui isso nativamente; note essa assimetria.Implemente a conversão de
tool_choice: mapeie umToolChoice(mode="force", tool_name="x")canônico para todos os três formatos de provedor. Em seguida, mapeiemode="any"emode="none". Consulte a tabela comparativa da lição.Escolha um dos três provedores e leia seu guia de chamada de função de ponta a ponta. Encontre um campo em sua especificação de esquema que os outros dois não suportam. Candidatos:
strictda OpenAI,disable_parallel_tool_useda Anthropic,function_calling_config.allowed_function_namesdo Gemini.Escreva um vetor de teste: uma chamada de ferramenta cujos argumentos violam o esquema declarado. Execute-o no validador de cada provedor (o da biblioteca padrão na Lição 01 servirá como proxy) e registre quais erros ocorrem. Documente qual provedor você usaria em produção visando maior rigor (strictness).
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| Function calling | "Uso de ferramentas" (Tool use) | API em nível de provedor para emissão estruturada de chamada de ferramenta |
| Declaração de ferramenta | "Especificação de ferramenta" | Nome + descrição + payload de entrada JSON Schema |
tool_choice |
"Forçar / proibir" | Modos auto / required / none / nome-específico |
| Modo estrito (Strict mode) | "Imposição de esquema" | Flag da OpenAI que restringe a decodificação para corresponder ao esquema |
Bloco tool_use |
"Formato de chamada da Anthropic" | Bloco de conteúdo inline com id, name, input |
Parte functionCall |
"Formato de chamada do Gemini" | Uma entrada parts[] contendo name, args e id |
| Argumentos como string | "JSON serializado em string" | A OpenAI retorna argumentos como uma string JSON, não como um objeto |
| Chamadas de ferramenta paralelas | "Fan-out em um único turno" | Múltiplas chamadas de ferramenta em uma única mensagem do assistente |
| Recusa (Refusal) | "Modelo declina" | Bloco de recusa exclusivo do modo estrito em vez de uma chamada |
| Subconjunto OpenAPI 3.0 | "Particularidade do esquema do Gemini" | O Gemini usa um dialeto semelhante ao JSON Schema com pequenas diferenças |
Leituras Adicionais
- OpenAI — Function calling guide — referência canônica incluindo modo estrito e chamadas paralelas
- Anthropic — Tool use overview — semântica dos blocos
tool_useetool_result - Google — Gemini function calling — chamadas paralelas, IDs únicos e subconjunto da OpenAPI
- Vertex AI — Function calling reference — interface corporativa do Gemini
- OpenAI — Structured outputs — detalhes da imposição de esquema no modo estrito