Building Your First Agentic AI System
A practical guide to building an autonomous AI agent that can use tools and accomplish complex tasks.
Building Your First Agentic AI System
In this tutorial, you'll build an autonomous AI agent that can plan tasks, use tools, and accomplish goals with minimal human intervention.
What We're Building
An agentic AI system that can:
- Accept high-level goals from users
- Break goals into subtasks
- Use tools (web search, calculator, file operations)
- Track progress and adapt to failures
- Provide clear status updates
Architecture Overview
User Goal → Agent Loop → Tool Execution → Result
↓ ↓
Planning Memory
Setup
pip install openai langchain python-dotenv
Create a .env file:
OPENAI_API_KEY=your_api_key_here
Core Components
1. The Agent Class
import os
from typing import List, Dict, Any
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
class AgenticAI:
def __init__(self):
self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.conversation_history = []
self.tools = self._register_tools()
def _register_tools(self) -> Dict[str, callable]:
"""Register available tools the agent can use"""
return {
"calculator": self.calculator,
"web_search": self.web_search,
"write_file": self.write_file,
"read_file": self.read_file
}
def calculator(self, expression: str) -> str:
"""Evaluate mathematical expressions"""
try:
result = eval(expression)
return f"Result: {result}"
except Exception as e:
return f"Error: {str(e)}"
def web_search(self, query: str) -> str:
"""Simulate web search (replace with actual API)"""
return f"Search results for: {query}"
def write_file(self, filename: str, content: str) -> str:
"""Write content to a file"""
try:
with open(filename, 'w') as f:
f.write(content)
return f"Successfully wrote to {filename}"
except Exception as e:
return f"Error writing file: {str(e)}"
def read_file(self, filename: str) -> str:
"""Read content from a file"""
try:
with open(filename, 'r') as f:
return f.read()
except Exception as e:
return f"Error reading file: {str(e)}"
2. The Planning System
def plan_task(self, goal: str) -> List[str]:
"""Break down a goal into subtasks"""
prompt = f"""
Given the goal: "{goal}"
Break this down into specific, actionable subtasks.
Each subtask should be concrete and achievable.
Available tools: {', '.join(self.tools.keys())}
Return a numbered list of subtasks.
"""
response = self.client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content.split('\n')
3. The Execution Loop
def execute_task(self, task: str) -> str:
"""Execute a single task using available tools"""
prompt = f"""
Task: {task}
Available tools:
- calculator(expression): Evaluate math
- web_search(query): Search the web
- write_file(filename, content): Write to file
- read_file(filename): Read from file
Decide which tool to use and provide the parameters.
Format: TOOL_NAME(param1, param2, ...)
If no tool is needed, respond with: NO_TOOL_NEEDED
"""
response = self.client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "You are a task execution assistant."},
{"role": "user", "content": prompt}
]
)
action = response.choices[0].message.content.strip()
# Parse and execute tool call
if action == "NO_TOOL_NEEDED":
return "Task completed without tools"
# Simple parsing (in production, use structured outputs)
if "(" in action and ")" in action:
tool_name = action[:action.index("(")]
params = action[action.index("(")+1:action.index(")")].split(",")
params = [p.strip().strip("'\"") for p in params]
if tool_name in self.tools:
return self.tools[tool_name](*params)
return f"Could not execute: {action}"
4. The Main Agent Loop
def run(self, goal: str, max_iterations: int = 10):
"""Main agent loop"""
print(f"🎯 Goal: {goal}\n")
# Step 1: Plan
print("📋 Planning...")
subtasks = self.plan_task(goal)
print("\nSubtasks:")
for task in subtasks:
print(f" {task}")
# Step 2: Execute
print("\n⚡ Executing...\n")
for i, task in enumerate(subtasks, 1):
if not task.strip() or task.strip().startswith("#"):
continue
print(f"[{i}] {task}")
result = self.execute_task(task)
print(f" ✓ {result}\n")
# Add to memory
self.conversation_history.append({
"task": task,
"result": result
})
print("✅ Goal completed!")
Using the Agent
# Create agent instance
agent = AgenticAI()
# Run with a goal
agent.run("Calculate the compound interest on $10,000 at 5% for 10 years and save the result to a file called results.txt")
Example Output
🎯 Goal: Calculate the compound interest on $10,000 at 5% for 10 years and save the result to a file called results.txt
📋 Planning...
Subtasks:
1. Calculate compound interest using the formula A = P(1 + r)^t
2. Format the result clearly
3. Write the result to results.txt
⚡ Executing...
[1] Calculate compound interest using the formula A = P(1 + r)^t
✓ Result: 16288.946267774416
[2] Format the result clearly
✓ NO_TOOL_NEEDED
[3] Write the result to results.txt
✓ Successfully wrote to results.txt
✅ Goal completed!
Advanced Features
Memory System
Add long-term memory:
class AgentMemory:
def __init__(self):
self.short_term = [] # Recent tasks
self.long_term = {} # Learned patterns
def remember(self, task: str, result: str):
self.short_term.append({"task": task, "result": result})
# Keep only last 10
if len(self.short_term) > 10:
self.short_term.pop(0)
def recall_similar(self, task: str) -> List[Dict]:
# In production, use embeddings for semantic similarity
return [item for item in self.short_term
if any(word in item["task"] for word in task.split())]
Error Recovery
Add retry logic:
def execute_with_retry(self, task: str, max_retries: int = 3) -> str:
for attempt in range(max_retries):
try:
result = self.execute_task(task)
if "error" not in result.lower():
return result
print(f" Retry {attempt + 1}/{max_retries}")
except Exception as e:
if attempt == max_retries - 1:
return f"Failed after {max_retries} attempts: {str(e)}"
return result
Progress Tracking
Add status updates:
def run_with_progress(self, goal: str):
total_tasks = len(self.plan_task(goal))
for i, task in enumerate(subtasks, 1):
progress = (i / total_tasks) * 100
print(f"Progress: {progress:.0f}% [{i}/{total_tasks}]")
# ... execute task
Best Practices
- Clear tool definitions: Make tool purposes obvious
- Structured outputs: Use JSON for tool calls in production
- Error handling: Always handle tool failures gracefully
- Safety limits: Set max iterations to prevent infinite loops
- Logging: Track all tool calls for debugging
- Human oversight: Allow human approval for sensitive operations
Common Challenges
Challenge 1: Hallucinated Tool Calls
Solution: Use structured outputs and validate tool parameters
Challenge 2: Infinite Loops
Solution: Implement max iteration limits and loop detection
Challenge 3: Context Window Limits
Solution: Summarize old conversations, keep only relevant history
Challenge 4: Tool Failures
Solution: Implement retry logic and fallback strategies
Production Considerations
For real-world deployment:
- Use structured outputs: OpenAI's function calling API
- Implement proper auth: Secure API keys and credentials
- Add rate limiting: Prevent excessive API calls
- Monitor costs: Track token usage
- Test extensively: Many edge cases in agentic systems
- Add safety rails: Prevent dangerous operations
Next Steps
Enhance your agent with:
- Reinforcement learning: Learn from successes/failures
- Multi-agent systems: Coordinate multiple specialized agents
- Chain-of-thought prompting: Improve reasoning quality
- External knowledge bases: RAG for domain-specific knowledge
Conclusion
You've built a functional agentic AI system! While this is a simplified version, it demonstrates the core concepts:
- Goal decomposition
- Tool use
- Autonomous execution
- Memory and state management
As you build more complex agents, focus on reliability, safety, and clear communication of agent actions.
Happy building!