What Even Is an Agent?
When I first heard the word "agent" thrown around in AI circles, I pictured something like a sci-fi robot that could just... do stuff. Autonomously. While I slept. The reality is a bit more grounded — but honestly still pretty magical once you build one yourself.
At its core, an AI agent is just a loop. The model looks at a task, decides whether it needs to use a tool, uses that tool, looks at the result, and decides what to do next. Repeat until done. That's it. The loop is what makes it an agent instead of a single prompt-response pair.
The three things every agent needs: tools (functions it can call), a result handler (logic that feeds tool output back into the model), and a stopping condition (so it doesn't run forever and eat your API credits). Let's build all three.
Step 1 — Define Your Tools
Tools are just regular functions. The trick is describing them to the model in a way it understands. With the Anthropic Claude API, you pass tools as a structured list alongside your messages. Here's a minimal example — a small calculator agent that can add numbers and look things up in a fake database.
# Define the tools the agent can use
tools = [
{
"name": "add_numbers",
"description": "Adds two numbers together. Use this when asked to sum values.",
"input_schema": {
"type": "object",
"properties": {
"a": {"type": "number", "description": "First number"},
"b": {"type": "number", "description": "Second number"}
},
"required": ["a", "b"]
}
},
{
"name": "lookup_item",
"description": "Looks up a product by name and returns its price.",
"input_schema": {
"type": "object",
"properties": {
"item_name": {"type": "string", "description": "Name of the product"}
},
"required": ["item_name"]
}
}
]Notice that each tool has a name, a plain-English description, and an input_schema that tells the model exactly what parameters to pass. The description is critical — Claude decides which tool to call based on it. Vague descriptions lead to wrong tool choices.
Write descriptions like you're training a new intern
The model reads your tool descriptions the same way a new hire reads documentation. Be specific about when to use each tool and what edge cases to watch for. "Adds two numbers" is fine. "Use this to sum prices before checkout" is better.
Step 2 — The Actual Tool Implementations
Tools are described to the model, but you execute them. The model tells you which tool to call and with what arguments — your code actually runs the function and returns the result. Here's the Python side:
# Fake product database
PRODUCTS = {
"notebook": 12.99,
"pen": 1.49,
"backpack": 49.95
}
# The real implementations
def add_numbers(a, b):
return a + b
def lookup_item(item_name):
price = PRODUCTS.get(item_name.lower())
if price is None:
return f"Item '{item_name}' not found."
return f"{item_name} costs ${price}"
# Dispatcher — routes tool calls to real functions
def execute_tool(name, inputs):
if name == "add_numbers":
return str(add_numbers(**inputs))
elif name == "lookup_item":
return lookup_item(**inputs)
return "Unknown tool"The dispatcher pattern is your best friend here. As you add more tools, all you do is add another elif branch. Clean, testable, easy to debug.
Step 3 — The Agent Loop
This is where it all comes together. The loop sends the conversation to Claude, checks if it wants to use a tool, executes the tool, feeds the result back, and repeats. When Claude stops asking for tools, it means it has an answer — that's your stopping condition.
import anthropic
client = anthropic.Anthropic()
def run_agent(user_message, max_iterations=10):
messages = [{"role": "user", "content": user_message}]
iteration = 0
while iteration < max_iterations:
iteration += 1
print(f"\n--- Iteration {iteration} ---")
# Call the model
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=messages
)
# Check stop reason
if response.stop_reason == "end_turn":
print("Agent finished.")
return response.content[0].text
# Handle tool use
if response.stop_reason == "tool_use":
# Add assistant response to history
messages.append({
"role": "assistant",
"content": response.content
})
# Execute each tool call and collect results
tool_results = []
for block in response.content:
if block.type == "tool_use":
print(f"Tool call: {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
})
# Feed results back into conversation
messages.append({
"role": "user",
"content": tool_results
})
return "Max iterations reached."Two stopping conditions are baked in here: Claude naturally finishing its turn (end_turn), and a hard cap on iterations. That second one is not optional — I learned this the hard way when a buggy tool kept returning errors and my agent happily looped forever retrying it.
Step 4 — Run It and Watch It Think
Here's the satisfying part. Let's give it a task that requires multiple tool calls:
# Run the agent
result = run_agent("What's the total cost of a notebook and a pen?")
print(f"\nFinal answer: {result}")
# Output:
--- Iteration 1 ---
Tool call: lookup_item({'item_name': 'notebook'})
Result: notebook costs $12.99
--- Iteration 2 ---
Tool call: lookup_item({'item_name': 'pen'})
Result: pen costs $1.49
--- Iteration 3 ---
Tool call: add_numbers({'a': 12.99, 'b': 1.49})
Result: 14.48
--- Iteration 4 ---
Agent finished.
Final answer: A notebook ($12.99) and a pen ($1.49) together cost $14.48.That's a real agent. It planned a multi-step path, called different tools in sequence, and synthesized the results into a coherent answer. You didn't tell it the order — it figured that out.
The Three Things That Will Trip You Up
After building a few of these, here's where I've personally burned time:
1. Forgetting to append the assistant message before tool results. The API requires the conversation history to include the assistant's tool-use block before you add the tool results. Skip it and you'll get a confusing validation error about message ordering. The fix is in the loop above — always push response.content to messages before you add the tool results.
2. Tools that can fail silently. If your tool returns an empty string or None, the model will try to reason about... nothing. Always return a meaningful error string like "Error: item not found" instead of None. Claude handles error messages in tool results gracefully — it'll either retry with different inputs or tell the user it couldn't complete the task.
3. No iteration cap. Seriously, always set one. Even well-behaved agents can get into retry loops when a tool fails unexpectedly. Start with 10 iterations for simple agents. Bump it up for complex multi-step tasks, but never remove it entirely.
Add logging before you add complexity
The print statements in the loop above aren't just for this tutorial — keep them in during development. Watching an agent reason step-by-step is how you catch bad tool descriptions and wrong dispatcher logic before they cause real problems.
Where to Take This Next
What you've built here is the skeleton every production agent is built on. The loop is the same whether you're building a customer support bot, a code review assistant, or an agent that browses the web. The only things that change are the tools and how complex you let the iteration get.
Some natural next steps from here: swap the fake database for a real API call, add a tool that reads files, or wire in a web search tool. Each new tool you add exponentially increases what the agent can accomplish — but keep your descriptions tight and your iteration cap in place.
One thing I'd encourage: resist the urge to immediately reach for a framework like LangChain or CrewAI. Understanding the raw loop first means you'll actually know what those frameworks are doing under the hood when something breaks. And something always breaks eventually.
The agent loop is one of those concepts that looks complicated until you build it once — and then you can't un-see it everywhere. Every AI assistant you've ever used is just this loop, with fancier tools and better descriptions. Now you know how to build one too.
More tutorials in this category, or explore the full field guide.