How to Create an AI Agent: A Step-by-Step Guide
An AI agent is a program that uses a language model to decide what to do next, not just to answer a single prompt. You give it a goal, a set of tools, and a loop that lets it observe results and act again. Creating one means wiring together four things: a model that can reason and call tools, a set of functions the model can invoke, a way to track state across steps, and a runtime that keeps looping until the task is done or a limit is hit.
This guide walks through that process concretely, with working code, so you can build a real agent rather than just a chatbot with extra prompts.
What Actually Makes Something an "Agent"
A plain LLM call takes input and returns output once. An agent adds a loop:
- Observe – get the current state (user request, tool results, prior steps)
- Reason – the model decides what to do next
- Act – call a tool, run code, query a database, hit an API
- Repeat – feed the result back in until the goal is met
The core difference from a chatbot is that the model's output can trigger real actions, and those actions produce new information the model reasons over. That's what "agent" means in practice — not a specific framework, but this observe-reason-act cycle.
Step 1: Define the Goal and Boundaries
Before writing code, decide:
- What the agent is allowed to do (read-only search vs. writing to a database vs. sending emails)
- How many steps it can take before stopping (a hard iteration cap avoids runaway loops and runaway bills)
- What "done" looks like — a specific output format, a confirmation message, or a tool call that signals completion
Skipping this step is the most common reason agent projects spiral into unpredictable behavior. Scope it tightly first, then expand.
Step 2: Choose Your Tools
Tools are just functions with a name, description, and schema for their inputs. The model doesn't execute them — it outputs a request to call one, your code runs the actual function, and you send the result back.
A minimal tool definition looks like this:
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}
Keep tool descriptions specific. Vague descriptions ("does stuff with data") lead to the model calling the wrong tool or passing malformed arguments. Write descriptions the way you'd explain the function to a new engineer.
Step 3: Build the Loop
Here's a working agent loop in JavaScript using a Claude-compatible Messages API. This example uses SubToAPI, which exposes Claude through a standard HTTPS API with streaming and tool use — useful if you already have Claude access and want an API key without separate infrastructure.
const tools = [
{
name: "get_weather",
description: "Get current weather for a given city",
input_schema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"]
}
}
];
async function callModel(messages) {
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-sonnet-4",
max_tokens: 1024,
messages,
tools
})
});
return res.json();
}
async function runAgent(userInput) {
let messages = [{ role: "user", content: userInput }];
for (let step = 0; step < 8; step++) {
const response = await callModel(messages);
const toolUse = response.content.find(b => b.type === "tool_use");
if (!toolUse) {
return response.content.find(b => b.type === "text")?.text;
}
const result = await executeLocalTool(toolUse.name, toolUse.input);
messages.push({ role: "assistant", content: response.content });
messages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify(result)
}]
});
}
return "Agent hit the step limit without finishing.";
}
async function executeLocalTool(name, input) {
if (name === "get_weather") {
return { city: input.city, tempC: 21, condition: "cloudy" };
}
return { error: "unknown tool" };
}
This is the entire skeleton. Everything else — memory, planning, multi-agent coordination — is built on top of this loop.
Step 4: Add Memory and State
For anything beyond a single session, you need to persist state:
- Short-term memory: keep the running
messagesarray so the model sees prior tool calls and results within one task - Long-term memory: store completed tasks, user preferences, or facts in a database and retrieve relevant entries before each run
- Working memory limits: long conversations eat context fast, especially with verbose tool results — summarize or truncate older turns once you exceed a token budget
Don't over-engineer this early. A simple array with a hard cap on message count solves most single-session agents.
Step 5: Handle Errors and Guardrails
Agents fail in specific, predictable ways:
- Infinite loops — always cap iterations
- Wrong tool arguments — validate inputs before executing, and return a clear error message back to the model so it can self-correct
- Hallucinated tool calls — reject calls to tools that don't exist in your registry instead of silently ignoring them
- Cost runaway — track token usage per run and set a budget ceiling; streaming responses (see
/docs/streaming) also let you stop generation early if something looks wrong
Step 6: Deploy It Behind an API
Once the loop works locally, wrap it in an HTTP endpoint so other services or a frontend can call it. If you're building on Claude, you'll need an API key, streaming support, and usage tracking — a signup at /signup gives you a sub_live_... key and a free trial to test this without committing to a plan upfront. Full request and response formats are documented at /docs/messages, and the quickstart at /docs/quickstart covers authentication end to end.
Common Mistakes to Avoid
- Giving the agent too many tools at once — start with 2-3, add more as you validate behavior
- No step limit — this is the single most common cause of runaway API costs
- Vague system prompts — be explicit about what the agent should and shouldn't do
- Ignoring tool_result formatting — the model needs structured, parseable output to reason well on the next step
questions
Do I need a framework to build an AI agent? No. A framework like LangChain or a custom orchestration library can speed up development, but the core pattern — loop, call model, execute tool, feed result back — is simple enough to build from scratch, and doing so once makes debugging frameworks much easier later.
What's the difference between an AI agent and a chatbot? A chatbot returns text in response to text. An agent can call functions, take real actions (search, write to a database, send a request), observe the outcome, and decide on a next step — it operates in a loop rather than a single request-response exchange.
How do I control API costs when running an agent? Set a hard cap on loop iterations, track token usage per run, and use streaming so you can cancel a response early if it's going off track. Reviewing usage metadata per request (available via /docs/tools) also helps you spot expensive tool-calling patterns before they scale.