← Blog

How to Implement an AI Agent: A Developer's Guide

2026-09-21 · 6 min read · SubToAPI Team

Implementing an AI agent means building a loop where a language model can reason about a task, decide which tools to call, execute those tools, and use the results to keep working until the task is done. It's not a single API call — it's a control flow around an LLM that gives it the ability to act, not just answer.

This guide walks through the actual pieces you need: the agent loop, tool definitions, state management, and error handling, with working code. It applies whether you're calling Claude, GPT, or another model, and whether you're hitting the provider's API directly or through a proxy like SubToAPI.

The Core Agent Loop

Every AI agent, no matter how sophisticated, is built around the same basic loop:

  1. Send the user's request plus available tools to the model
  2. The model responds with either a final answer or a tool call request
  3. If it's a tool call, execute the tool and send the result back
  4. Repeat until the model returns a final answer

That's it. Frameworks like LangChain, CrewAI, or AutoGen wrap this loop in abstractions, but you can build a functional agent in under 100 lines without any of them. Understanding the raw loop first makes debugging agent behavior far easier later.

async function runAgent(userMessage, tools, maxSteps = 8) {
  let messages = [{ role: "user", content: userMessage }];

  for (let step = 0; step < maxSteps; step++) {
    const response = await callModel(messages, tools);

    if (response.stop_reason !== "tool_use") {
      return response.content; // final answer
    }

    messages.push({ role: "assistant", content: response.content });

    const toolResults = await Promise.all(
      response.tool_calls.map(async (call) => ({
        tool_use_id: call.id,
        content: await executeTool(call.name, call.input),
      }))
    );

    messages.push({ role: "user", content: toolResults });
  }

  throw new Error("Agent exceeded max steps without finishing");
}

The maxSteps cap matters more than it looks — without it, a model stuck in a bad reasoning pattern will keep calling tools indefinitely and burn through your API budget.

Step 1: Define Tools Precisely

Tools are just functions the model can request, described with a name, a description, and a JSON schema for arguments. The quality of your tool descriptions directly affects how reliably the agent picks the right tool at the right time.

const tools = [
  {
    name: "search_orders",
    description: "Search customer orders by email or order ID. Returns order status, items, and total.",
    input_schema: {
      type: "object",
      properties: {
        query: { type: "string", description: "Email address or order ID" },
      },
      required: ["query"],
    },
  },
];

Two practical rules: keep tool names and descriptions specific (not "search" — "search_orders"), and never give the model two tools that do nearly the same thing. Overlapping tools cause it to pick the wrong one or alternate between them unpredictably. See /docs/tools for schema details if you're integrating against SubToAPI's Messages endpoint.

Step 2: Execute Tools Safely

The executeTool function is where most agent bugs actually live, not in the model's reasoning. It needs to validate inputs, catch failures, and return something the model can act on — including failures.

async function executeTool(name, input) {
  try {
    switch (name) {
      case "search_orders":
        return JSON.stringify(await db.orders.search(input.query));
      default:
        return JSON.stringify({ error: `Unknown tool: ${name}` });
    }
  } catch (err) {
    return JSON.stringify({ error: err.message });
  }
}

Never let a tool exception crash the loop. If the model gets a clear error message back, it can often self-correct — retry with different arguments, or fall back to telling the user what went wrong. A silent crash just kills the whole agent run.

Step 3: Manage State and Memory

For single-turn agent tasks, the message array in the loop above is your entire memory. For anything that spans multiple user sessions, you need to persist state outside the loop:

A common mistake is treating the LLM's context window as your only source of state. It works for a demo, but it's expensive and unreliable at scale — long histories increase token cost and can cause the model to lose track of earlier instructions.

Step 4: Add Guardrails

Production agents need limits beyond the max-step counter:

None of this is optional if the agent has access to real systems. An agent that can only read data is low risk; one that can write or delete needs explicit confirmation steps before executing.

Choosing How to Call the Model

You can call the model provider's API directly, but if you're already paying for a Claude subscription, you may not have API access separately — Claude.ai plans and the Anthropic API are billed independently. SubToAPI turns an existing Claude subscription into a standard HTTPS API with a sub_live_... key, so the agent loop above works unchanged against a familiar Messages-style endpoint:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Find order #4471"}],
    "tools": [...]
  }'

This gives you streaming, tool use, and usage metadata without managing separate API billing. Start with /docs/quickstart, check the request/response shape in /docs/messages, and see /docs/streaming if your agent needs to stream partial responses to a UI while tool calls happen in the background. Plans start at €9/month with a free trial at /signup, and team pricing is on /pricing.

Testing Your Agent

Before shipping, run the agent against a fixed set of test prompts covering: a happy path, a case where the required tool fails, a case where the user asks something no tool can handle, and a case designed to trigger a loop (ambiguous instructions). If it handles all four without crashing or looping forever, your core implementation is solid — everything after that is tuning prompts and tool descriptions.

questions

Do I need a framework like LangChain to implement an AI agent? No. The agent loop is straightforward enough to write directly, as shown above. Frameworks help with complex multi-agent orchestration, but for a single agent with a handful of tools, raw code is easier to debug and has fewer moving parts.

How many tools should an agent have access to? Keep it under 10–15 well-described tools per agent. Beyond that, models start confusing similar tools and calling the wrong one. If you need more capabilities, split into specialized sub-agents rather than one agent with dozens of tools.

What's the biggest cause of agent failures in production? Unbounded loops and unhandled tool errors. A missing max-step limit or a tool that throws instead of returning a structured error will cause runs that hang, cost far more than expected, or fail silently with no useful debugging trail.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →