LLM Agents Academy
Module 1: Foundations of LLM Agents

Your First Agent

Build a simple agent from scratch using raw API calls

~15 min readIncludes code

Overview

You have learned what agents are, why they matter, and how the observe-think-act loop drives every agentic system. Now it is time to build one.

In this lesson, you will construct a working agent from scratch using raw API calls. No frameworks, no abstractions, just Python and the Anthropic SDK. By the end, you will have a calculator agent that takes natural language math questions, reasons about which operations to perform, and returns the final answer.

Why start from scratch? Because every framework you will encounter in Module 8 and beyond is built on top of exactly these primitives. Understanding them now means you will never be confused by what a framework does under the hood.

What We Are Building

Our agent will take a natural language question like "What is 25 multiplied by 4, then add 10?" and figure out the steps to solve it. The agent has access to four arithmetic tools and uses the LLM to decide which tool to call, in what order, and when the task is done.

graph TD
  A["User asks a math question"] --> B["Send question + tools to LLM"]
  B --> C{"LLM responds"}
  C -->|"tool_use"| D["Extract tool name + arguments"]
  D --> E["Execute the tool function"]
  E --> F["Send tool result back to LLM"]
  F --> C
  C -->|"end_turn"| G["Return final answer to user"]
  style A fill:#ebf8ff,stroke:#3182ce
  style G fill:#c6f6d5,stroke:#38a169
  subgraph AgentLoop["The Agent Loop"]
      B
      C
      D
      E
      F
  end
  style AgentLoop fill:#f7fafc,stroke:#a0aec0

This is the observe-think-act cycle from lesson 04 made concrete. The LLM observes the question and any tool results, thinks about what to do next, and acts by either calling a tool or returning a final answer.

Step 1: Define the Tools

Tools are plain Python functions. Each one does a single arithmetic operation and returns the result as a string. We also create a list of tool definitions that describe each tool to the LLM using JSON Schema. These definitions are how the model knows what tools are available and what arguments they accept.

tools.py

Notice the functions return JSON strings rather than raw numbers. This is a common pattern: tool results are sent back to the LLM as text, so returning structured JSON makes it easy for the model to parse the output.

Step 2: Create Tool Definitions for the API

The Anthropic API needs a structured description of each tool so the model knows when and how to use them. Each definition includes a name, a description that tells the model what the tool does, and an input_schema that specifies the required parameters.

tool_definitions.py

Good descriptions are critical. The LLM reads these descriptions to decide which tool to call and when. Vague descriptions lead to wrong tool choices. Notice how each description explains not just what the tool does, but when to use it.

Step 3: Set Up the LLM Client

Now we initialize the Anthropic client. The SDK reads your API key from the ANTHROPIC_API_KEY environment variable automatically.

client.py

Setting up your API key: Before running this code, set your API key in your terminal with export ANTHROPIC_API_KEY="your-key-here". You can get a key from console.anthropic.com. Never hardcode API keys in your source code.

Step 4: Implement the Agent Loop

This is the core of the agent. The loop sends the user's question to the LLM along with the tool definitions, then checks what the model wants to do:

  • If the model returns a tool_use block, we execute the requested tool and send the result back.
  • If the model returns end_turn, it has finished reasoning and we extract the final answer.

This loop continues until the model decides it has enough information to answer the question.

agent_loop.py

Let's walk through the key parts of this loop:

  • System prompt tells the model it should use the tools rather than trying to compute answers itself. This is important because LLMs can do basic arithmetic, but we want to demonstrate the tool-use pattern.
  • response.stop_reason tells us what the model wants to do next. A value of "tool_use" means it wants to call a tool. A value of "end_turn" means it is done.
  • response.content contains a list of content blocks. When the model calls a tool, you will find blocks with type == "tool_use" that include the name of the tool and the input arguments.
  • tool_use_id is critical. When we send results back, each result must include the tool_use_id from the corresponding tool_use block so the model can match results to requests.
  • Messages grow over time. Each iteration appends the assistant's response and our tool results. The full conversation history is sent with every API call, giving the model the context it needs to reason about multi-step problems.

Step 5: Run the Agent

Let's put it all together and test with a multi-step math question.

run_agent.py

When you run this, you will see output like:

output

Notice how the agent made two trips through the loop. First it called multiply, got back 100, then called add with 100 and 10 to get 110. The model decided the order of operations, chose the right tools, and chained the results together -- all from a natural language question.

The Complete Agent

Here is the full working code in a single file that you can copy and run.

calculator_agent.py

What Makes This an Agent?

Compare what we just built to a simple API call:

  • A single API call sends a prompt and gets a response. One turn, done.
  • An agent runs a loop. It sends a prompt, gets a response, potentially acts on it, sends the results back, and repeats. The model drives the control flow by deciding when to call tools and when to stop.

The three properties from lesson 02 are all present here:

  1. Autonomy -- the model decides which tools to call and in what order, without us specifying the steps.
  2. Tool use -- the model interacts with external functions to perform computations.
  3. Goal-directed behavior -- the model works toward answering the user's question, chaining tool calls until it has enough information.

What Comes Next

This hand-rolled loop is exactly what agent frameworks automate for you. In Module 8, you will see how tools like LangGraph provide the same loop with built-in features like state management, error handling, retries, and streaming. But they all reduce to this same pattern: send a message, check if the model wants to call a tool, execute it, send results back, repeat.

Understanding the raw loop first means you will always be able to debug a framework when something goes wrong, extend it when it does not do what you need, or build your own when no framework fits.

Summary

You built a working agent from scratch using the Anthropic Python SDK. The key components are:

  • Tool definitions that describe capabilities to the LLM using JSON Schema
  • An LLM client that sends messages and tool definitions to the API
  • An agent loop that cycles until the model's stop_reason is end_turn, executing tool calls and feeding results back along the way
  • Tool results that must include the tool_use_id to match each result with its corresponding request

Every agent, no matter how sophisticated, builds on this foundation. The difference between this calculator and a production agent is the complexity of the tools and the robustness of the loop, not the underlying pattern.