DEV Community

Serhii Kalyna
Serhii Kalyna

Posted on • Originally published at kalyna.pro

How to Build an AI Agent with Python: Step-by-Step Tutorial (2026)

An AI agent is a program that can reason, make decisions, and call external tools to complete a task — not just answer a single question. In this tutorial you’ll build a working AI agent in Python using the Claude API, tool use (function calling), and a simple agent loop. By the end you’ll have an agent that can search the web, do math, and chain multiple actions on its own.


Prerequisites


What Makes an AI Agent Different From a Chatbot

A regular chatbot takes a message and returns a response. An AI agent goes further: it decides what actions to take, executes them by calling tools (Python functions), observes the results, and keeps going until the task is done.

The three core components of any agent:

  • LLM brain — Claude decides what to do next
  • Tools — Python functions the agent can call (search, calculator, file ops, APIs)
  • Agent loop — runs until the task is complete or the model says it’s done

Step 1: Install the SDK

pip install anthropic
Enter fullscreen mode Exit fullscreen mode

Set your API key as an environment variable:

export ANTHROPIC_API_KEY="sk-ant-..."
Enter fullscreen mode Exit fullscreen mode

Step 2: Define Your Tools

Tools are regular Python functions. You describe them to Claude using a JSON schema — Claude reads the description and decides when to call them.

import anthropicimport mathclient = anthropic.Anthropic()def calculate(expression: str) -> str:    """Evaluate a math expression."""    try:        result = eval(expression, {"__builtins__": {}}, {"math": math})        return str(result)    except Exception as e:        return f"Error: {e}"def search_web(query: str) -> str:    """Return search results for a query."""    # Replace with SerpAPI, Brave Search, or Tavily    return f"Top result for '{query}': AI agents use LLMs to reason and act autonomously."TOOLS = [    {        "name": "calculate",        "description": "Evaluate a mathematical expression. Use for any arithmetic or math.",        "input_schema": {            "type": "object",            "properties": {                "expression": {                    "type": "string",                    "description": "A valid Python math expression, e.g. '2 ** 10' or 'math.sqrt(144)'",                }            },            "required": ["expression"],        },    },    {        "name": "search_web",        "description": "Search the web for up-to-date information on any topic.",        "input_schema": {            "type": "object",            "properties": {"query": {"type": "string", "description": "The search query"}},            "required": ["query"],        },    },]TOOL_MAP = {"calculate": calculate, "search_web": search_web}
Enter fullscreen mode Exit fullscreen mode

Step 3: Build the Agent Loop

The agent loop sends a message to Claude, checks whether it wants to call a tool, executes the tool, feeds the result back, and repeats — until Claude returns a final answer.

def run_agent(user_message: str, max_iterations: int = 10) -> str:    messages = [{"role": "user", "content": user_message}]    for i in range(max_iterations):        response = client.messages.create(            model="claude-opus-4-7",            max_tokens=4096,            tools=TOOLS,            messages=messages,        )        tool_uses = [b for b in response.content if b.type == "tool_use"]        if response.stop_reason == "end_turn" or not tool_uses:            for block in response.content:                if hasattr(block, "text"):                    return block.text            return "(no text response)"        # Add Claude's response to history        messages.append({"role": "assistant", "content": response.content})        # Execute each tool and collect results        tool_results = []        for tool_use in tool_uses:            fn = TOOL_MAP.get(tool_use.name)            result = fn(**tool_use.input) if fn else f"Unknown tool: {tool_use.name}"            tool_results.append({                "type": "tool_result",                "tool_use_id": tool_use.id,                "content": result,            })        # Feed results back to Claude        messages.append({"role": "user", "content": tool_results})    return "Max iterations reached."
Enter fullscreen mode Exit fullscreen mode

Step 4: Run Your Agent

if __name__ == "__main__":    answer = run_agent("What is 2 to the power of 16?")    print("Answer:", answer)    answer = run_agent(        "Search for what an AI agent is, then calculate how many seconds are in 7 days."    )    print("Answer:", answer)
Enter fullscreen mode Exit fullscreen mode

Sample output:

[iter 1] stop_reason=tool_use  → calling calculate(2 ** 16)  ← 65536[iter 2] stop_reason=end_turnAnswer: 2 to the power of 16 is 65,536.[iter 1] stop_reason=tool_use  → search_web + calculate[iter 2] stop_reason=end_turnAnswer: An AI agent is a program that uses LLMs to reason autonomously.There are 604,800 seconds in 7 days.
Enter fullscreen mode Exit fullscreen mode

Complete Agent — Full Code

Here’s the entire agent in one file you can copy and run:

import anthropicimport mathclient = anthropic.Anthropic()def calculate(expression: str) -> str:    try:        return str(eval(expression, {"__builtins__": {}}, {"math": math}))    except Exception as e:        return f"Error: {e}"def search_web(query: str) -> str:    # Replace with Tavily / Brave Search / SerpAPI    return f"Search result for '{query}': [connect your real API here]"TOOLS = [    {"name": "calculate",     "description": "Evaluate a math expression (Python syntax).",     "input_schema": {"type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"]}},    {"name": "search_web",     "description": "Search the web for current information.",     "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}},]TOOL_MAP = {"calculate": calculate, "search_web": search_web}def run_agent(user_message: str, max_iterations: int = 10) -> str:    messages = [{"role": "user", "content": user_message}]    for _ in range(max_iterations):        response = client.messages.create(            model="claude-opus-4-7", max_tokens=4096, tools=TOOLS, messages=messages        )        tool_uses = [b for b in response.content if b.type == "tool_use"]        if response.stop_reason == "end_turn" or not tool_uses:            return next((b.text for b in response.content if hasattr(b, "text")), "")        messages.append({"role": "assistant", "content": response.content})        results = [            {"type": "tool_result", "tool_use_id": tu.id,             "content": (TOOL_MAP[tu.name](**tu.input) if tu.name in TOOL_MAP else "Unknown tool")}            for tu in tool_uses        ]        messages.append({"role": "user", "content": results})    return "Max iterations reached."if __name__ == "__main__":    task = input("Task: ")    print("\nAgent:", run_agent(task))
Enter fullscreen mode Exit fullscreen mode

How to Extend Your Agent

Add Real Search

Replace the search_web stub with a real API. Good options: Tavily (built for AI agents, free tier), Brave Search API (1,000 free calls/month), or SerpAPI.

Add File Operations

def read_file(path: str) -> str:    with open(path) as f:        return f.read()def write_file(path: str, content: str) -> str:    with open(path, "w") as f:        f.write(content)    return f"Written {len(content)} chars to {path}"
Enter fullscreen mode Exit fullscreen mode

Add Memory

Pass the full messages list between sessions by saving it to a JSON file or SQLite database. The agent will remember previous interactions.

Multi-Agent Systems

One agent can call another as a tool. A planner agent breaks a task into subtasks; worker agents execute each one. This pattern scales to complex workflows and is the foundation of MCP.


Key Concepts Recap

  • Tool use — Claude decides when to call a function; you define what it does
  • Agent loop — keep calling the API until stop_reason == "end_turn" with no pending tool calls
  • Tool results — always return results as a tool_result block with the matching tool_use_id
  • Max iterations — always set a cap to prevent infinite loops
  • Model choiceclaude-opus-4-7 for complex reasoning; claude-haiku-4-5-20251001 for fast, cheap subtasks

What’s Next?

You’ve built a working AI agent from scratch. From here you can:

  • Connect a real search API (Tavily, Brave) for live information
  • Add persistent memory — store conversation history in SQLite between sessions
  • Wrap this agent in a Telegram bot or REST API
  • Read the Anthropic tool use docs for parallel tool calls and advanced patterns
  • Explore Model Context Protocol (MCP) — a standard for connecting agents to external services

Originally published at kalyna.pro

Top comments (0)