Tokia

Agente com Tool Calling

Um agente e um modelo que decide quais ferramentas chamar para responder uma pergunta. O fluxo: o modelo recebe a pergunta e a lista de ferramentas disponiveis, decide qual chamar, voce executa a ferramenta, e devolve o resultado para o modelo gerar a resposta final.

Como funciona

  • Voce define ferramentas com nome, descricao e schema de parametros
  • O modelo analisa a pergunta e retorna tool_calls (nao executa nada)
  • Seu codigo executa as funcoes e devolve o resultado como mensagem tool
  • O modelo recebe o resultado e gera a resposta final em linguagem natural

1. Definir ferramentas

python
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://api.usetokia.com/v1",
    api_key="sk-...",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "consultar_clima",
            "description": "Consulta a previsao do tempo de uma cidade",
            "parameters": {
                "type": "object",
                "properties": {
                    "cidade": {"type": "string", "description": "Nome da cidade"},
                },
                "required": ["cidade"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "consultar_cotacao",
            "description": "Consulta a cotacao atual do dolar em BRL",
            "parameters": {"type": "object", "properties": {}},
        },
    },
]

2. Implementar as funcoes

python
def consultar_clima(cidade: str) -> str:
    dados = {
        "brasilia": "28C, ensolarado",
        "sao paulo": "22C, nublado",
        "rio de janeiro": "32C, sol com nuvens",
    }
    return dados.get(cidade.lower(), f"Dados nao disponiveis para {cidade}")

def consultar_cotacao() -> str:
    return "USD 1 = BRL 5.60"

TOOL_REGISTRY = {
    "consultar_clima": consultar_clima,
    "consultar_cotacao": consultar_cotacao,
}

3. Loop do agente

O loop continua enquanto o modelo pedir tool calls. Quando ele responde sem pedir ferramentas, a conversa termina:

python
def run_agent(question: str) -> str:
    messages = [
        {"role": "system", "content": "Voce e um assistente que usa ferramentas para responder."},
        {"role": "user", "content": question},
    ]

    while True:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            tools=tools,
        )

        choice = response.choices[0]

        if choice.finish_reason == "tool_calls" and choice.message.tool_calls:
            messages.append(choice.message)

            for tool_call in choice.message.tool_calls:
                fn_name = tool_call.function.name
                fn_args = json.loads(tool_call.function.arguments)

                fn = TOOL_REGISTRY.get(fn_name)
                if fn:
                    result = fn(**fn_args)
                else:
                    result = f"Ferramenta {fn_name} nao encontrada"

                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": str(result),
                })
        else:
            return choice.message.content or ""

print(run_agent("Como esta o tempo em Brasilia e qual a cotacao do dolar?"))

Versao TypeScript

typescript
import OpenAI from "openai";
import type { ChatCompletionMessageParam } from "openai/resources/chat/completions";

const client = new OpenAI({
  baseURL: "https://api.usetokia.com/v1",
  apiKey: process.env.TOKIA_API_KEY,
});

const toolRegistry: Record<string, (args: Record<string, string>) => string> = {
  consultar_clima: ({ cidade }) => {
    const dados: Record<string, string> = {
      brasilia: "28C, ensolarado",
      "sao paulo": "22C, nublado",
    };
    return dados[cidade.toLowerCase()] ?? "Dados indisponiveis";
  },
};

async function runAgent(question: string): Promise<string> {
  const messages: ChatCompletionMessageParam[] = [
    { role: "system", content: "Use ferramentas para responder." },
    { role: "user", content: question },
  ];

  while (true) {
    const response = await client.chat.completions.create({
      model: "gpt-4o-mini",
      messages,
      tools: [
        {
          type: "function",
          function: {
            name: "consultar_clima",
            description: "Previsao do tempo de uma cidade",
            parameters: {
              type: "object",
              properties: { cidade: { type: "string" } },
              required: ["cidade"],
            },
          },
        },
      ],
    });

    const choice = response.choices[0];
    if (!choice.message.tool_calls?.length) {
      return choice.message.content ?? "";
    }

    messages.push(choice.message);
    for (const tc of choice.message.tool_calls) {
      const fn = toolRegistry[tc.function.name];
      const args = JSON.parse(tc.function.arguments);
      messages.push({
        role: "tool",
        tool_call_id: tc.id,
        content: fn ? fn(args) : "Ferramenta nao encontrada",
      });
    }
  }
}

Boas praticas

  • Limite de iteracoes: adicione um contador no loop (max 5-10 iteracoes) para evitar loops infinitos se o modelo ficar chamando tools sem parar.
  • Descricoes claras: a qualidade das descricoes das ferramentas impacta diretamente a precisao do modelo em escolher a ferramenta certa.
  • Modelo: gpt-4o-mini funciona bem para 2-5 tools. Para cenarios com 10+ tools ou logica complexa de decisao, use claude-sonnet-46.
  • Seguranca: valide os argumentos antes de executar. O modelo pode enviar valores inesperados.

Proximos passos