Tool design
Ferramentas transformam um assistente em um sistema capaz de consultar e alterar o ambiente. Esse poder exige contratos que expressem finalidade, argumentos, resultados e efeitos. Nesta semana você projetará ferramentas que sejam compreensíveis para o modelo e controláveis pela aplicação.
O caso é atualizar endereço de entrega. Buscar um pedido e alterar seu endereço parecem operações próximas, mas possuem riscos diferentes. Vamos desenhar uma consulta informativa e uma escrita autorizada, com idempotência, erros e confirmação vinculada ao resultado desejado.
JavaScriptAo terminar esta aula
- Ferramentas são fronteiras de capacidade.
- Descrições orientam; implementação impõe contratos.
- Idempotência e controle de versão se complementam.
Antes de continuar: Laboratório: Harness básico
Function calling e schemas de entrada e saída
FundamentosO modelo recebe descrições e contratos de ferramentas e pode propor uma chamada. A aplicação valida argumentos, autorização e estado antes de executar; depois devolve uma observação para continuar o fluxo. Esse ciclo é uma fronteira entre decisão probabilística e execução de software. Uma ferramenta bem definida reduz ambiguidades, mas não garante que o modelo a escolherá corretamente. Avalie a escolha e os argumentos com tarefas representativas.
↗ Function calling↗ Structured model outputs↗ OpenAPI Specification 3.1.1
O input schema informa tipos, campos obrigatórios e restrições suportadas. O output schema deve comunicar sucesso, erro e estado de maneira consumível. Para consulta de pedido, retorne identificador, situação e campos necessários, evitando despejar toda a conta. Para alteração de endereço, não aceite userId livre como autoridade: use o contexto autenticado. Valide o schema no servidor e verifique regra de negócio, como proibir alteração depois de despacho. Como visto em structured outputs, estrutura correta não implica permissão ou elegibilidade.
↗ Function calling↗ Structured model outputs↗ OpenAPI Specification 3.1.1
Granularidade e descrições úteis
FundamentosGranularidade define quanto trabalho uma ferramenta realiza. Uma ferramenta muito pequena exige várias chamadas e contexto intermediário; uma ferramenta ampla pode esconder efeitos e dificultar aprovação. Uma consulta buscarPedidoParaEntrega pode reunir dados relevantes sem permitir escrita. Já resolverPedido que busca, modifica e envia mensagens mistura responsabilidades difíceis de controlar. Escolha fronteiras orientadas ao objetivo e ao risco, não uma cópia mecânica de todos os endpoints internos.
Uma descrição útil informa quando usar, quando não usar, quais argumentos são necessários, efeitos e erros. Diferencie buscarEnderecoAtual de atualizarEndereco. Se há duas consultas parecidas, explique a distinção em linguagem da tarefa. O retorno deve incluir informação suficiente para o próximo passo, mas não conteúdo irrelevante que polui contexto. A eficiência é avaliada pelo resultado correto, número de chamadas, consumo e facilidade de diagnóstico. Não assuma que menos ferramentas ou menos chamadas sempre é melhor: uma escrita separada pode justificar uma etapa adicional de confirmação.
Side effects e idempotência
FundamentosSide effect é uma mudança observável além de produzir um valor de retorno: atualizar endereço, enviar mensagem ou debitar saldo. A aplicação precisa saber quais ferramentas causam esses efeitos para aplicar políticas e recuperação adequadas. A consulta pode ser repetida com pouca consequência operacional; a atualização pode exigir uma intenção estável e controle de versão para não sobrescrever mudança recente.
↗ Making retries safe with idempotent APIs↗ Function calling
Idempotência preserva o efeito lógico em repetições da mesma intenção. Associe chave ao pedido, usuário e conteúdo normalizado, conforme o contrato do serviço. Uma chave reutilizada com endereço diferente deve ser rejeitada ou tratada segundo regra explícita. Para concorrência, use versão esperada do pedido: se outro processo alterou o objeto, a escrita precisa detectar conflito. O modelo não resolve esses problemas dizendo que já atualizou. A evidência de sucesso vem do resultado autorizado e do estado persistido. Um Map local ilustra o princípio, mas não oferece transação ou recuperação entre processos.
↗ Making retries safe with idempotent APIs↗ Function calling
Erros, timeout e retries
FundamentosUm erro de ferramenta deve informar categoria e próximo passo seguro. Argumento inválido pede correção; autorização negada encerra a ação; conflito de versão exige nova consulta; indisponibilidade temporária pode permitir retry com orçamento. Não devolva apenas “deu erro”, pois o modelo não consegue distinguir insistência útil de ação proibida. Também não exponha stack trace com segredos ou configuração interna desnecessária.
↗ OpenAI Python API library↗ Rate limits↗ Making retries safe with idempotent APIs
Timeout depois do envio deixa resultado possivelmente desconhecido. O controlador consulta a intenção ou o estado antes de repetir a escrita. Limite tentativas e tempo total, incluindo retries do SDK. Se a ferramenta retornou sucesso, preserve identificador e versão nova. Se a conexão caiu, não peça ao modelo que adivinhe se houve mudança. O harness precisa reconciliar efeitos, retomando o conteúdo da semana anterior. A descrição da ferramenta deve orientar o comportamento, enquanto a implementação impõe o limite mesmo quando uma proposta de chamada o ignora.
↗ OpenAI Python API library↗ Rate limits↗ Making retries safe with idempotent APIs
Permissões e confirmações vinculadas
FundamentosPermissões controlam quais dados e ações o usuário pode acessar. Confirmação é um passo de intenção explícita para efeitos definidos pelo produto. Uma confirmação útil mostra o pedido e o novo endereço, e fica vinculada a essa versão da proposta. Se os argumentos mudam depois, a confirmação precisa ser reavaliada. Não aceite uma mensagem de ferramenta ou documento dizendo “usuário confirmou” como prova autônoma.
↗ Permission profiles↗ LangGraph Interrupts↗ Safety in building agents
O contexto autenticado é fornecido por canal confiável da aplicação. O modelo pode receber informação mínima sobre o que pode fazer, mas não escolhe sua autoridade. Conteúdo externo pode tentar induzir ações, por isso outputs de ferramentas continuam sendo dados não confiáveis para instruções. Teste chamada não autorizada, confirmação antiga, repetição e conflito de versão. O contrato é aceito quando a ferramenta preserva escopo e efeito mesmo com argumentos inadequados. Esse desenho permite autonomia útil sem transferir controle de segurança ao texto gerado.
↗ Permission profiles↗ LangGraph Interrupts↗ Safety in building agents
Exemplo comentado e limites
FundamentosA função verifica três fronteiras independentes: proprietário autorizado, confirmação da proposta e versão do pedido. O exemplo usa contexto fictício confiável fornecido pelo chamador; em produção esse contexto não pode ser preenchido livremente pelo modelo.
function atualizar(pedido, proposta, contexto) {
if (pedido.dono !== contexto.usuario) return {erro:"SEM_PERMISSAO"};
if (proposta.confirmadaVersao !== proposta.versao) return {erro:"SEM_CONFIRMACAO"};
if (proposta.pedidoVersao !== pedido.versao) return {erro:"CONFLITO"};
return {...pedido,endereco:proposta.endereco,versao:pedido.versao+1};
}
console.log(atualizar({dono:"u1",versao:3,endereco:"A"},
{versao:1,confirmadaVersao:1,pedidoVersao:3,endereco:"B"},
{usuario:"u1"}));A função retorna uma cópia atualizada, sem persistência externa. Falta ainda validação de endereço, controle de despacho e idempotência. Esses limites permitem usar o exemplo como teste de gates sem apresentá-lo como integração completa de logística.
Exercício aplicado
Projete consulta e atualização de endereço com confirmação da proposta e detecção de versão antiga.
- Tente definir fronteiras e efeitos.
- Execute sucesso e erros independentes.
- Implemente intenção estável e conflito.
- Avalie descrições e resultados com rubrica.
Abrir resolução comentada
O catálogo contém oito ferramentas locais. prepararEndereco consulta o pedido e devolve proposta sem alterar o endereço. atualizarEndereco valida o schema mínimo, usa contexto autorizado, confere confirmação e versão esperada, registra intenção com payload normalizado e grava resultado com recibo. Os asserts verificam que consulta e preparação não causam escrita, enquanto o replay da mesma intenção devolve o recibo sem incrementar efeitos.
O ledger vincula intenção ao usuário e pedido e rejeita payload incompatível. Uma intenção nova usando a versão antiga falha com CONFLITO. A perda de resposta é provocada após a atualização do endereço; recuperarIntencao consulta o resultado já gravado. O replay seguinte continua sem repetir a escrita. criarNota também possui deduplicação local, evitando que o exemplo esconda efeitos de uma ferramenta aparentemente simples.
Os mocks de buscarCliente, listarAtendimentos e arquivarAtendimento são fixtures explícitas do catálogo de prática; a versão de produção deve implementar consultas e persistência reais com os contratos descritos no laboratório. Os testes executam os controles de intenção, conflito, confirmação e recuperação. A estrutura Map continua limitada a um processo síncrono: concorrência e reinício exigem transação ou operação condicional e armazenamento durável. Nenhuma chamada deste exemplo atualiza um CRM externo.
import assert from "node:assert/strict";
const pedidos=new Map([["p1",{id:"p1",cliente:"u1",versao:3,endereco:"A",despachado:false}]]);
const intencoes=new Map();const notas=new Map();const traces=[];
let efeitos=0;
function falhar(codigo){throw new Error(codigo);}
function pedidoAutorizado(id,ctx){
const pedido=pedidos.get(id);
if(!pedido||pedido.cliente!==ctx.usuario) falhar("SEM_PERMISSAO");
return pedido;
}
function atualizarEndereco(args,ctx){
const pedido=pedidoAutorizado(args.pedidoId,ctx);
if(typeof args.endereco!=="string"||!args.endereco.trim()) falhar("SCHEMA_INVALIDO");
const normalizado=JSON.stringify({pedidoId:args.pedidoId,endereco:args.endereco.trim(),
pedidoVersao:args.pedidoVersao,propostaVersao:args.propostaVersao});
const chave=ctx.usuario+":"+args.intencaoId;
const anterior=intencoes.get(chave);
if(anterior){
if(anterior.payload!==normalizado) falhar("PAYLOAD_INCOMPATIVEL");
return anterior.resultado;
}
if(args.confirmadaVersao!==args.propostaVersao) falhar("SEM_CONFIRMACAO");
if(args.pedidoVersao!==pedido.versao) falhar("CONFLITO");
if(pedido.despachado) falhar("PEDIDO_DESPACHADO");
const registro={usuario:ctx.usuario,pedidoId:pedido.id,payload:normalizado};
intencoes.set(chave,registro);
const atualizado={...pedido,endereco:args.endereco.trim(),versao:pedido.versao+1};
pedidos.set(pedido.id,atualizado);efeitos++;
registro.resultado={status:"atualizado",pedidoId:pedido.id,versao:atualizado.versao,recibo:"crm-"+efeitos};
traces.push({etapa:"escrita",intencao:args.intencaoId,status:"confirmado"});
if(args.perderResposta) falhar("RESPOSTA_PERDIDA");
return registro.resultado;
}
function recuperarIntencao(intencaoId,ctx){
const registro=intencoes.get(ctx.usuario+":"+intencaoId);
if(!registro) return {status:"nao_enviado"};
pedidoAutorizado(registro.pedidoId,ctx);
traces.push({etapa:"reconciliacao",intencao:intencaoId,status:"consultado"});
return registro.resultado;
}
// Catálogo de oito ferramentas locais com efeitos explícitos.
const ferramentas={
buscarCliente:(_,ctx)=>({id:ctx.usuario}),
consultarPedido:(a,ctx)=>({...pedidoAutorizado(a.pedidoId,ctx)}),
listarAtendimentos:(_,ctx)=>[{id:"at1",cliente:ctx.usuario,status:"aberto"}],
consultarElegibilidade:(a,ctx)=>({podeAlterar:!pedidoAutorizado(a.pedidoId,ctx).despachado}),
prepararEndereco:(a,ctx)=>({pedidoId:pedidoAutorizado(a.pedidoId,ctx).id,
endereco:a.endereco,propostaVersao:1,pedidoVersao:pedidos.get(a.pedidoId).versao}),
atualizarEndereco,
criarNota:(a,ctx)=>{
if(typeof a.texto!=="string"||!a.texto.trim()) falhar("SCHEMA_INVALIDO");
const chave=ctx.usuario+":"+a.intencaoId;
const existente=notas.get(chave);
if(existente&&existente.texto!==a.texto) falhar("PAYLOAD_INCOMPATIVEL");
if(existente) return existente;
const nota={id:"nota-"+(notas.size+1),cliente:ctx.usuario,texto:a.texto};
notas.set(chave,nota);return nota;
},
arquivarAtendimento:(a,ctx)=>{
if(a.atendimentoId!=="at1"||a.cliente!==ctx.usuario) falhar("SEM_PERMISSAO");
if(!a.confirmado) falhar("SEM_CONFIRMACAO");
return {atendimentoId:a.atendimentoId,status:"arquivado"};
}
};
function chamar(nome,args,ctx){
if(!Object.hasOwn(ferramentas,nome)) falhar("TOOL_NAO_PERMITIDA");
return ferramentas[nome](args,ctx);
}
const ctx={usuario:"u1"};
const proposta=chamar("prepararEndereco",{pedidoId:"p1",endereco:"B"},ctx);
assert.equal(pedidos.get("p1").endereco,"A");
const args={...proposta,intencaoId:"i1",confirmadaVersao:1};
const recibo=chamar("atualizarEndereco",args,ctx);
assert.equal(pedidos.get("p1").endereco,"B");
assert.deepEqual(chamar("atualizarEndereco",args,ctx),recibo);assert.equal(efeitos,1);
assert.throws(()=>chamar("atualizarEndereco",{...args,endereco:"C"},ctx),/PAYLOAD_INCOMPATIVEL/);
assert.throws(()=>chamar("atualizarEndereco",{...args,intencaoId:"i2"},ctx),/CONFLITO/);
assert.throws(()=>chamar("atualizarEndereco",args,{usuario:"u2"}),/SEM_PERMISSAO/);
const proposta2=chamar("prepararEndereco",{pedidoId:"p1",endereco:"C"},ctx);
const args2={...proposta2,intencaoId:"i2",confirmadaVersao:1,perderResposta:true};
assert.throws(()=>chamar("atualizarEndereco",args2,ctx),/RESPOSTA_PERDIDA/);
const recuperado=recuperarIntencao("i2",ctx);
assert.equal(recuperado.versao,5);assert.equal(efeitos,2);
assert.deepEqual(chamar("atualizarEndereco",args2,ctx),recuperado);assert.equal(efeitos,2);
assert.throws(()=>chamar("atualizarEndereco",{...args2,intencaoId:"i3",
pedidoVersao:5,confirmadaVersao:0},ctx),/SEM_CONFIRMACAO/);
const nota=chamar("criarNota",{intencaoId:"n1",texto:"Endereço confirmado"},ctx);
assert.deepEqual(chamar("criarNota",{intencaoId:"n1",texto:"Endereço confirmado"},ctx),nota);
assert.equal(notas.size,1);assert.equal(Object.keys(ferramentas).length,8);
assert.throws(()=>chamar("terminal",{},ctx),/TOOL_NAO_PERMITIDA/);
console.log({status:"aprovado",efeitos,intencoes:intencoes.size,notas:notas.size,traces});Como conferir seu resultado
- Consulta não altera pedido.
- Confirmação e versão são verificadas.
- Retry de escrita respeita intenção e estado.
Aplique em um problema novo
Primeiro resolva sem consultar a resposta. Explique suas decisões e guarde a evidência. A conclusão de leitura é independente desta autoavaliação.
Confira seus pré-requisitos
- Definir ferramenta com efeito explícito.
- Aplicar versão esperada/intenção.
Duas propostas usam endereço v4; primeira escreve e perde resposta. Decida segunda e replay.
Conferir raciocínio e critérios de domínio
Segunda intenção em v4 conflita com estado atualizado.
Replay da primeira recupera recibo sem nova versão.
Contexto autorizado e confirmação vinculada protegem escrita; consulta/preparação não escrevem.
Evidências para autoavaliação ou revisão por pares
- Conflito: Segunda intenção em v4 conflita com estado atualizado.
- Replay: Replay da primeira recupera recibo sem nova versão.
- Autoridade: Contexto autorizado e confirmação vinculada protegem escrita; consulta/preparação não escrevem.
Um erro frequente
Retry corrige conflito de versão.
Conflito requer nova consulta e decisão, não retries cegos.
Teste sua compreensão
Responda com suas palavras antes de abrir o comentário. Saber explicar uma decisão é parte do domínio.
1. Quem executa a chamada proposta?
2. Confirmação genérica autoriza argumentos modificados?
3. Timeout resolve o estado do efeito?
Não.
Pode ser necessário reconciliar resultado desconhecido.
Seu progresso fica salvo neste navegador. Concluir a leitura não substitui demonstrar o domínio nos exercícios.
Referências e aprofundamento
Documentação oficial e trabalhos originais. As referências registram o escopo e as limitações para você conferir o que sustentam.
- Function calling
OpenAI • consulta: 2026-10-06
OpenAICiclo de chamada de funções, schemas, descrições, argumentos, retorno e execução pela aplicação.
Limites: O modelo propõe chamadas; validação, autorização e execução ficam na aplicação. Confirmar efeitos externos fora do prompt.
- Structured model outputs
OpenAI • consulta: 2026-10-06
OpenAIJSON Schema, saída estruturada, strict mode e distinção entre resposta estruturada e function calling.
Limites: Suporte a subconjunto JSON Schema e modelos específicos; JSON válido não garante verdade factual ou regra de negócio.
- OpenAPI Specification 3.1.1
OpenAPI Initiative • consulta: 2026-10-06
FundamentosContrato HTTP, schemas, operações, parâmetros e respostas.
Limites: Versão fixada 3.1.1; suporte das ferramentas a JSON Schema/OpenAPI deve ser verificado.
- Writing effective tools for AI agents
Anthropic • consulta: 2026-10-06
AgentesIA & MLGranularidade, nomes, descrições e retornos úteis e eficientes em tokens.
Limites: Relato de engenharia do fornecedor; avaliar tools em tarefas reais antes de generalizar.
- Making retries safe with idempotent APIs
Amazon Web Services • consulta: 2026-10-06
APIsIdempotência, identificador de requisição, retries seguros e efeitos colaterais.
Limites: Padrão de sistemas distribuídos; suporte a chave de idempotência deve ser verificado em cada endpoint, não presumido para todo LLM.
- LangGraph Interrupts
LangChain • consulta: 2026-10-06
LangGraphAgentesPausa, human-in-the-loop, aprovação e retomada de execução.
Limites: Aprovação precisa validar ação/argumentos antes de execução; não é autorização abstrata de toda sessão.
- OpenAI Python API library
OpenAI • consulta: 2026-10-06
PythonOpenAIAPIsCliente SDK, streaming, erros tipados, retries, timeout, logging e request IDs.
Limites: Defaults do SDK não substituem orçamento global de retries; pin de versão obrigatório.
- Rate limits
OpenAI • consulta: 2026-10-06
OpenAILimites por taxa, backoff e recuperação de erros temporários.
Limites: Limites por conta/modelo mudam; evitar retries ilimitados e tempestades de retry.
- Permission profiles
OpenAI • consulta: 2026-10-06
OpenAIPerfis de permissões, acesso a arquivos, rede e execução de comandos.
Limites: Suporte e isolamento variam por SO/cliente; hierarquia de instruções não substitui barreira do sistema operacional.
- Safety in building agents
OpenAI • consulta: 2026-10-06
OpenAIAgentesPrompt injection, vazamento de dados, entradas não confiáveis e aprovação de tools.
Limites: Prompt de segurança não substitui permissões da aplicação; guia pode mostrar ferramentas legadas, aplicar os princípios em stack atual.