Programmatic tool calling: deixe o agente escrever o código em vez de chamar tool a tool
Você monta um agente, pluga uns MCPs, e na primeira tarefa séria ele precisa: buscar 20 funcionários, puxar os gastos de cada um, comparar com o limite e te dizer quem estourou. Doze, quinze, vinte chamadas de tool. Cada uma é um turno do modelo. Cada turno volta com o resultado inteiro entrando no contexto. No fim, o agente leu centenas de kilobytes de dado bruto só pra te responder uma linha: "três pessoas passaram do orçamento".
Caro. Lento. E o contexto entope no meio do caminho.
A ideia que está mudando isso tem nome: programmatic tool calling. Em vez de chamar uma tool de cada vez, o agente escreve um pequeno programa que orquestra todas as chamadas, processa o resultado ali mesmo e devolve pro modelo só a resposta final. Neste post você vai entender o padrão, ver o código de como isso funciona na prática (com a API da Anthropic e com o Code Mode da Cloudflare), e saber quando vale a pena trocar a arquitetura do seu agente.
TL;DR
- O que é: padrão onde o LLM escreve código que chama as tools (em loop, em paralelo, com filtro), em vez de chamar tool a tool via round-trip do modelo.
- Stack/Modelos: Claude (API com
code_execution), Cloudflare Code Mode, ou implementação própria com sandbox. Conceito vale pra qualquer modelo bom de código. - Custo/Acesso: disponível na Claude API, Claude Platform na AWS e Microsoft Foundry. Cobrança junto com o code execution.
- Por que importa: Cloudflare reporta queda de 1,17 milhão de tokens para ~1.000 no acesso a 2.500 endpoints — 99,9% menos contexto.
O contexto: por que chamar tool a tool é caro
O fluxo clássico de tool use é uma conversa de ida e volta. O modelo decide chamar uma tool, você executa, devolve o resultado, o modelo lê, decide a próxima. Funciona lindamente pra uma chamada. Começa a doer em três. Vira tragédia em doze.
Dois custos se acumulam.
O primeiro é a definição das tools. Todo schema de toda tool entra no contexto logo de cara, antes mesmo de o agente fazer qualquer coisa. Um servidor MCP gordo pode despejar dezenas de milhares de tokens só de descrição de função. A Anthropic mostrou um caso onde carregar tudo de uma vez custava 150.000 tokens — e que, deixando o agente descobrir só as ferramentas que precisa, caiu pra 2.000 tokens, uma economia de 98,7%.
O segundo é o resultado intermediário. Cada retorno de tool trafega pelo contexto, e em workflows encadeados ele trafega mais de uma vez — o output de uma chamada vira o input da próxima, e o modelo relê tudo no meio. No exemplo da Anthropic, transcrever uma reunião de vendas de 2 horas e jogar entre Google Drive e Salesforce significava processar 50.000 tokens a mais, só de dado passando de um lado pro outro.
Some os dois e você tem a conta real: chamar 10 tools direto consome cerca de 10x mais tokens do que chamar as mesmas 10 dentro de um código que devolve só o resumo.
Isso não é hype. É a física do round-trip.
A virada: o agente escreve o código
A sacada do programmatic tool calling — ou "code mode", como ficou conhecido — é simples de enunciar e poderosa na prática: dê ao modelo uma ferramenta de "executar código" e exponha as tools como funções que esse código pode chamar. O modelo então escreve um script que faz o loop, o filtro, o paralelo, a condicional — tudo o que código sabe fazer — e só o resultado final volta pro contexto.
Por que isso funciona tão bem? Porque o modelo é muito melhor escrevendo código do que enfileirando tool calls. Como a Cloudflare colocou: os LLMs viram milhões de linhas de TypeScript real, e só exemplos artificiais e contrived de tool-calling. Você está pedindo pro modelo fazer o que ele mais treinou.
E não é achismo de marketing. O paper CodeAct (ICML 2024) mostrou que consolidar as ações do agente num espaço único de código executável dá até 20% mais taxa de sucesso que as alternativas baseadas em JSON. Nos benchmarks de busca agêntica como BrowseComp e DeepSearchQA, a Anthropic relata que adicionar programmatic tool calling foi o fator que destravou a performance dos agentes.
Pré-requisitos
Pra acompanhar a parte de código:
- [ ] Chave da Claude API (o
code_executionprecisa estar habilitado na conta). - [ ] Modelo compatível: Opus 4.8/4.7/4.6/4.5 ou Sonnet 4.6/4.5, via
code_execution_20260120. - [ ] Noção de tool use tradicional — se isso aqui é novidade, vale ler a doc de tool use antes.
- [ ] Pra testar o Code Mode: conta Cloudflare com Workers.
Disponível na Claude API, na Claude Platform na AWS e na Microsoft Foundry. Ainda não no Bedrock nem no Vertex.
Mão na massa: programmatic tool calling na Claude API
Passo 1: marque a tool como chamável por código
A diferença de uma definição normal é um campo só: allowed_callers. Você adiciona o code_execution na lista de tools e marca a sua tool de negócio como invocável de dentro do código.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=4096,
messages=[{
"role": "user",
"content": "Consulte o faturamento das regiões Oeste, Leste e Centro e me diga qual teve a maior receita",
}],
tools=[
{"type": "code_execution_20260120", "name": "code_execution"},
{
"name": "query_database",
"description": "Executa uma query SQL no banco de vendas. Retorna uma lista de linhas como objetos JSON.",
"input_schema": {
"type": "object",
"properties": {
"sql": {"type": "string", "description": "Query SQL a executar"}
},
"required": ["sql"],
},
"allowed_callers": ["code_execution_20260120"],
},
],
)
O allowed_callers aceita três valores: ["direct"] (só o modelo chama, é o default), ["code_execution_20260120"] (só de dentro do código) ou os dois. A doc recomenda escolher um — misturar confunde o modelo na hora de decidir o caminho.
Passo 2: o modelo escreve o código
Aqui mora a mágica. Em vez de te devolver três tool_use em sequência, o Claude escreve um único bloco de código que chama a tool em loop e processa o resultado:
# código que o PRÓPRIO Claude gera e roda no sandbox
regions = ["Oeste", "Leste", "Centro", "Norte", "Sul"]
results = {}
for region in regions:
data = await query_database(f"<sql para {region}>")
results[region] = sum(row["revenue"] for row in data)
top_region = max(results.items(), key=lambda x: x[1])
print(f"Região líder: {top_region[0]} com R${top_region[1]:,} de receita")
Repare no await. As suas tools viram funções assíncronas automaticamente, o que deixa o modelo paralelizar chamadas quando elas não dependem uma da outra. Cinco regiões, um turno do modelo — não cinco.
Passo 3: os dados ficam fora do contexto
Esse é o ponto que muda a conta de custo. Quando o código chama query_database, o code execution pausa, a API te devolve um tool_use, você responde com o resultado, e o código continua de onde parou. O resultado bruto da query — que pode ser 847 linhas de dado — fica no sandbox. Nunca entra no contexto do modelo.
O que volta pro Claude é só o stdout:
Região líder: Oeste com R$45,000 de receita
Uma linha. Não as 847. Os resultados intermediários não contam no seu uso de tokens de entrada e saída — só o output final do código e a resposta do Claude. É essa a diferença entre "três pessoas estouraram o orçamento" custar uma linha ou custar a planilha inteira.
Passo 4: filtre, decida, termine cedo
Como é código de verdade, o modelo ganha o que tool-calling puro nunca teve: controle de fluxo. Pode filtrar antes de devolver:
logs = await fetch_logs(server_id)
errors = [log for log in logs if "ERROR" in log]
print(f"{len(errors)} erros encontrados")
for error in errors[-10:]: # só os 10 últimos voltam pro contexto
print(error)
Pode parar assim que achar o que procura, sem varrer o resto:
for endpoint in endpoints:
status = await check_health(endpoint)
if status == "healthy":
print(f"Endpoint saudável: {endpoint}")
break # para aqui, não checa os outros
Isso é o que o tool-calling tradicional não consegue fazer sem queimar um turno de modelo por iteração.
O mesmo padrão no Code Mode da Cloudflare
A Cloudflare empacotou a mesma ideia de um jeito diferente. O @cloudflare/codemode converte suas tools em uma API TypeScript tipada, dá ao modelo uma única tool de "escrever código", e roda o código gerado num sandbox isolado de Worker. As chamadas de tool são despachadas de volta pro host via Workers RPC, e só o resultado final volta.
O número que eles publicaram é o argumento mais forte do padrão inteiro: o custo de contexto pra interagir com mais de 2.500 endpoints de API caiu de 1,17 milhão de tokens pra cerca de 1.000 — uma redução de ~99,9%. E esse custo é fixo: não importa se a API tem 10 ou 10.000 endpoints, o footprint não explode.
A parte de segurança também é levada a sério: o código roda com fetch() e connect() externos bloqueados por padrão (globalOutbound: null), então o código gerado só conversa com o mundo através das tools que você expôs. Sandbox de verdade, não confiança cega.
Tutorial te mostra o caminho — no Clã você constrói junto. Aula ao vivo toda semana, projetos reais de Engenharia de IA, ao lado de quem já está em produção.
Entrar no ClãLimitações e onde você vai se queimar
Esse padrão não é bala de prata. Onde ele cobra o preço:
- Tool call simples não compensa. Pra uma chamada única e rápida, o overhead de subir o code execution é maior que o ganho. A própria doc lista isso como caso ruim de uso — programmatic tool calling brilha em workflows com 3+ chamadas dependentes, processamento de dado grande ou operações em lote.
- Você está executando código gerado por LLM. Se rodar fora de um sandbox, isso é um vetor de injeção. Resultado de tool volta como string e pode conter comando executável. Rode em container isolado, sem egress de rede, e valide o que entra. A opção gerenciada da Anthropic e o Worker da Cloudflare já fazem isso por você — implementação caseira é por sua conta e risco.
- Tem incompatibilidades. Na Claude API, programmatic tool calling não roda com structured outputs (
strict: true), não dá pra forçar viatool_choice, e tools vindas de um MCP connector não podem ser chamadas por código. - Container expira. Ele vive enquanto a tarefa roda, mas tem timeout de inatividade (~4,5 min). Se a sua tool demora pra responder, o código pode tomar
TimeoutError. Monitore oexpires_at.
FAQ rápido
Isso substitui o MCP? Não. É uma forma melhor de consumir MCP. As tools continuam sendo MCP; muda que o agente as orquestra por código em vez de uma a uma. A própria Cloudflare chama o Code Mode de "a melhor forma de usar MCP".
Funciona com qualquer modelo? O conceito sim — qualquer modelo decente de código se beneficia. Mas o suporte nativo na Claude API exige code_execution_20260120 (Opus 4.5+ ou Sonnet 4.5+). Fora isso, você implementa o sandbox por conta própria.
É só economia de token ou tem ganho de qualidade? Os dois. Menos token e menos latência, sim — mas os benchmarks (BrowseComp, DeepSearchQA, CodeAct) mostram também mais acerto, porque o modelo raciocina sobre composição de ferramentas no terreno em que ele é mais forte: código.
Preciso reescrever meu agente do zero? Não. Na Claude API é adicionar allowed_callers nas tools certas e ligar o code_execution. O resto do seu fluxo continua igual.
Function calling clássico vs. programmatic tool calling: lado a lado
Se você ainda está decidindo se vale migrar, ajuda ver as duas abordagens na mesma mesa. A tabela abaixo resume onde cada uma ganha e onde cada uma cobra o preço — é a mesma comparação que abriu a nossa primeira leitura do padrão, no dia do anúncio.
| Dimensão | Function calling clássico | Programmatic tool calling |
|---|---|---|
| Quem orquestra | O modelo, uma chamada por turno | Código no sandbox |
| Round-trips pra 20 lookups | 20 inferências | 1 inferência + N tool_result |
| Resultado bruto no contexto | Entra inteiro, toda vez | Fica no container; só o stdout volta |
| Filtrar, paralelizar, parar cedo | Simulado turno a turno | Nativo (for, if, asyncio.gather) |
| Estado entre chamadas | Histórico de mensagens | REPL persistente no container |
| Debug | JSON de cada turno | Código gerado + stdout |
| Onde não cabe | Pipeline com muito dado | Uma chamada única e rápida |
A leitura prática: quanto mais composição a tarefa exige (encadear, agregar, cruzar), mais o lado direito da tabela vence. Pra um get_user_by_id isolado, o lado esquerdo continua sendo o caminho certo.
Os números que fazem o modelo turno a turno virar legado
O custo do fluxo clássico não é teórico — a Anthropic publicou os números no anúncio do padrão. Um agente com cinco MCPs plugados já carrega cerca de 55 mil tokens de definição de tool antes de ler a primeira mensagem do usuário, e setups internos chegavam a 134 mil tokens só de schema de ferramenta.
Na ponta da execução, o efeito é o mesmo: em tarefas de pesquisa multistep, o consumo médio caiu de 43.588 pra 27.297 tokens, uma queda de 37%, só de trocar o loop turno a turno por código orquestrando as chamadas. Combinado com o tool search tool (que reduz as definições carregadas de 77K pra 8,7K tokens, 85% a menos), você ataca os dois lados do gargalo: o que entra antes da conversa e o que trafega durante ela.
Esses números são os que sustentam a tese de que function calling clássico vira o caso especial, não o padrão. Não porque esteja errado, mas porque força o modelo a decidir uma frase por vez quando ele poderia escrever o script inteiro.
O que o código gerado revela (e o campo caller.type)
Vale olhar com calma o que o modelo escreve sem você pedir. No caso clássico de "quais engenheiros estouraram o orçamento de viagem no Q3", com duas tools (get_team_members e get_expenses), o script gerado traz quatro padrões que aparecem de graça:
- Paralelização com
asyncio.gather: vinte funcionários viram vinte chamadas concorrentes, não vinte round-trips. - Filtragem local: a lista bruta de despesas nunca sobe pro modelo, só quem passou do limite.
- Agregação em Python puro: a soma por categoria custa zero token.
- Output controlado: o
printfinal é o único dado que volta pro contexto na próxima inferência.
Do lado da API, cada vez que esse código invoca uma tool, você recebe um tool_use com um campo a mais: caller, com type: "code_execution_20260120" e o tool_id do bloco de código. É assim que você distingue uma chamada vinda do script de uma chamada direta do modelo — e é esse o gancho pra logar, auditar ou tratar as duas de forma diferente no seu harness. O exemplo completo, com o script gerado e o JSON do caller, está no post original.
E se eu já uso LangChain ou similar?
Provedores intermediários já começaram a expor o padrão — o LiteLLM, por exemplo, suporta programmatic tool calling na integração com a Anthropic. Se o seu stack passa por um proxy assim, vale checar se a flag allowed_callers já chega intacta na outra ponta.
E se não chegar, a essência do padrão é genérica: um modelo que escreve código que chama funções. Dá pra implementar com sandbox próprio (o Code Mode da Cloudflare é uma prova disso), sem depender do container gerenciado da Anthropic — desde que você leve a sério a parte de isolamento que discutimos nas limitações.
Conclusão
O ponto não é "código é mais legal que JSON". O ponto é arquitetural: quando o agente orquestra tools por código, os dados pesados ficam onde devem ficar — fora do contexto — e o modelo só lê a conclusão. É a diferença entre um agente que aguenta uma tarefa real de produção e um que entope o contexto na terceira chamada.
E é exatamente esse tipo de decisão — onde o dado mora, quando o modelo precisa ler, como você desenha o agente pra não estourar custo e latência — que separa o "fiz um agente no fim de semana" do "coloquei um agente em produção". É a mesma régua que discutimos em Anatomia de um Agent Harness: a engenharia em volta do modelo é o que aguenta produção. E é sobre isso o Workshop Arquitetando Soluções de IA: arquitetar software com agentes de IA na prática, com as decisões de engenharia na mesa, não slide motivacional.
O próximo passo é olhar suas tools com outro olho. Toda vez que você vir um agente fazendo a mesma chamada em loop, ou puxando um dataset enorme só pra responder uma linha, é candidato a virar código. Comece por aí.
{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.
Conteúdo é o que não falta. Falta quem desembaralhe: o que importa agora é como implementar do jeito certo. No Clã você tem isso ao vivo, toda semana, com quem já filtrou o ruído.
Entrar no Clã