Um problema, três dialetos
OpenAI, Anthropic e Google expressam a mesma intenção: “quero executar esta função com estes argumentos”. O envelope muda. A OpenAI retorna um item function_call, a Anthropic um bloco tool_use e o Google uma parte functionCall.
Um harness não deveria espalhar esses três formatos por todo o produto. Traduza-os na fronteira do provedor para um comando comum do executor. Mas normalizar não significa apagar: o identificador nativo da chamada precisa sobreviver para que o resultado volte à solicitação correta.
function_call
Os argumentos chegam como string JSON. call_id conecta a saída da função.
tool_use
O input já é um objeto. O resultado referencia tool_use_id.
functionCall
Os argumentos normalmente são um objeto e id é opcional, mas precisa ser devolvido quando existe.
O JSON retornado por cada API
Estes exemplos reduzem capturas reais aos campos envolvidos na chamada. Os identificadores opacos foram substituídos por nomes legíveis. OpenAI e Google foram capturados com uma solicitação mínima forçada; o exemplo da Anthropic vem de uma fixture gravada anteriormente e foi conferido com a documentação oficial porque a credencial de captura havia expirado.
{
"type": "function_call",
"id": "fc_openai_1",
"call_id": "call_openai_1",
"name": "lookup_demo_value",
"arguments": "{\"key\":\"height\"}",
"status": "completed"
}{
"type": "tool_use",
"id": "toolu_anthropic_1",
"name": "lookup_demo_value",
"input": {
"key": "height"
}
}{
"functionCall": {
"name": "lookup_demo_value",
"args": {
"key": "height"
},
"id": "call_google_1"
}
}| Conceito | OpenAI | Anthropic | |
|---|---|---|---|
| Nome | name | name | functionCall.name |
| Argumentos | arguments, string JSON | input, objeto | args, objeto ou string tolerada |
| Correlação | call_id | id → tool_use_id | id opcional na chamada e resposta |
| Resultado | function_call_output | tool_result | functionResponse |
Uma forma comum sem perder contexto
O código que executa uma função precisa apenas de name e args. O código que monta o próximo turno também precisa do provedor e de seu identificador nativo. Separar esses conceitos impede que um detalhe da OpenAI chegue ao handler e também evita responder apenas “por nome” quando duas chamadas usam a mesma função.
{
"execution": {
"name": "lookup_demo_value",
"args": {
"key": "height"
}
},
"correlation": {
"provider": "openai | anthropic | google",
"callId": "identificador nativo da chamada"
}
}normalize os dados consumidos pelo seu código; preserve os metadados consumidos pelo protocolo.
Parseie o stream, não os pacotes
Em streaming, um fragmento de rede não é igual a um evento SSE nem a um JSON completo. A rede pode cortar no meio de "arguments", juntar cinco eventos no mesmo fragmento ou deixar o evento final sem a linha em branco de encerramento.
- Acumule texto em um buffer.
- Extraia apenas eventos SSE completos e preserve o restante.
- Interprete o tipo do evento e agregue deltas pelo índice ou identificador.
- Ao fechar o stream, processe o restante mais uma vez se ele formar um evento válido.
A OpenAI pode separar a criação do item e os deltas de argumentos; a Anthropic transmite input_json_delta; o Google costuma enviar uma parte de função completa. O parser deve esconder essa diferença e produzir o mesmo resultado para qualquer partição de bytes.
Várias chamadas e dados quebrados
Preserve todas as chamadas
Um turno pode solicitar várias tools. Mantenha a ordem do provedor e devolva um resultado por chamada.
O nome não basta
Duas chamadas a lookup_demo_value podem ter inputs diferentes. Correlacione por id, não apenas pelo nome.
Não invente uma chamada
Ignore um evento truncado ou JSON externo inválido. Converta um erro explícito do provedor em erro controlado.
Argumentos inválidos podem ser normalizados para um objeto vazio para manter o parser estável, mas isso não é validação. Antes de executar uma ação com efeitos, o harness ainda precisa verificar JSON Schema, permissões e regras de negócio.
O contrato de testes
Fixtures por provedor
Uma chamada, nenhuma chamada e várias chamadas com ids e argumentos concretos.
Continuação nativa
Verifique call_id, tool_use_id e o id opcional do Google.
Property-based
Gere partições arbitrárias do stream e exija o mesmo resultado da reprodução em um fragmento.
Adicione regressões para JSON truncado, erros explícitos e formatos inesperados de argumentos. Esses testes não provam que o modelo escolherá a tool certa; provam que o harness interpretará de forma determinística tudo o que receber.
Fontes oficiais
Aprender a construir um agente completo
Este artigo faz parte de uma série sobre como desenvolver um agente ou harness de ponta a ponta. Gymnasia é o exemplo prático, mas os parsers por provedor, a forma comum e os testes de fragmentação podem ser aplicados a outros produtos.