← Blog

How to Build an AI Agent with Claude: A Practical Guide

2026-09-25 · 5 min read · SubToAPI Team

Building an AI agent with Claude means giving the model a loop: it receives a task, decides whether to call a tool, executes that tool, reads the result, and repeats until the task is done. This is different from a simple chatbot, which just takes a prompt and returns a reply. An agent needs state, tool definitions, and a control loop that keeps calling Claude until it produces a final answer instead of another tool request.

This guide walks through the actual architecture: how to structure the loop, define tools, manage context, and handle errors, with runnable code. It applies whether you're calling Anthropic's API directly or going through a proxy like SubToAPI.

The Core Agent Loop

At minimum, an agent needs four things:

  1. A system prompt that defines the agent's role and constraints
  2. A set of tool definitions (functions the model can call)
  3. A loop that sends messages, checks for tool calls, executes them, and sends results back
  4. A stopping condition — either the model returns a final text answer, or you hit a max iteration count

Here's a minimal loop in JavaScript:

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

  for (let step = 0; step < maxSteps; step++) {
    const response = await fetch("https://api.anthropic.com/v1/messages", {
      method: "POST",
      headers: {
        "x-api-key": process.env.ANTHROPIC_API_KEY,
        "anthropic-version": "2023-06-01",
        "content-type": "application/json",
      },
      body: JSON.stringify({
        model: "claude-sonnet-4-20250514",
        max_tokens: 1024,
        system: "You are a research agent. Use tools to gather facts before answering.",
        messages,
        tools,
      }),
    });

    const data = await response.json();
    messages.push({ role: "assistant", content: data.content });

    const toolUse = data.content.find((block) => block.type === "tool_use");
    if (!toolUse) {
      return data.content.find((block) => block.type === "text")?.text;
    }

    const result = await toolHandlers[toolUse.name](toolUse.input);
    messages.push({
      role: "user",
      content: [
        { type: "tool_result", tool_use_id: toolUse.id, content: JSON.stringify(result) },
      ],
    });
  }

  return "Max steps reached without a final answer.";
}

This is the whole pattern. Everything else — better error handling, parallel tool calls, memory, guardrails — builds on top of this loop.

Defining Tools Correctly

Claude decides which tool to call based on the tool's name, description, and JSON schema. Vague descriptions produce vague tool selection. Be explicit about what the tool does and when it should be used:

const tools = [
  {
    name: "search_docs",
    description:
      "Search internal documentation for a given query. Use this when the user asks about product features, pricing, or setup instructions. Do not use for general knowledge questions.",
    input_schema: {
      type: "object",
      properties: {
        query: { type: "string", description: "The search query" },
        limit: { type: "integer", description: "Max results to return", default: 5 },
      },
      required: ["query"],
    },
  },
];

A common mistake is giving the agent too many overlapping tools. If two tools could plausibly handle the same request, Claude will sometimes pick the wrong one or bounce between them. Keep tool boundaries clear, and if you need many capabilities, group them into a smaller number of well-scoped tools rather than one tool per API endpoint.

Managing Context and Memory

Agents that run multi-step tasks accumulate a lot of message history — tool calls, tool results, intermediate reasoning. Two things matter here:

A practical pattern: after every few tool calls, have the agent (or a cheaper model) summarize progress so far and replace the raw tool results with the summary. This keeps token usage predictable on long-running agents.

Error Handling and Guardrails

Real agents fail in predictable ways: a tool throws an error, the model calls a tool with malformed input, or the loop never terminates because the model keeps requesting more information. Handle these explicitly:

Choosing a Model for Agent Work

Tool selection and multi-step reasoning benefit from a stronger model. Haiku is fast and cheap but can struggle with complex tool chains; Sonnet is the usual default for agents that need to reason across several steps; Opus is worth it for agents making high-stakes decisions where mistakes are costly. Many production agents mix models: Sonnet or Opus for planning and tool selection, Haiku for cheap sub-tasks like summarization.

Deployment Considerations

Once the agent logic works locally, you need a stable way to call the API in production: key management, usage tracking per user or per agent, and streaming for long-running responses so the frontend isn't waiting on a single blocking call. If you're building this for a team or a product with multiple users, SubToAPI gives you an HTTPS endpoint with application API keys, streaming, and usage metadata per key, so you can run agents for different customers or team members without building that infrastructure yourself. See the quickstart for setup and the tool use docs for the exact request format.

FAQ

Do I need a framework like LangChain to build a Claude agent?

No. The core loop is maybe 40 lines of code, as shown above. Frameworks add value for complex multi-agent orchestration, but for a single agent with a handful of tools, a plain loop is easier to debug and modify.

How do I stop an agent from looping forever?

Set a hard maximum on iterations (typically 5–15 depending on task complexity) and optionally a token budget. If the loop hits the limit without a final answer, return the best partial result along with a clear message that the task wasn't completed.

Can an agent call multiple tools at once?

Yes — Claude can return multiple tool_use blocks in a single response. Execute them in parallel where they don't depend on each other's output, then send all results back in the same tool_result batch before the next model call.

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 →