Building Autonomous Agents
Autonomous agents go beyond single tool calls. They plan multi-step tasks, execute them, handle failures, and adapt their approach — all without human intervention.
Agent Architecture
┌────────────────────────────────────────────┐
│ Autonomous Agent │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Planner │ │ Executor │ │
│ │ (Break task │→ │ (Run tools, │ │
│ │ into steps) │ │ handle results) │ │
│ └──────────────┘ └───────┬──────────┘ │
│ ↑ │ │
│ │ ┌────────▼─────────┐ │
│ │ │ Evaluator │ │
│ └──────────│ (Check results, │ │
│ │ adjust plan) │ │
│ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ Working Memory │ │
│ │ - Current plan & progress │ │
│ │ - Tool results & observations │ │
│ │ - Error history & recovery attempts │ │
│ └──────────────────────────────────────┘ │
└────────────────────────────────────────────┘Building a Research Agent
A practical agent that researches topics using web search:
python
from openai import OpenAI
import json
client = OpenAI()
class ResearchAgent:
def __init__(self):
self.tools = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for information on a topic",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "take_notes",
"description": "Save important findings for the final report",
"parameters": {
"type": "object",
"properties": {
"note": {"type": "string", "description": "The finding to save"},
"source": {"type": "string", "description": "Where this info came from"}
},
"required": ["note"]
}
}
},
{
"type": "function",
"function": {
"name": "write_report",
"description": "Write the final research report using collected notes",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string"},
"sections": {
"type": "array",
"items": {"type": "string"},
"description": "Section headings for the report"
}
},
"required": ["title"]
}
}
}
]
self.notes = []
self.messages = []
def web_search(self, query):
"""Simulate web search (use real API in production)."""
return f"Search results for '{query}': [Relevant information found]"
def take_notes(self, note, source=""):
self.notes.append({"note": note, "source": source})
return f"Note saved. Total notes: {len(self.notes)}"
def write_report(self, title, sections=None):
notes_text = "\n".join(
f"- {n['note']} (Source: {n['source']})" for n in self.notes
)
return f"Report '{title}' drafted with {len(self.notes)} findings."
def execute_tool(self, name, args):
tool_map = {
"web_search": self.web_search,
"take_notes": self.take_notes,
"write_report": self.write_report
}
return tool_map[name](**args)
def run(self, task, max_steps=10):
self.messages = [
{
"role": "system",
"content": """You are a thorough research agent. When given a topic:
1. Plan your research approach
2. Search for information from multiple angles
3. Take notes on key findings
4. Write a comprehensive report
Be systematic and thorough. Use at least 3 searches."""
},
{"role": "user", "content": task}
]
for step in range(max_steps):
response = client.chat.completions.create(
model="gpt-4o",
messages=self.messages,
tools=self.tools,
tool_choice="auto"
)
message = response.choices[0].message
if not message.tool_calls:
return message.content # Final answer
self.messages.append(message)
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
print(f" Step {step + 1}: {name}({args})")
result = self.execute_tool(name, args)
self.messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
return "Research incomplete — reached maximum steps."
# Usage
agent = ResearchAgent()
report = agent.run("Research the current state of quantum computing")
print(report)Building a Code Agent
An agent that writes, tests, and fixes code:
python
class CodeAgent:
def __init__(self):
self.tools = [
{
"type": "function",
"function": {
"name": "write_file",
"description": "Write content to a file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"}
},
"required": ["path", "content"]
}
}
},
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read the contents of a file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"}
},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "run_code",
"description": "Execute a Python script and return output",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"}
},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "run_tests",
"description": "Run pytest on a test file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"}
},
"required": ["path"]
}
}
}
]
def run(self, task):
messages = [
{
"role": "system",
"content": """You are a code agent. When given a coding task:
1. Write the implementation
2. Write tests
3. Run the tests
4. If tests fail, fix the code and retry
5. Report the final result
Always write tests. Always run them. Fix failures."""
},
{"role": "user", "content": task}
]
for step in range(15):
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=self.tools
)
message = response.choices[0].message
if not message.tool_calls:
return message.content
messages.append(message)
for tool_call in message.tool_calls:
result = self.execute_tool(
tool_call.function.name,
json.loads(tool_call.function.arguments)
)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
return "Task incomplete."Error Recovery
Robust agents handle failures gracefully:
python
def execute_with_retry(self, tool_name, args, max_retries=3):
for attempt in range(max_retries):
try:
result = self.execute_tool(tool_name, args)
return result
except Exception as e:
if attempt == max_retries - 1:
return {
"error": str(e),
"attempts": max_retries,
"suggestion": "Try a different approach"
}
# Wait before retry
import time
time.sleep(2 ** attempt)Agent Guardrails
Iteration Limits
python
MAX_ITERATIONS = 15
MAX_TOOL_CALLS = 50Budget Controls
python
class BudgetTracker:
def __init__(self, max_tokens=100000, max_cost=1.0):
self.total_tokens = 0
self.max_tokens = max_tokens
self.max_cost = max_cost
def check_budget(self, usage):
self.total_tokens += usage.total_tokens
if self.total_tokens > self.max_tokens:
raise BudgetExceededError("Token budget exceeded")Action Approval
python
SAFE_TOOLS = {"web_search", "read_file", "calculate"}
DANGEROUS_TOOLS = {"write_file", "send_email", "delete_file", "run_code"}
def should_approve(tool_name, args):
if tool_name in SAFE_TOOLS:
return True
print(f"Agent wants to: {tool_name}({args})")
return input("Allow? [y/n]: ").lower() == "y"Agent Patterns
1. Plan-Then-Execute
python
# Step 1: Generate a plan
plan = generate_plan(task)
# ["Search for X", "Analyze results", "Write summary"]
# Step 2: Execute each step
for step in plan:
result = execute_step(step)
if not result.success:
plan = replan(task, completed_steps, error=result.error)2. Reflection Pattern
python
# After completing the task, evaluate quality
reflection = llm_call(
f"You completed this task: {task}\n"
f"Your output: {output}\n"
f"Rate your output 1-5 and identify improvements."
)
if reflection.score < 4:
# Try again with feedback
improved = retry_with_feedback(task, output, reflection.feedback)3. Scratchpad Pattern
Maintain a running scratchpad of thoughts and observations:
python
scratchpad = []
scratchpad.append(f"Task: {task}")
scratchpad.append(f"Step 1: Searched for X, found Y")
scratchpad.append(f"Step 2: Analyzed Y, key insight: Z")
# Include scratchpad in every LLM call for full contextKey Takeaways
- Start with simple agents — 2-3 tools, clear task scope
- Always set limits — max iterations, budget, timeout
- Error recovery is essential — agents will encounter failures
- Human-in-the-loop for dangerous actions — require approval
- Evaluate agent performance — track success rate, efficiency, cost
- Use existing frameworks when possible — LangChain, CrewAI, etc.
Next, we'll explore multi-agent systems where specialized agents collaborate on complex tasks.