Construtor de Ferramentas para Agentes
Ferramentas são como agentes de IA interagem com o mundo. Uma ferramenta bem projetada é a diferença entre um agente que funciona e um que alucina, falha silenciosamente ou custa 10x mais tokens do que o necessário.
Esta skill aborda design de ferramentas, do esquema ao tratamento de erros. Práticas recomendadas de JSON Schema, escrita de descrições que realmente ajudam o LLM, validação e o emergente padrão MCP que está se tornando a lingua franca para ferramentas de IA.
Insight chave: Descrições de ferramentas são mais importantes que implementações de ferramentas. O LLM nunca vê seu código - ele vê apenas o esquema e a descrição.
Princípios
- Qualidade da descrição > qualidade da implementação para precisão do LLM
- Mire em menos de 20 ferramentas - mais causa confusão
- Toda ferramenta precisa de tratamento explícito de erros - falhas silenciosas envenenam agentes
- Retorne strings, não objetos - LLMs processam texto
- Validação antes da execução - rejeite, corrija ou escale, nunca falhe silenciosamente
- Teste ferramentas com o LLM, não apenas testes unitários
Capacidades
- agent-tools
- function-calling
- tool-schema-design
- mcp-tools
- tool-validation
- tool-error-handling
Escopo
- multi-agent-coordination → multi-agent-orchestration
- agent-memory → agent-memory-systems
- api-design → api-designer
- llm-prompting → prompt-engineering
Ferramentas
Padrões
- JSON Schema - Quando: Todas as definições de ferramenta Nota: O formato universal para esquemas de ferramenta
- MCP (Model Context Protocol) - Quando: Construindo ferramentas reutilizáveis e multiplataforma Nota: Padrão aberto da Anthropic, amplamente adotado
Frameworks
- Anthropic SDK - Quando: Agentes baseados em Claude Nota: O tool runner beta lida com a maioria da complexidade
- OpenAI Functions - Quando: Agentes baseados em OpenAI Nota: Use modo estrito para conformidade garantida com o esquema
- Vercel AI SDK - Quando: Manipulação de ferramentas de múltiplos provedores Nota: Abstrai diferenças entre provedores
- LangChain Tools - Quando: Agentes baseados em LangChain Nota: Converte ferramentas MCP para o formato LangChain
Padrões
Design de Esquema de Ferramenta
Criando JSON Schema claro e inequívoco para ferramentas
Quando usar: Definindo qualquer nova ferramenta para um agente
PRÁTICAS RECOMENDADAS DE ESQUEMA DE FERRAMENTA:
1. Descrições Detalhadas (Mais Importante)
RUIM - Muito vago:
{
"name": "get_stock_price",
"description": "Obtém preço de ação",
"input_schema": {
"type": "object",
"properties": {
"ticker": {"type": "string"}
}
}
}
BOM - Abrangente:
{
"name": "get_stock_price",
"description": "Recupera o preço atual da ação para um símbolo de ticker
fornecido. O símbolo do ticker deve ser um símbolo válido para uma empresa
de capital aberto em uma grande bolsa de valores dos EUA como NYSE ou NASDAQ.
Retorna o último preço de negociação em USD. Use quando o usuário perguntar
sobre preços atuais ou recentes de ações. NÃO fornece dados
históricos, informações da empresa ou previsões.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "O símbolo do ticker da ação, ex: AAPL para Apple Inc."
}
},
"required": ["ticker"]
}
}
2. Descrições de Parâmetros
Todo parâmetro precisa:
- O que é
- Formato esperado
- Valor de exemplo
- Casos extremos/limitações
{
"location": {
"type": "string",
"description": "Cidade e estado/país. Formato: 'Cidade, Estado' para EUA
(ex: 'São Francisco, CA') ou 'Cidade, País' para internacional
(ex: 'Tóquio, Japão'). Não use CEP ou coordenadas."
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Unidade de temperatura. Padrão é a localidade do usuário se não
especificado. Use 'fahrenheit' para usuários dos EUA, 'celsius' para outros."
}
}
3. Use Enums Quando Possível
Enums restringem o LLM a valores válidos:
"priority": {
"type": "string",
"enum": ["baixa", "média", "alta", "crítica"],
"description": "Nível de prioridade da tarefa"
}
"action": {
"type": "string",
"enum": ["criar", "ler", "atualizar", "excluir"],
"description": "A operação CRUD a ser executada"
}
4. Obrigatório vs Opcional
Seja explícito sobre o que é obrigatório:
{
"type": "object",
"properties": {
"query": {...}, // Obrigatório
"limit": {...}, // Opcional com padrão
"offset": {...} // Opcional
},
"required": ["query"],
"additionalProperties": false // Modo estrito
}
Ferramenta com Exemplos de Entrada
Usando exemplos para guiar o uso da ferramenta pelo LLM
Quando usar: Ferramentas complexas com objetos aninhados ou entradas sensíveis a formato
EXEMPLOS DE USO DA FERRAMENTA (Recurso Beta Anthropic):
Exemplos mostram a Claude padrões concretos que esquemas não conseguem expressar.
Melhora a precisão de 72% para 90% em operações complexas.
{ "name": "criar_evento_calendario", "description": "Cria um evento de calendário com participantes e lembretes opcionais", "input_schema": { "type": "object", "properties": { "title": {"type": "string", "description": "Título do evento"}, "start_time": { "type": "string", "description": "Data e hora no formato ISO 8601, ex: 2024-03-15T14:00:00Z" }, "duration_minutes": {"type": "integer", "description": "Duração do evento"}, "attendees": { "type": "array", "items": {"type": "string"}, "description": "Endereços de email dos participantes" } }, "required": ["title", "start_time", "duration_minutes"] }, "input_examples": [ { "title": "Reunião de Equipe", "start_time": "2024-03-15T09:00:00Z", "duration_minutes": 30, "attendees": ["[email protected]", "[email protected]"] }, { "title": "Bate-papo Rápido", "start_time": "2024-03-15T14:00:00Z", "duration_minutes": 15 }, { "title": "Revisão de Projeto", "start_time": "2024-03-15T16:00:00-05:00", "duration_minutes": 60, "attendees": ["[email protected]"] } ] }
PRINCÍPIOS DE DESIGN DE EXEMPLOS:
- Use dados realistas, não placeholders
- Mostre padrões de especificação mínima, parcial e completa
- Mantenha conciso: 1-5 exemplos por ferramenta
- Foque em casos ambíguos
Tratamento de Erros da Ferramenta
Retornando erros que ajudam o LLM a se recuperar
Quando usar: Qualquer ferramenta que pode falhar
PRÁTICAS RECOMENDADAS DE TRATAMENTO DE ERROS:
Retorne Erros Informativos
RUIM:
{"error": "Falhou"}
{"error": true}
BOM:
{
"error": true,
"error_type": "nao_encontrado",
"message": "Local 'Atlântida' não encontrado no banco de dados meteorológico.
Forneça um nome de cidade real como 'São Francisco, CA'.",
"suggestions": ["São Francisco, CA", "Los Angeles, CA"]
}
Resultado de Ferramenta Anthropic com Erro
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Erro: Local 'Atlântida' não encontrado no banco de dados meteorológico.
Forneça um nome de cidade real como 'São Francisco, CA'.",
"is_error": true
}
Categorias de Erro para Tratar
1. Erros de Validação de Entrada
- Parâmetros obrigatórios ausentes
- Formato inválido
- Valores fora do intervalo
2. Erros de Serviço Externo
- API indisponível
- Limite de taxa excedido
- Tempo limite excedido
3. Erros de Lógica de Negócio
- Recurso não encontrado
- Permissão negada
- Conflito/duplicata
4. Erros Internos
- Exceções inesperadas
- Corrupção de dados
Padrão de Implementação
from dataclasses import dataclass
from typing import Union
@dataclass
class ToolResult:
success: bool
content: str
error_type: str = None
suggestions: list[str] = None
def to_response(self) -> dict:
if self.success:
return {"content": self.content}
return {
"content": f"Erro ({self.error_type}): {self.content}",
"is_error": True
}
def get_weather(location: str) -> ToolResult:
# Validar entrada
if not location or len(location) < 2:
return ToolResult(
success=False,
content="Local deve ter pelo menos 2 caracteres",
error_type="validation_error"
)
try:
data = weather_api.fetch(location)
return ToolResult(
success=True,
content=f"Temperatura: {data.temp}°F, Condições: {data.conditions}"
)
except LocationNotFound:
return ToolResult(
success=False,
content=f"Local '{location}' não encontrado",
error_type="not_found",
suggestions=weather_api.suggest_locations(location)
)
except RateLimitError:
return ToolResult(
success=False,
content="Limite de taxa do serviço meteorológico excedido. Tente novamente em 60 segundos.",
error_type="rate_limit"
)
except Exception as e:
return ToolResult(
success=False,
content=f"Erro inesperado: {str(e)}",
error_type="internal_error"
)
Padrão de Ferramenta MCP
Construindo ferramentas usando Model Context Protocol
Quando usar: Criando ferramentas reutilizáveis e multiplataforma
IMPLEMENTAÇÃO DE FERRAMENTA MCP:
MCP (Model Context Protocol) é o padrão aberto da Anthropic para
conectar agentes de IA a sistemas externos. Construa uma vez, use em toda parte.
Servidor MCP Básico (TypeScript)
import { Server } from "@modelcontextprotocol/sdk/server";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
const server = new Server({
name: "weather-server",
version: "1.0.0"
});
// Definir ferramentas
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "get_weather",
description: "Obter clima atual para um local. Retorna
temperatura, condições e umidade. Use para consultas
meteorológicas sobre cidades específicas.",
inputSchema: {
type: "object",
properties: {
location: {
type: "string",
description: "Cidade e estado, ex: 'São Francisco, CA'"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
default: "fahrenheit"
}
},
required: ["location"]
}
}
]
}));
// Lidar com chamadas de ferramenta
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_weather") {
try {
const weather = await fetchWeather(args.location, args.unit);
return {
content: [
{
type: "text",
text: JSON.stringify(weather)
}
]
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Erro: ${error.message}`
}
],
isError: true
};
}
}
throw new Error(`Ferramenta desconhecida: ${name}`);
});
// Iniciar servidor
const transport = new StdioServerTransport();
await server.connect(transport);
Benefícios do MCP
- Compatibilidade universal entre provedores de LLM
- Bibliotecas de ferramentas reutilizáveis
- Suporte a transporte streaming e SSE
- Observabilidade integrada
- Controles de acesso a ferramentas
Padrão Tool Runner
Usando tool runners de SDK para manipulação automática
Quando usar: Construindo loops de ferramenta sem gerenciamento manual
TOOL RUNNER (SDK Anthropic Beta):
O tool runner lida automaticamente com o loop de chamada de ferramenta:
- Executa ferramentas quando Claude as chama
- Gerencia o estado da conversa
- Lida com tentativas de erro
- Fornece suporte a streaming
Exemplo Python
import anthropic
from anthropic import beta_tool
client = anthropic.Anthropic()
@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
'''Obter o clima atual em um determinado local.
Args:
location: A cidade e estado, ex: São Francisco, CA
unit: Unidade de temperatura, 'celsius' ou 'fahrenheit'
'''
# Implementação
return json.dumps({"temperature": "72°F", "conditions": "Ensolarado"})
@beta_tool
def search_web(query: str) -> str:
'''Pesquisar na web por informações.
Args:
query: A consulta de pesquisa
'''
# Implementação
return json.dumps({"results": [...]})
# Tool runner lida com o loop
runner = client.beta.messages.tool_runner(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[get_weather, search_web],
messages=[
{"role": "user", "content": "Como está o clima em Paris?"}
]
)
# Processar cada mensagem
for message in runner:
print(message.content[0].text)
# Ou apenas obter o resultado final
final = runner.until_done()
TypeScript com Zod
import { Anthropic } from '@anthropic-ai/sdk';
import { betaZodTool } from '@anthropic-ai/sdk/helpers/beta/zod';
import { z } from 'zod';
const anthropic = new Anthropic();
const getWeatherTool = betaZodTool({
name: 'get_weather',
description: 'Obter o clima atual em um determinado local',
inputSchema: z.object({
location: z.string().describe('Cidade e estado, ex: São Francisco, CA'),
unit: z.enum(['celsius', 'fahrenheit']).default('fahrenheit')
}),
run: async (input) => {
// Entrada com segurança de tipos!
return JSON.stringify({temperature: '72°F'});
}
});
const runner = anthropic.beta.messages.toolRunner({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
tools: [getWeatherTool],
messages: [{ role: 'user', content: "Como está o clima em Paris?" }]
});
for await (const message of runner) {
console.log(message.content[0].text);
}
Execução Paralela de Ferramentas
Executando múltiplas ferramentas simultaneamente
Quando usar: Chamadas de ferramenta independentes que podem ser executadas em paralelo
EXECUÇÃO PARALELA DE FERRAMENTAS:
Por padrão, Claude pode chamar múltiplas ferramentas em uma resposta.
Isso reduz drasticamente a latência para operações independentes.
Lidando com Resultados Paralelos
# Claude retorna múltiplos blocos tool_use:
response.content = [
{"type": "text", "text": "Vou verificar ambos os locais..."},
{"type": "tool_use", "id": "toolu_01", "name": "get_weather",
"input": {"location": "San Francisco, CA"}},
{"type": "tool_use", "id": "toolu_02", "name": "get_weather",
"input": {"location": "New York, NY"}},
{"type": "tool_use", "id": "toolu_03", "name": "get_time",
"input": {"timezone": "America/Los_Angeles"}},
{"type": "tool_use", "id": "toolu_04", "name": "get_time",
"input": {"timezone": "America/New_York"}}
]
Padrão de Execução Paralela em Python
from concurrent.futures import ThreadPoolExecutor
from typing import List, Dict
def execute_tools_parallel(tools_responses: List[Dict]) -> List[str]:
"""Executa ferramentas em paralelo com base em respostas da API."""
results = [None] * len(tools_responses)
def execute_tool(index: int, tool_call: Dict):
tool_name = tool_call["name"]
tool_input = tool_call["input"]
if tool_name == "get_weather":
results[index] = get_weather(**tool_input)
elif tool_name == "get_time":
results[index] = get_time(**tool_input)
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [
executor.submit(execute_tool, i, tc)
for i, tc in enumerate(tools_responses)
]
return results
# Exemplo de uso
parallel_results = execute_tools_parallel(tool_calls)
TypeScript com Promise.all
interface ToolCall {
id: string;
name: string;
input: Record<string, any>;
}
async function executeToolsParallel(
toolCalls: ToolCall[]
): Promise<string[]> {
const handlers: Record<string, (input: any) => Promise<string>> = {
get_weather: async (input) => {
const { location, unit } = input;
return JSON.stringify({temperature: '72°F'});
},
get_time: async (input) => {
const { timezone } = input;
return new Date().toLocaleTimeString('pt-BR', { timeZone: timezone });
}
};
const results = await Promise.all(
toolCalls.map(async (call) => {
try {
const handler = handlers[call.name];
if (!handler) {
throw new Error(`Ferramenta desconhecida: ${call.name}`);
}
return await handler(call.input);
} catch (error) {
return JSON.stringify({
error: true,
message: `Erro executando ${call.name}: ${error.message}`
});
}
})
);
return results;
}
// Exemplo de uso
const toolCalls: ToolCall[] = [
{ id: 'toolu_01', name: 'get_weather', input: { location: 'São Francisco, CA' } },
{ id: 'toolu_02', name: 'get_time', input: { timezone: 'America/Los_Angeles' } }
];
const results = await executeToolsParallel(toolCalls);
Padrão de Encadeamento de Ferramentas
Quando ferramentas dependem dos resultados de outras ferramentas
Quando usar: Ferramentas com dependências sequenciais
ENCADEAMENTO DE FERRAMENTAS:
Claude lida com encadeamento naturalmente - quando uma ferramenta retorna
um resultado, Claude pode decidir qual ferramenta chamar em seguida.
Isso forma uma cadeia de chamadas de ferramenta.
Exemplo de Encadeamento
def search_company(name: str) -> str:
# Retorna informações da empresa
return json.dumps({"symbol": "AAPL", "name": "Apple Inc.", "sector": "Tecnologia"})
def get_stock_price(symbol: str) -> str:
# Usa o símbolo da saída de search_company
return json.dumps({"symbol": symbol, "price": 150.25, "currency": "USD"})
def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
# Converte o preço da ação para outra moeda
return json.dumps({
"original": {"amount": amount, "currency": from_currency},
"converted": {"amount": amount * 0.92, "currency": to_currency}
})
Lidando com Encadeamento
# Claude decide a sequência:
# 1. search_company("Apple")
# 2. get_stock_price("AAPL")
# 3. convert_currency(150.25, "USD", "EUR")
messages = [
{"role": "user", "content": "Quanto vale uma ação da Apple em euros?"}
]
# Cada chamada de ferramenta é automaticamente encadeada
# Claude interpreta resultados e decide a próxima ação
Considerações sobre Encadeamento
- Encadeamento aumenta latência (mais viagens de ida e volta)
- Ferramentas com efeitos colaterais precisam ser idempotentes
- Cada link na cadeia é uma oportunidade para o LLM alucinar
- Mantenha cadeias curtas (2-3 ferramentas) para confiabilidade