← Blog

How to Make an AI Agent That Actually Works

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

An AI agent is a program that uses a language model to decide what to do next, takes an action (like calling a function or an API), looks at the result, and repeats until it reaches a goal. Making one isn't about finding a magic framework — it's about wiring together four things correctly: a model, a set of tools, a memory of what's happened so far, and a loop that keeps everything moving.

This guide walks through that loop step by step, with working code, so you end up with an agent you can actually run and extend, not just a diagram.

The core loop

Every AI agent, regardless of how it's marketed, runs the same basic cycle:

  1. Observe — collect the current state: user input, tool outputs, conversation history.
  2. Think — send that state to the model and ask what to do next.
  3. Act — execute the action the model chose (call a tool, query a database, hit an API).
  4. Repeat — feed the result back in and go to step 1, until the task is done.

That's it. Everything else — planning, memory, multi-agent orchestration — is refinement on top of this loop.

while not done:
    state = observe()
    decision = model.decide(state)
    result = act(decision)
    state = update(state, result)
    done = check_completion(state)

Step 1: Pick a model and get a real API key

You need programmatic access to a model that supports structured tool calling, not just chat completions. If you're already paying for Claude but only have consumer access, you can't call it from code — this is exactly the gap SubToAPI fills: it turns your existing Claude subscription into a proper HTTPS API with sub_live_... keys, so you get standard request/response calls, streaming, and usage metadata without a separate enterprise contract. Sign up at /signup and grab a key from the dashboard.

A basic call looks like this:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "max_tokens": 512,
    "messages": [
      {"role": "user", "content": "Summarize this ticket: server returns 502 on checkout."}
    ]
  }'

Full request/response shape is documented at /docs/messages — this is the "think" step of the loop.

Step 2: Define tools the agent can actually use

An agent without tools is just a chatbot. Tools are functions the model can choose to call — search a database, send an email, run a calculation, hit an internal API. Define them with a clear name, description, and parameter schema so the model knows when and how to use them.

{
  "name": "get_order_status",
  "description": "Look up the current status of an order by ID",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string" }
    },
    "required": ["order_id"]
  }
}

When you send this alongside a user message, the model can respond with a request to call get_order_status instead of guessing an answer. Your code executes the real function, returns the result, and the loop continues. This pattern — tool definition, model decision, execution, result — is covered in detail at /docs/tools.

A minimal tool-calling agent in JavaScript:

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

  while (true) {
    const res = await fetch("https://api.subtoapi.app/v1/messages", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SUBTOAPI_KEY}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        model: "claude-3-5-sonnet",
        max_tokens: 1024,
        messages,
        tools: [orderStatusTool],
      }),
    });

    const data = await res.json();
    const toolCall = data.content.find((c) => c.type === "tool_use");

    if (!toolCall) return data.content; // final answer

    const result = await executeTool(toolCall.name, toolCall.input);
    messages.push({ role: "assistant", content: data.content });
    messages.push({
      role: "user",
      content: [{ type: "tool_result", tool_use_id: toolCall.id, content: result }],
    });
  }
}

This is the whole agent: a loop, a model call, a decision point, and a tool executor.

Step 3: Add memory and stopping conditions

Two things separate a working agent from one that loops forever or forgets context:

Step 4: Stream output for responsiveness

If your agent takes multiple seconds per step (model call + tool execution), stream the model's response so users see progress instead of a frozen screen. Streaming also lets you show intermediate reasoning or tool calls as they happen, which matters a lot for anything interactive. Setup details are at /docs/streaming.

Step 5: Move from prototype to production

A prototype agent that works in a notebook and a production agent that survives real traffic are different things. Before shipping:

SubToAPI's dashboard gives you usage metadata per key and team seats, which is useful once you have more than one agent or more than one person building against the same account — plans start at €9/month, see /pricing. If you're just getting the loop running for the first time, /docs/quickstart has the shortest path from zero to a working call.

Common mistakes to avoid

questions

Do I need a framework to make an AI agent? No. The core loop — observe, decide, act, repeat — is simple enough to write directly against an API. Frameworks add convenience for complex orchestration, but they're not required to get a working agent running.

What's the difference between a chatbot and an AI agent? A chatbot responds to messages. An agent takes actions — calling tools, APIs, or functions — based on the model's decisions, and continues iterating until a task is complete, not just until one reply is generated.

How do I control API costs while an agent is running? Cap loop iterations, limit tool result size before feeding it back to the model, and monitor token usage per request. A dashboard with usage metadata per API key makes this easier to catch before it becomes a large bill.

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 →