An AI agent is not a chatbot. A chatbot waits for a prompt and answers it. An agent is given a goal and uses an LLM, tools, and memory to take a sequence of actions until that goal is met. This guide walks through building one from scratch.
TL;DR
An AI agent is an LLM wrapped in a loop: think, choose a tool, execute it, observe the result, and repeat until the goal is reached. The two pieces that matter most are the reasoning loop and the tool boundary. Get those right and the rest is configuration.
What is an AI agent?
The most influential formulation is ReAct, from Yao et al. (2022), which stands for "reasoning and acting". A ReAct agent alternates between generating a reasoning step and issuing an action (a tool call), feeding each observation back into the next step.
The loop looks like this:
while not done:
thought = model.think(goal, history)
action = model.choose_action(thought) # a tool name + arguments
observation = run_tool(action)
history.append(thought, action, observation)
That loop is the whole agent. The model is just the component that decides, each turn, what to do next.
How it works
There are three moving parts:
- The model: any instruction-following LLM that can produce structured tool calls.
- The tools: functions with a name, a description, and a typed schema for their arguments.
- The runtime: the loop that executes tool calls, enforces a stop condition, and keeps the context bounded.
Tool calling is provided natively by the major model APIs. With Anthropic Claude you define tools as part of the request; with OpenAI you pass a functions array. In both cases the model returns a structured request such as {"name": "search_web", "arguments": {"query": "..."}} rather than free text, and your runtime is responsible for running it and returning the result.
A minimal example
Here is a compact TypeScript agent using Anthropic's tool-use API. It searches a small local "knowledge base" and loops until it has an answer:
const tools = [
{
name: "lookup",
description: "Search the company knowledge base",
input_schema: { type: "object", properties: { query: { type: "string" } } },
},
];
async function runAgent(goal: string) {
const messages = [{ role: "user", content: goal }];
for (let i = 0; i < 10; i++) {
const res = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
tools,
messages,
});
const toolUse = res.content.find((b) => b.type === "tool_use");
if (!toolUse) return res.content[0].text; // model is done
messages.push({ role: "assistant", content: res.content });
messages.push({
role: "user",
content: [{ type: "tool_result", tool_use_id: toolUse.id, content: runTool(toolUse) }],
});
}
return "stopped after max iterations";
}
When to use an agent
Use a plain LLM call when one inference answers the question. Reach for an agent when the task requires multiple steps, external data, or actions with side effects, for example researching a lead across several sources, or triaging a support ticket by looking up an order and updating a CRM.
Best practices
- Give every tool a precise description. The model selects tools by reading those descriptions; a vague description produces the wrong tool.
- Cap the number of iterations and stop cleanly. A loop with no exit condition is the most common production failure.
- Keep secrets out of prompts. Tools run with your credentials, not the model's.
- Log every tool call. When an agent misbehaves, the audit trail is how you diagnose it.
- Require human approval for destructive actions (writes, sends, deletes).
References
- Anthropic, Building effective agents: https://www.anthropic.com/engineering/building-effective-agents
- Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models: https://arxiv.org/abs/2210.03629
- Anthropic tool use documentation: https://docs.anthropic.com/en/docs/build-with-claude/tool-use/overview
See the full build-along recipe in the AI Cookbook: https://cookbook.4mlabs.io/recipes/internal-ai-os