Back to Blog

How to Build an AI Agent from Scratch

A practical guide to building a production AI agent with an LLM, tools, and a reasoning loop. Working Python and TypeScript examples, plus the failure modes to plan for.

Tutorial · 2026-08-14

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:

  1. The model: any instruction-following LLM that can produce structured tool calls.
  2. The tools: functions with a name, a description, and a typed schema for their arguments.
  3. 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

References

See the full build-along recipe in the AI Cookbook: https://cookbook.4mlabs.io/recipes/internal-ai-os

Ship production AI in production with 4M Labs

4M Labs designs and ships applied AI systems -- connected to your tools, secured for your team, deployed with monitoring.

  • Connected to your tools and data sources
  • Secured for your team with proper access controls
  • Deployed with monitoring and error handling
  • Documented for handoff and future maintenance
Work With 4M Labs