- Conectar modelos a dados em tempo real (bancos de dados, calendários, APIs internas) sem gambiarras no prompt.
- Agentes que executam ações em nome dos usuários: criar tickets, enviar e-mails, executar consultas.
- Fluxos de várias etapas em que o modelo decide quais ferramentas invocar e em que ordem.
- Substituir a análise frágil com regex por chamadas de função estruturadas na extração de dados.
Início rápido
Permita que modelos de IA chamem funções externas com parâmetros estruturados.curl -X POST https://router.toqan.ai/eu/v3/router/responses \
-H "Authorization: Bearer $TOQAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4",
"input": "What is the weather in NYC?",
"tools": [{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "City and state" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["location"]
}
}],
"tool_choice": "auto"
}'
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.TOQAN_API_KEY,
baseURL: "https://router.toqan.ai/eu/v3/router",
});
async function getWeather(location: string, unit = "celsius") {
// Your implementation here - fetch from a weather API, etc.
return { location, temperature: 22, unit, conditions: "sunny" };
}
const tools = [
{
type: "function" as const,
name: "get_weather",
description: "Get current weather for a location",
parameters: {
type: "object",
properties: {
location: { type: "string", description: "City and state" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["location"],
},
},
];
const response = await client.responses.create({
model: "openai/gpt-5.4",
input: "What's the weather in NYC?",
tools,
tool_choice: "auto",
});
const toolCall = response.output.find((item) => item.type === "function_call");
if (toolCall && toolCall.type === "function_call") {
const args = JSON.parse(toolCall.arguments);
const result = await getWeather(args.location, args.unit);
const callId = toolCall.call_id;
// Pattern: previous_response_id - the router maintains conversation state server-side
const finalResponse = await client.responses.create({
model: "openai/gpt-5.4",
previous_response_id: response.id,
input: [{
type: "function_call_output",
call_id: callId,
output: JSON.stringify(result),
}],
});
console.log(finalResponse.output_text);
}
from openai import OpenAI
import json
import os
client = OpenAI(
api_key=os.environ.get("TOQAN_API_KEY"),
base_url="https://router.toqan.ai/eu/v3/router",
)
def get_weather(location: str, unit: str = "celsius") -> dict:
# Your implementation here - fetch from a weather API, etc.
return {"location": location, "temperature": 22, "unit": unit, "conditions": "sunny"}
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
}
]
response = client.responses.create(
model="openai/gpt-5.4",
input="What's the weather in NYC?",
tools=tools,
tool_choice="auto",
)
tool_call = next((item for item in response.output if item.type == "function_call"), None)
if tool_call:
args = json.loads(tool_call.arguments)
result = get_weather(args["location"], args.get("unit", "celsius"))
# Pattern: previous_response_id - the router maintains conversation state server-side
final_response = client.responses.create(
model="openai/gpt-5.4",
previous_response_id=response.id,
input=[{
"type": "function_call_output",
"call_id": tool_call.call_id,
"output": json.dumps(result),
}],
)
print(final_response.output_text)
curl -X POST https://router.toqan.ai/eu/v3/router/chat/completions \
-H "Authorization: Bearer $TOQAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4",
"messages": [{"role": "user", "content": "What is the weather in NYC?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "City and state" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["location"]
}
}
}],
"tool_choice": "auto"
}'
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.TOQAN_API_KEY,
baseURL: "https://router.toqan.ai/eu/v3/router",
});
const tools = [
{
type: "function" as const,
function: {
name: "get_weather",
description: "Get current weather for a location",
parameters: {
type: "object",
properties: {
location: { type: "string", description: "City and state" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["location"],
},
},
},
];
async function getWeather(location: string, unit = "celsius") {
// Your implementation here - fetch from a weather API, etc.
return { location, temperature: 22, unit, conditions: "sunny" };
}
const response = await client.chat.completions.create({
model: "openai/gpt-5.4",
messages: [{ role: "user", content: "What's the weather in NYC?" }],
tools,
tool_choice: "auto",
});
if (response.choices[0].message.tool_calls) {
const toolCall = response.choices[0].message.tool_calls[0];
const args = JSON.parse(toolCall.function.arguments);
const result = await getWeather(args.location, args.unit);
const finalResponse = await client.chat.completions.create({
model: "openai/gpt-5.4",
messages: [
{ role: "user", content: "What's the weather in NYC?" },
response.choices[0].message,
{
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(result),
},
],
});
console.log(finalResponse.choices[0].message.content);
}
from openai import OpenAI
import json
import os
client = OpenAI(
api_key=os.environ.get("TOQAN_API_KEY"),
base_url="https://router.toqan.ai/eu/v3/router",
)
def get_weather(location: str, unit: str = "celsius") -> dict:
# Your implementation here - fetch from a weather API, etc.
return {"location": location, "temperature": 22, "unit": unit, "conditions": "sunny"}
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
},
}
]
messages = [{"role": "user", "content": "What's the weather in NYC?"}]
response = client.chat.completions.create(
model="openai/gpt-5.4",
messages=messages,
tools=tools,
tool_choice="auto",
)
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
result = get_weather(args["location"], args.get("unit", "celsius"))
messages.append(response.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result),
})
final_response = client.chat.completions.create(
model="openai/gpt-5.4",
messages=messages,
)
print(final_response.choices[0].message.content)
Configuração
Definição da ferramenta
Responses API (/v3/router/responses): formato plano:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | "function" | Sim | Tipo da ferramenta |
name | string | Sim | Identificador da função |
description | string | Sim | O que a função faz |
parameters | object | Sim | JSON Schema dos parâmetros |
/v3/router/chat/completions): wrapper function aninhado:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | "function" | Sim | Tipo da ferramenta |
function.name | string | Sim | Identificador da função |
function.description | string | Sim | O que a função faz |
function.parameters | object | Sim | JSON Schema dos parâmetros |
Opções de escolha de ferramenta
| Valor | Comportamento |
|---|---|
"auto" | O modelo decide quando usar ferramentas |
"none" | Desativa o uso de ferramentas |
"required" | Força o uso de ferramentas |
{type: "function", function: {name: "tool_name"}} | Força uma ferramenta específica |
Formato da mensagem de ferramenta
Este formato se aplica ao endpoint de Chat Completions (
/v3/router/chat/completions). Na Responses API, os resultados das ferramentas usam type: "function_call_output", call_id e output.tool:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
role | "tool" | Sim | Role da mensagem para resultados de ferramentas |
tool_call_id | string | null | Sim | ID da chamada de ferramenta que está sendo respondida |
content | string | Sim | Resultado da execução da ferramenta serializado em JSON |
O
tool_call_id pode ser null em determinados cenários, como quando os resultados das ferramentas são fornecidos sem uma chamada de ferramenta correspondente do modelo ou ao trabalhar com provedores que não exigem IDs de chamada de ferramenta.Exemplos de código
curl -X POST https://router.toqan.ai/eu/v3/router/responses \
-H "Authorization: Bearer $TOQAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4",
"input": "What is the weather like in San Francisco?",
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit"
}
},
"required": ["location"]
}
}
],
"tool_choice": "auto"
}'
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.TOQAN_API_KEY,
baseURL: "https://router.toqan.ai/eu/v3/router",
});
const tools = [
{
type: "function" as const,
name: "get_weather",
description: "Get the current weather for a location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA",
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
description: "The temperature unit",
},
},
required: ["location"],
},
},
];
const response = await client.responses.create({
model: "openai/gpt-5.4",
input: "What's the weather like in San Francisco?",
tools,
tool_choice: "auto",
});
const toolCall = response.output.find((item) => item.type === "function_call");
if (toolCall && toolCall.type === "function_call") {
const args = JSON.parse(toolCall.arguments);
const weatherResult = {
temperature: 72,
unit: "fahrenheit",
description: "Sunny with light clouds",
};
// Pattern: spread response.output - the full conversation history is sent client-side
const finalResponse = await client.responses.create({
model: "openai/gpt-5.4",
input: [
...response.output,
{
type: "function_call_output",
call_id: toolCall.call_id,
output: JSON.stringify(weatherResult),
},
],
});
console.log(finalResponse.output_text);
}
from openai import OpenAI
import json
import os
client = OpenAI(
api_key=os.environ.get("TOQAN_API_KEY"),
base_url="https://router.toqan.ai/eu/v3/router",
)
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit",
},
},
"required": ["location"],
},
}
]
response = client.responses.create(
model="openai/gpt-5.4",
input="What's the weather like in San Francisco?",
tools=tools,
tool_choice="auto",
)
tool_call = next((item for item in response.output if item.type == "function_call"), None)
if tool_call:
arguments = json.loads(tool_call.arguments)
weather_result = {
"temperature": 72,
"unit": "fahrenheit",
"description": "Sunny with light clouds",
}
final_response = client.responses.create(
model="openai/gpt-5.4",
input=[
*response.output,
{
"type": "function_call_output",
"call_id": tool_call.call_id,
"output": json.dumps(weather_result),
},
],
)
print(final_response.output_text)
curl -X POST https://router.toqan.ai/eu/v3/router/chat/completions \
-H "Authorization: Bearer $TOQAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4",
"messages": [
{
"role": "user",
"content": "What is the weather like in San Francisco?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit"
}
},
"required": ["location"]
}
}
}
],
"tool_choice": "auto"
}'
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.TOQAN_API_KEY,
baseURL: "https://router.toqan.ai/eu/v3/router",
});
const tools = [
{
type: "function" as const,
function: {
name: "get_weather",
description: "Get the current weather for a location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA",
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
description: "The temperature unit",
},
},
required: ["location"],
},
},
},
];
const messages = [
{ role: "user" as const, content: "What's the weather like in San Francisco?" },
];
const response = await client.chat.completions.create({
model: "openai/gpt-5.4",
messages,
tools,
tool_choice: "auto",
});
if (response.choices[0].message.tool_calls) {
const toolCall = response.choices[0].message.tool_calls[0];
const args = JSON.parse(toolCall.function.arguments);
const weatherResult = {
temperature: 72,
unit: "fahrenheit",
description: "Sunny with light clouds",
};
messages.push(response.choices[0].message);
messages.push({
role: "tool" as const,
tool_call_id: toolCall.id,
content: JSON.stringify(weatherResult),
});
const finalResponse = await client.chat.completions.create({
model: "openai/gpt-5.4",
messages,
});
console.log(finalResponse.choices[0].message.content);
}
from openai import OpenAI
import json
import os
client = OpenAI(
api_key=os.environ.get("TOQAN_API_KEY"),
base_url="https://router.toqan.ai/eu/v3/router",
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit",
},
},
"required": ["location"],
},
},
}
]
messages = [{"role": "user", "content": "What's the weather like in San Francisco?"}]
response = client.chat.completions.create(
model="openai/gpt-5.4",
messages=messages,
tools=tools,
tool_choice="auto",
)
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
arguments = json.loads(tool_call.function.arguments)
weather_result = {
"temperature": 72,
"unit": "fahrenheit",
"description": "Sunny with light clouds",
}
messages.append(response.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(weather_result),
})
final_response = client.chat.completions.create(
model="openai/gpt-5.4",
messages=messages,
)
print(final_response.choices[0].message.content)
Padrões de execução de funções
Handler básico de ferramentas
Um registro que mapeia nomes de ferramentas para funções handler, substituindo instruções switch por chamada por um único caminho de despacho.class ToolHandler {
constructor() {
this.tools = new Map();
}
register(name, func, schema) {
this.tools.set(name, { func, schema });
}
async execute(toolCall) {
const tool = this.tools.get(toolCall.function.name);
if (!tool) throw new Error(`Unknown tool: ${toolCall.function.name}`);
const args = JSON.parse(toolCall.function.arguments);
const result = await tool.func(args);
return {
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(result),
};
}
}
// Stubs - replace with your actual implementations
const weatherSchema = { type: "object", properties: { location: { type: "string" } }, required: ["location"] };
const searchSchema = { type: "object", properties: { query: { type: "string" } }, required: ["query"] };
const getWeatherAPI = async ({ location }) => ({ temperature: 72, condition: "sunny", location });
const searchWebAPI = async ({ query }) => ({ results: [`Result for: ${query}`] });
const response = { choices: [{ message: { tool_calls: [] } }] };
const handler = new ToolHandler();
handler.register("get_weather", getWeatherAPI, weatherSchema);
handler.register("search_web", searchWebAPI, searchSchema);
// Execute tool calls
const toolResults = await Promise.all(
response.choices[0].message.tool_calls.map((call) => handler.execute(call)),
);
import json
import asyncio
async def get_weather_api(args: dict) -> dict: return {"temperature": 72, "condition": "sunny", "location": args["location"]} # replace with your weather API
async def search_web_api(args: dict) -> dict: return {"results": [f"Result for: {args['query']}"]} # replace with your search API
class ToolHandler:
def __init__(self):
self._tools = {}
def register(self, name: str, func):
self._tools[name] = func
async def execute(self, tool_call) -> dict:
func = self._tools.get(tool_call.function.name)
if not func:
raise ValueError(f"Unknown tool: {tool_call.function.name}")
args = json.loads(tool_call.function.arguments)
result = await func(args)
return {"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result)}
handler = ToolHandler()
handler.register("get_weather", get_weather_api)
handler.register("search_web", search_web_api)
async def execute_all_tools(message):
return await asyncio.gather(*[handler.execute(call) for call in message.tool_calls])
Execução paralela de ferramentas
Quando o modelo retornar várias chamadas de ferramentas em um único turno, execute-as simultaneamente para reduzir a latência.async function getWeatherAsync(args: Record<string, unknown>) { return { temperature: 72, condition: "sunny" }; } // replace with your weather API
async function searchProductsAsync(args: Record<string, unknown>) { return { products: [] as unknown[] }; } // replace with your product search
async function checkInventoryAsync(args: Record<string, unknown>) { return { inStock: true }; } // replace with your inventory API
// Replace with your actual response and messages array
const response = { choices: [{ message: { role: "assistant" as const, content: null as string | null, tool_calls: [] as Array<{ function: { name: string; arguments: string }; id: string }> } }] }; // e.g., client.chat.completions.create(...)
let messages: Array<{ role: string; tool_call_id?: string; content: string | null }> = [];
const dispatch: Record<string, (args: Record<string, unknown>) => Promise<unknown>> = {
get_weather: getWeatherAsync,
search_products: searchProductsAsync,
check_inventory: checkInventoryAsync,
};
async function executeToolsParallel(
toolCalls: Array<{ function: { name: string; arguments: string }; id: string }>,
) {
return Promise.all(
toolCalls.map(async (call) => {
const args = JSON.parse(call.function.arguments) as Record<string, unknown>;
const fn = dispatch[call.function.name] ?? (() => Promise.resolve({ error: `Unknown: ${call.function.name}` }));
const result = await fn(args);
return { role: "tool" as const, tool_call_id: call.id, content: JSON.stringify(result) };
}),
);
}
if (response.choices[0].message.tool_calls) {
messages.push(response.choices[0].message);
const toolResults = await executeToolsParallel(response.choices[0].message.tool_calls);
messages = [...messages, ...toolResults];
}
import asyncio
import json
async def get_weather_async(args): return {"temperature": 72, "condition": "sunny"} # replace with your weather API
async def search_products_async(args): return {"products": []} # replace with your product search
async def check_inventory_async(args): return {"in_stock": True} # replace with your inventory API
# Replace these with your actual API call and conversation history
response = None # e.g. client.chat.completions.create(model=..., messages=messages, tools=[...])
messages: list = []
async def execute_tools_parallel(tool_calls):
async def execute_single_tool(tool_call):
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# Route to appropriate function
if function_name == "get_weather":
result = await get_weather_async(arguments)
elif function_name == "search_products":
result = await search_products_async(arguments)
elif function_name == "check_inventory":
result = await check_inventory_async(arguments)
else:
result = {"error": f"Unknown function: {function_name}"}
return {
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
}
# Execute all tools concurrently
results = await asyncio.gather(
*[execute_single_tool(call) for call in tool_calls]
)
return results
# Usage
if response.choices[0].message.tool_calls:
messages.append(response.choices[0].message)
tool_results = await execute_tools_parallel(
response.choices[0].message.tool_calls
)
# Add to conversation
messages.extend(tool_results)
Casos de uso avançados
Integração com banco de dados
Exponha o acesso SQL como uma ferramenta para que o modelo possa consultar dados diretamente. Sempre sanitize as consultas antes da execução.const sanitizeSql = (q: string) => q; // replace with your SQL sanitizer
const getDbConnection = () => null as unknown as { execute: (q: string) => Promise<{ fetchAll: () => unknown[] }> }; // replace with your DB client
const db = getDbConnection();
const tools = [
{
type: "function" as const,
function: {
name: "query_database",
description: "Query the customer database",
parameters: {
type: "object",
properties: {
query: { type: "string", description: "SQL query to execute" },
limit: { type: "integer", description: "Maximum number of results" },
},
required: ["query"],
},
},
},
];
async function queryDatabase(args: { query: string; limit?: number }) {
const query = sanitizeSql(args.query);
const limit = args.limit ?? 10;
const results = await db.execute(`${query} LIMIT ${limit}`); // limit is integer-typed in the schema, safe to interpolate directly
return { results: results.fetchAll() };
}
sanitize_sql = lambda q: q # replace with your SQL sanitizer
def get_db_connection():
pass # replace with your database client, e.g. from myapp.db import get_db_connection
db = get_db_connection()
tools = [
{
"type": "function",
"function": {
"name": "query_database",
"description": "Query the customer database",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "SQL query to execute"
},
"limit": {
"type": "integer",
"description": "Maximum number of results"
}
},
"required": ["query"]
}
}
}
]
async def query_database(args):
query = sanitize_sql(args["query"])
limit = args.get("limit", 10)
results = await db.execute(f"{query} LIMIT {limit}")
return {"results": results.fetchall()}
Integração com API
Exponha ações de serviços externos (e-mail, calendário, notificações) como ferramentas que o modelo pode invocar durante uma conversa.const apiTools = [
{
type: "function",
function: {
name: "send_email",
description: "Send an email to a recipient",
parameters: {
type: "object",
properties: {
to: { type: "string", description: "Email address" },
subject: { type: "string", description: "Email subject" },
body: { type: "string", description: "Email content" },
},
required: ["to", "subject", "body"],
},
},
},
{
type: "function",
function: {
name: "create_calendar_event",
description: "Create a calendar event",
parameters: {
type: "object",
properties: {
title: { type: "string" },
start_time: { type: "string", format: "date-time" },
duration: { type: "integer", description: "Duration in minutes" },
attendees: { type: "array", items: { type: "string" } },
},
required: ["title", "start_time"],
},
},
},
];
const emailAPI = { send: async (args: Record<string, unknown>) => ({ messageId: "msg_001" }) }; // replace with your email client
const calendarAPI = { createEvent: async (args: Record<string, unknown>) => ({ eventId: "evt_001" }) }; // replace with your calendar client
const executeApiTool = async (toolCall) => {
const { name } = toolCall.function;
const args = JSON.parse(toolCall.function.arguments);
switch (name) {
case "send_email":
return await emailAPI.send(args);
case "create_calendar_event":
return await calendarAPI.createEvent(args);
default:
throw new Error(`Unknown API tool: ${name}`);
}
};
import json
# Replace with your actual API clients
class EmailAPI:
async def send(self, args: dict) -> dict: return {"message_id": "msg_001"}
class CalendarAPI:
async def create_event(self, args: dict) -> dict: return {"event_id": "evt_001"}
email_api = EmailAPI()
calendar_api = CalendarAPI()
api_tools = [
{
"type": "function",
"function": {
"name": "send_email",
"description": "Send an email to a recipient",
"parameters": {
"type": "object",
"properties": {
"to": {"type": "string", "description": "Email address"},
"subject": {"type": "string", "description": "Email subject"},
"body": {"type": "string", "description": "Email content"},
},
"required": ["to", "subject", "body"],
},
},
},
{
"type": "function",
"function": {
"name": "create_calendar_event",
"description": "Create a calendar event",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start_time": {"type": "string", "format": "date-time"},
"duration": {"type": "integer", "description": "Duration in minutes"},
"attendees": {"type": "array", "items": {"type": "string"}},
},
"required": ["title", "start_time"],
},
},
},
]
async def execute_api_tool(tool_call) -> dict:
args = json.loads(tool_call.function.arguments)
match tool_call.function.name:
case "send_email":
return await email_api.send(args)
case "create_calendar_event":
return await calendar_api.create_event(args)
case _:
raise ValueError(f"Unknown API tool: {tool_call.function.name}")
Fluxos de várias etapas
Execute o modelo em loop até que ele produza uma resposta final em texto, permitindo um comportamento de agente em várias etapas sem streaming.import { OpenAI } from "openai";
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
const client = new OpenAI({
apiKey: process.env.TOQAN_API_KEY,
baseURL: "https://router.toqan.ai/eu/v3/router",
});
class WorkflowEngine {
private tools: Record<string, { func: (args: Record<string, unknown>) => Promise<unknown>; schema: ChatCompletionTool }> = {};
register(name: string, func: (args: Record<string, unknown>) => Promise<unknown>, schema: ChatCompletionTool) {
this.tools[name] = { func, schema };
}
async executeWorkflow(initialPrompt: string, maxSteps = 10): Promise<string> {
const conversation: ChatCompletionMessageParam[] = [{ role: "user", content: initialPrompt }];
for (let step = 0; step < maxSteps; step++) {
const response = await client.chat.completions.create({
model: "openai/gpt-5.4",
messages: conversation,
tools: Object.values(this.tools).map((t) => t.schema),
tool_choice: "auto",
});
conversation.push(response.choices[0].message);
if (!response.choices[0].message.tool_calls) {
return response.choices[0].message.content ?? "";
}
for (const call of response.choices[0].message.tool_calls) {
conversation.push(await this.executeTool(call));
}
}
return "Workflow exceeded maximum steps";
}
private async executeTool(toolCall: { function: { name: string; arguments: string }; id: string }): Promise<ChatCompletionMessageParam> {
const args = JSON.parse(toolCall.function.arguments) as Record<string, unknown>;
const tool = this.tools[toolCall.function.name];
const result = tool ? await tool.func(args) : { error: `Unknown tool: ${toolCall.function.name}` };
return { role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result) };
}
}
from openai import AsyncOpenAI
import os
import json
client = AsyncOpenAI(
api_key=os.environ.get("TOQAN_API_KEY"),
base_url="https://router.toqan.ai/eu/v3/router",
)
class WorkflowEngine:
def __init__(self):
self.tools = {}
self.conversation = []
def register_tool(self, name, func, schema):
self.tools[name] = {"func": func, "schema": schema}
async def execute_workflow(self, initial_prompt, max_steps=10):
self.conversation = [{"role": "user", "content": initial_prompt}]
for step in range(max_steps):
response = await client.chat.completions.create(
model="openai/gpt-5.4",
messages=self.conversation,
tools=[v["schema"] for v in self.tools.values()],
tool_choice="auto"
)
self.conversation.append(response.choices[0].message)
# Check if tools need to be executed
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
result = await self.execute_tool(tool_call)
self.conversation.append(result)
else:
# No tools called, workflow complete
return response.choices[0].message.content
return "Workflow exceeded maximum steps"
async def execute_tool(self, tool_call):
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
if tool_name in self.tools:
result = await self.tools[tool_name]["func"](arguments)
else:
result = {"error": f"Unknown tool: {tool_name}"}
return {
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
}
Tratamento de erros
Retorne objetos de erro estruturados nas saídas das ferramentas para que o modelo possa relatar falhas ou tentar novamente com argumentos corrigidos.const validateArgs = (name: string, args: Record<string, unknown>) => ({ valid: true, errors: [] as string[] }); // replace with your validator
const executeFunction = async (name: string, args: Record<string, unknown>): Promise<unknown> => ({}); // replace with your dispatcher
const safeToolExecution = async (toolCall) => {
try {
const args = JSON.parse(toolCall.function.arguments);
// Validate arguments
const validation = validateArgs(toolCall.function.name, args);
if (!validation.valid) {
return {
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify({
error: "Invalid arguments",
details: validation.errors,
}),
};
}
// Execute with timeout
const result = await Promise.race([
executeFunction(toolCall.function.name, args),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Tool execution timeout")), 30000),
),
]);
return {
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify(result),
};
} catch (error) {
console.error(`Tool execution failed: ${error.message}`);
return {
role: "tool",
tool_call_id: toolCall.id,
content: JSON.stringify({
error: "Tool execution failed",
message: error.message,
}),
};
}
};
import json
import asyncio
def validate_args(name: str, args: dict) -> dict: return {"valid": True, "errors": []} # replace with your validator
async def execute_function(name: str, args: dict): return {} # replace with your dispatcher
async def safe_tool_execution(tool_call) -> dict:
try:
args = json.loads(tool_call.function.arguments)
validation = validate_args(tool_call.function.name, args)
if not validation["valid"]:
return {
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps({"error": "Invalid arguments", "details": validation["errors"]}),
}
result = await asyncio.wait_for(
execute_function(tool_call.function.name, args),
timeout=30,
)
return {"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result)}
except Exception as e:
return {
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps({"error": "Tool execution failed", "message": str(e)}),
}
Boas práticas
Design de ferramentas
- Use nomes de função claros e descritivos.
- Forneça descrições detalhadas dos parâmetros.
- Inclua exemplos nas descrições.
- Torne as ferramentas idempotentes sempre que possível.
Design de schemas
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state (e.g., 'San Francisco, CA')",
"examples": ["New York, NY", "London, UK"]
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"description": "Temperature unit system",
"default": "metric"
}
},
"required": ["location"]
}
Considerações de segurança
- Nunca exponha operações destrutivas diretamente.
- Valide todas as entradas minuciosamente.
- Use listas de permissões para operações sensíveis.
- Implemente uma autenticação adequada.
- Registre em log todas as execuções de ferramentas.
Solução de problemas
A ferramenta não está sendo chamada- Verifique se as descrições das ferramentas estão claras.
- Confirme se os schemas dos parâmetros estão corretos.
- Garanta que tool_choice esteja definido adequadamente.
- Experimente prompts mais explícitos.
- Valide o JSON Schema minuciosamente.
- Adicione exemplos de parâmetros.
- Verifique se os campos obrigatórios estão marcados.
- Simplifique estruturas de parâmetros complexas.
- Implemente um tratamento de erros adequado.
- Adicione proteção por timeout.
- Valide as entradas antes da execução.
- Retorne mensagens de erro estruturadas.
Limitações
| Limitação | Impacto | Alternativa |
|---|---|---|
| Limite de ferramentas | Máximo de ~20 ferramentas por solicitação | Agrupe funções relacionadas |
| Tamanho dos parâmetros | Schemas grandes podem falhar | Simplifique a estrutura dos parâmetros |
| Tempo de execução | As ferramentas bloqueiam a resposta | Use padrões assíncronos |
| Propagação de erros | Falhas podem interromper o fluxo | Implemente recuperação de erros |
| Diferenças entre modelos | Qualidade variável na chamada de ferramentas | Teste com vários modelos |
Otimização de desempenho
Cache de ferramentas
Armazene em cache os resultados de ferramentas somente leitura para evitar chamadas externas redundantes em uma sessão.class CachedToolExecutor {
constructor() {
this.cache = new Map();
this.cacheTTL = 300000; // 5 minutes
}
getCacheKey(toolCall) {
return `${toolCall.function.name}:${toolCall.function.arguments}`;
}
async executeFunction(toolCall) {
throw new Error("executeFunction must be implemented in a subclass");
}
async execute(toolCall) {
const key = this.getCacheKey(toolCall);
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
return cached.result;
}
const result = await this.executeFunction(toolCall);
this.cache.set(key, { result, timestamp: Date.now() });
return result;
}
}
import time
import json
class CachedToolExecutor:
def __init__(self, cache_ttl: int = 300):
self._cache: dict = {}
self._cache_ttl = cache_ttl
def _cache_key(self, tool_call) -> str:
return f"{tool_call.function.name}:{tool_call.function.arguments}"
async def execute_function(self, tool_call) -> dict:
raise NotImplementedError("Implement in subclass")
async def execute(self, tool_call) -> dict:
key = self._cache_key(tool_call)
entry = self._cache.get(key)
if entry and (time.time() - entry["timestamp"]) < self._cache_ttl:
return entry["result"]
result = await self.execute_function(tool_call)
self._cache[key] = {"result": result, "timestamp": time.time()}
return result
Operações em lote
Aceite arrays de entradas em uma única ferramenta para reduzir o número de turnos do modelo necessários em operações em massa.const getWeather = (location: string) => ({ temperature: 72, condition: "sunny", location }); // replace with your weather API
const getWeatherBatch = (locations: string[]) =>
Object.fromEntries(locations.map((loc) => [loc, getWeather(loc)]));
const getWeatherBatchTool = {
type: "function" as const,
name: "get_weather_batch",
description: "Get weather for multiple locations in a single call",
parameters: {
type: "object",
properties: {
locations: { type: "array", items: { type: "string" }, description: "List of city names" },
},
required: ["locations"],
},
};
# Pattern fragment: add client setup and get_weather stub before using
# Instead of multiple individual calls
def get_weather_batch(locations):
return {loc: get_weather(loc) for loc in locations}
# Tool that accepts multiple inputs
get_weather_batch_tool = {
"type": "function",
"name": "get_weather_batch",
"description": "Get weather for multiple locations in a single call",
"parameters": {
"type": "object",
"properties": {
"locations": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["locations"]
}
}

