Course HomeCitiesReserve Your Seat
Day 1 of 5 45 minutes

What Agents Are and Why They Matter

The difference between a chatbot and an agent. The agent loop. Your first working agent in ~50 lines of Python.

What you'll build today

A basic agent that runs a loop: calls Claude, receives a tool call request, executes the tool, feeds the result back, and continues until Claude has a final answer. It handles search and math. ~50 lines of Python.

1
The Core Concept

Chatbot vs Agent: the actual difference

A chatbot takes a message in, returns a message out. That's the entire lifecycle. One turn, one response, done.

An agent runs a loop. It can take multiple actions, observe the results of those actions, and decide what to do next based on those observations. The loop runs until the task is complete — or until it hits a stopping condition.

This distinction matters because most real-world tasks aren't "one message in, one answer out." They require:

  • Looking something up before answering
  • Doing multiple calculations and combining results
  • Checking whether an action succeeded before proceeding
  • Making decisions based on what's been discovered so far

Chatbots handle questions. Agents handle tasks.

2
The Agent Loop

Perceive → Reason → Act → Evaluate

User Task

PERCEIVE — receive input, gather context

REASON — Claude decides what to do next

ACT — call a tool, execute a function, take an action

EVALUATE — observe result, decide if task is done

[if not done: loop back to REASON]

Final Answer to User

The key to this loop is tool calling. Instead of generating text, Claude can output a structured "tool call" — specifying a function to run and the arguments to pass. Your code executes that function and returns the result. Claude uses the result in its next reasoning step.

This is the same pattern used by every serious AI agent system — from Claude's built-in computer use to custom enterprise pipelines. The architecture is simple. The leverage comes from what tools you give the agent access to.

3
The Code

Build your first agent

Install the SDK first:

Terminal
pip install anthropic
export ANTHROPIC_API_KEY=your_key_here

Here's the complete first agent:

Pythonagent_day1.py
import anthropic
import json
import math

client = anthropic.Anthropic()

# ── Tool definitions ──────────────────────────────
TOOLS = [
    {
        "name": "search",
        "description": "Search for information about a topic",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "Search query"}
            },
            "required": ["query"]
        }
    },
    {
        "name": "calculate",
        "description": "Evaluate a mathematical expression",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {"type": "string", "description": "Math expression to evaluate"}
            },
            "required": ["expression"]
        }
    }
]

# ── Tool implementations ───────────────────────────
def search(query: str) -> str:
    # Simulated search — replace with real API in production
    results = {
        "population of france": "France has a population of approximately 68 million (2024).",
        "gdp of germany": "Germany's GDP is approximately $4.4 trillion (2023).",
        "python release date": "Python was first released in 1991 by Guido van Rossum.",
    }
    for key, val in results.items():
        if any(word in query.lower() for word in key.split()):
            return val
    return f"No results found for '{query}'"

def calculate(expression: str) -> str:
    try:
        # Safe eval: only math operations
        allowed = {'__builtins__': {}, 'math': math}
        for name in dir(math):
            allowed[name] = getattr(math, name)
        result = eval(expression, allowed)
        return str(result)
    except Exception as e:
        return f"Error: {e}"

def execute_tool(tool_name: str, tool_input: dict) -> str:
    if tool_name == "search":
        return search(tool_input["query"])
    elif tool_name == "calculate":
        return calculate(tool_input["expression"])
    raise ValueError(f"Unknown tool: {tool_name}")

# ── The agent loop ─────────────────────────────────
def run_agent(task: str, max_steps: int = 10) -> str:
    messages = [{"role": "user", "content": task}]

    for step in range(max_steps):
        print(f"\n--- Step {step + 1} ---")

        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=1024,
            tools=TOOLS,
            messages=messages
        )

        # If no tool calls, Claude has a final answer
        if response.stop_reason == "end_turn":
            final = response.content[0].text
            print(f"Final answer: {final}")
            return final

        # Process tool calls
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                print(f"  Tool: {block.name}({block.input})")
                result = execute_tool(block.name, block.input)
                print(f"  Result: {result}")
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result
                })

        # Add assistant response + tool results to history
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": tool_results})

    return "Max steps reached without final answer."

# ── Test it ────────────────────────────────────────
if __name__ == "__main__":
    run_agent(
        "What is the population of France divided by 1000? "
        "Search for the population first, then calculate."
    )

Run it: python agent_day1.py. You should see Step 1 call the search tool, Step 2 call calculate with the result, and Step 3 produce the final answer. That three-step sequence is the agent loop in action.

4
How It Works

Walk through the code

Tool definitions

Each tool is a dict with a name, description, and input_schema. The schema is JSON Schema format — it tells Claude exactly what inputs a tool accepts. Claude uses the description to decide which tool to call. Write good descriptions.

The message loop

The loop maintains a messages list. Every exchange (user → Claude → tool result → Claude) gets appended. This gives Claude the full conversation history on each API call. That history is how Claude knows what it's already done and what comes next.

stop_reason

When Claude has no more tools to call, stop_reason is "end_turn" and the response contains text. When it wants to call a tool, stop_reason is "tool_use". The loop checks this to decide whether to continue or return the final answer.

Why max_steps? Agents can loop forever if the task isn't solvable or a bug creates an infinite loop. Always set a step limit. In production, you'll also want to set a token budget. We cover that on Day 5.

Day 1 Challenge

Complete before Day 2

  1. Run the agent and verify it calls both tools in sequence
  2. Add a third tool: get_current_time that returns the current datetime
  3. Give the agent this task: "What time is it, and what is the square root of 1764?"
  4. Add a step_count to the print output so you can see the loop clearly

Tomorrow: Tools. You'll build 5 real tools (web search, file reading, API calls, database queries) and give the agent the ability to solve complex multi-step problems.