What Is an AI Agent
A chatbot replies and stops. An AI agent is a system that decides what to do next on its own: call a tool, wait for a result, revise its plan, and repeat the cycle until the task is complete.
The key distinction: agents operate in a loop, not in a single pass.
The Agent Loop
Each iteration has four steps:
Perception β Reasoning β Action β Observation
β β
βββββββββββββββββββββββββββββββββββββββββββ
| Step | What happens |
|------|-------------|
| Perception | The agent receives inputs: user request, previous tool results, memory contents |
| Reasoning | The LLM decides: answer in text, call a tool, or finish |
| Action | A tool runs (your code), an API is called, a file is written |
| Observation | The action result is added to context β the loop starts again |
How the LLM Makes Decisions
The model doesn't "think" in the intuitive sense. It generates the next token based on the entire context β system prompt, conversation history, tool descriptions, and previous results.
Three possible outcomes each iteration:
- Final answer β the model considers the task done.
- Tool call β the model generates a JSON request instead of text.
- Chain-of-thought β the model reasons aloud before deciding.
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "search_web",
"description": "Search the web for current information. Use when the user asks about recent events or facts you may not know.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
]
def run_agent(user_message: str) -> str:
messages = [{"role": "user", "content": user_message}]
for _ in range(10): # iteration limit β mandatory safety guard
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
return response.content[0].text
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "user", "content": tool_results})
return "Max iterations reached"
def execute_tool(name: str, input: dict) -> str:
if name == "search_web":
return f"[Search results for '{input['query']}': ...]"
return "Tool not found"
Agents vs Chatbots
| Feature | Chatbot | AI Agent |
|---------|---------|---------|
| Passes | One | Many (loop) |
| Tool access | No | Yes |
| Planning | No | Yes |
| Mid-task replanning | No | Yes |
| Autonomy | Low | High |
When the Loop Stops
The agent finishes when:
- The LLM returns
stop_reason: "end_turn" with no tool calls.
- The iteration limit is reached.
- A tool returns a stop signal.
Always set a maximum iteration count β an agent can loop forever if a tool consistently fails.