Skip to content
Integrations/Custom Agents

Build Your Own Agent

A small, bounded loop with explicit tools and predictable exits.

Prerequisites

Complete the integration setup and verify a successful text request before configuring this client. Review the shared compatibility checks alongside the client-specific limitations below.

A Tool Loop

An agent sends conversation history, receives an answer or tool call, executes only allowed tools, and sends the result back. The example below uses a read-only tool and a hard turn limit. Never pass model-generated arguments to a shell.

Use Node.js 22 or later. In a new project directory, install the SDK:

Terminal
npm init -y
npm install openai

Save the following as agent.mjs. The example uses openai/gpt-5.5; substitute an enabled model that supports function tools.

agent.mjs
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.AUDIOLAB_API_KEY,
  baseURL: process.env.AUDIOLAB_BASE_URL,
  maxRetries: 0, // Decide explicitly how to handle uncertain requests.
  timeout: 30_000,
});
const messages = [{ role: "user", content: "What time is it in UTC?" }];
const tools = [
  {
    type: "function",
    function: {
      name: "utc_time",
      description: "Read the current UTC time",
      parameters: {
        type: "object",
        properties: {},
        additionalProperties: false,
      },
    },
  },
];
let completed = false;
for (let turn = 0; turn < 4; turn++) {
  const result = await client.chat.completions.create(
    {
      model: "openai/gpt-5.5",
      messages,
      tools,
      max_tokens: 256,
    },
    { headers: { "Idempotency-Key": crypto.randomUUID() } },
  );
  const message = result.choices[0].message;
  messages.push(message);
  if (!message.tool_calls?.length) {
    console.log(message.content);
    completed = true;
    break;
  }
  for (const call of message.tool_calls) {
    if (call.type !== "function" || call.function.name !== "utc_time")
      throw new Error("Tool is not allowed");
    const args = JSON.parse(call.function.arguments);
    if (!args || typeof args !== "object" || Array.isArray(args) || Object.keys(args).length)
      throw new Error("Invalid tool arguments");
    messages.push({
      role: "tool",
      tool_call_id: call.id,
      content: new Date().toISOString(),
    });
  }
}
if (!completed) throw new Error("Agent reached its four-turn limit");

Run it from the same terminal where you exported the credentials:

Terminal
node agent.mjs

The script prints the model’s answer, or reports that the four-turn limit was reached. If the model calls utc_time, the application adds the current UTC timestamp as a tool result and sends another request. Each model turn can incur usage; the limit bounds calls, not a fixed dollar amount.

Put Bounds Around the Loop

Limit turns, output tokens, tool time, and total spend. Validate every tool argument. Ask the user before consequential writes. Keep tool results associated with the original call IDs, and record a trace that can be inspected without exposing secrets.

Add a Voice Layer

For a voice agent, add microphone transport, speech recognition, speech synthesis, interruption handling, and turn detection around the same reasoning loop.