Intermediate

Building a Simple Agent

A minimal agent is just a loop: call Claude, check if it wants to use a tool, execute the tool, send the result back, repeat until done. This page shows a complete minimal implementation in Python using the Anthropic SDK.

Setup: SDK and Tool Definition

import anthropic
import json

client = anthropic.Anthropic()

# Define a simple tool
tools = [
    {
        "name": "get_stock_price",
        "description": "Get the current stock price for a ticker symbol. Returns the price as a float.",
        "input_schema": {
            "type": "object",
            "properties": {
                "ticker": {
                    "type": "string",
                    "description": "The stock ticker symbol, e.g. 'AAPL' or 'GOOGL'"
                }
            },
            "required": ["ticker"]
        }
    }
]

def get_stock_price(ticker: str) -> float:
    # In a real implementation, this would call a stock API
    prices = {"AAPL": 175.42, "GOOGL": 140.88, "MSFT": 378.91}
    return prices.get(ticker, None)

The Basic Agent Loop

User Message
Add to messages[]
Call Claude
API request
stop_reason?
tool_use or end_turn
Execute Tool
Append result
Return Answer
Loop complete

Simple agent loop — iterate until end_turn or max_iterations

def run_agent(user_message: str, max_iterations: int = 10) -> str:
    messages = [{"role": "user", "content": user_message}]

    for iteration in range(max_iterations):
        # Call Claude with the current messages and tools
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            tools=tools,
            messages=messages
        )

        # Add Claude's response to the messages array
        messages.append({"role": "assistant", "content": response.content})

        # If Claude is done (no tool calls), return the final text response
        if response.stop_reason == "end_turn":
            # Extract text from the final response
            for block in response.content:
                if hasattr(block, "text"):
                    return block.text
            return "Task completed."

        # If Claude wants to use tools, process the tool calls
        if response.stop_reason == "tool_use":
            tool_results = []

            for block in response.content:
                if block.type == "tool_use":
                    # Execute the requested tool
                    result = execute_tool(block.name, block.input)

                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result)
                    })

            # Send all tool results back to Claude
            messages.append({"role": "user", "content": tool_results})

    return "Max iterations reached."

def execute_tool(name: str, inputs: dict):
    if name == "get_stock_price":
        price = get_stock_price(inputs["ticker"])
        if price is None:
            return {"error": f"Ticker '{inputs['ticker']}' not found"}
        return {"ticker": inputs["ticker"], "price": price}
    return {"error": f"Unknown tool: {name}"}

When Claude Signals It Is Done

The stop_reason field tells you why Claude stopped generating:

  • end_turn — Claude finished its response naturally. The task is complete (or Claude needs more information from the user).
  • tool_use — Claude is requesting tool calls. Execute them and continue the loop.
  • max_tokens — The response was cut off. Increase max_tokens or handle this as an error.
  • stop_sequence — A configured stop sequence was reached.

Maximum Iterations: Preventing Infinite Loops

Always set a maximum iteration count. Without it, a bug in your tool implementation or an unexpected model behaviour could cause the loop to run indefinitely:

  • 10–15 iterations is appropriate for most simple agents
  • Complex research or coding agents may need 20–50 iterations
  • If max iterations is reached, return an error or partial result — do not silently truncate
  • Log the iteration count for each run — consistently high iteration counts indicate a problem with your tool descriptions or task scope

Running the Agent

# Example usage
result = run_agent("What's the current price of Apple and Microsoft stock? Which one is higher?")
print(result)
# Claude will call get_stock_price twice (AAPL and MSFT),
# then compare and answer the question.

Checklist: Do You Understand This?

  • The agent loop is a while/for loop in your code — Claude does not loop itself
  • Maintain a messages array; append Claude's response after each API call, then append tool results
  • Check stop_reason: end_turn = done; tool_use = call tools and continue
  • Always set a max iterations limit — no safeguard against infinite loops otherwise
  • Return descriptive errors from tool calls — Claude adapts based on what the error message says

Page built: 01 Jun 2026