Introduction
AI agents are transforming how businesses automate complex workflows. In this comprehensive tutorial, you’ll learn how to build a production-ready AI agent using Python and the OpenAI API. We’ll create an intelligent assistant that can understand context, use tools, and complete multi-step tasks autonomously.
What You’ll Build
By the end of this tutorial, you’ll have a functional AI agent that can:
- Process natural language commands
- Execute Python functions as tools
- Maintain conversation context
- Handle errors gracefully
- Integrate with external APIs
Prerequisites
- Python 3.8+ installed
- OpenAI API key (get one at platform.openai.com)
- Basic understanding of Python and APIs
- pip package manager
Step 1: Project Setup
Create a new directory and set up a virtual environment:
mkdir ai-agent-tutorial
cd ai-agent-tutorial
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install openai python-dotenv requests
Create a .env file for your API key:
OPENAI_API_KEY=your_api_key_here
Step 2: Understanding the Agent Architecture
A modern AI agent consists of three core components:
- Language Model: The “brain” that understands and generates text (GPT-4)
- Tools/Functions: Capabilities the agent can use (search, calculate, API calls)
- Orchestration Loop: The logic that decides when to think, act, or respond
Step 3: Building the Agent Class
Create a file called agent.py:
import openai
import json
import os
from dotenv import load_dotenv
from typing import List, Dict, Any, Callable
load_dotenv()
class AIAgent:
def __init__(self, model="gpt-4", temperature=0.7):
self.client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.model = model
self.temperature = temperature
self.messages = []
self.tools = {}
self.tool_schemas = []
def add_tool(self, name: str, description: str, parameters: Dict, func: Callable):
"""Register a tool that the agent can use"""
self.tools[name] = func
self.tool_schemas.append({
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": parameters
}
})
def add_message(self, role: str, content: str):
"""Add a message to the conversation history"""
self.messages.append({"role": role, "content": content})
def run(self, user_input: str, max_iterations: int = 10):
"""Run the agent with user input"""
self.add_message("user", user_input)
for i in range(max_iterations):
print(f"\nš Iteration {i+1}")
# Call the API
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
tools=self.tool_schemas if self.tool_schemas else None,
temperature=self.temperature
)
message = response.choices[0].message
# Check if the model wants to use a tool
if message.tool_calls:
print(f"š ļø Agent is using tools...")
# Add the assistant's message with tool calls
self.messages.append({
"role": "assistant",
"content": message.content,
"tool_calls": [tc.to_dict() for tc in message.tool_calls]
})
# Execute each tool call
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f" ā Calling {function_name} with args: {function_args}")
# Execute the function
if function_name in self.tools:
result = self.tools[function_name](**function_args)
# Add tool result to conversation
self.messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": function_name,
"content": str(result)
})
else:
print(f"ā Unknown tool: {function_name}")
# Continue the loop to let the model respond with tool results
continue
# If no tool calls, the model is done
else:
print(f"ā
Agent response: {message.content}")
self.messages.append({
"role": "assistant",
"content": message.content
})
return message.content
print("ā ļø Max iterations reached")
return None
Step 4: Creating Useful Tools
Let’s create some practical tools for our agent. Add this to a new file called tools.py:
import requests
from datetime import datetime
def get_weather(location: str) -> str:
"""Get current weather for a location"""
# Using a free weather API (no key required)
try:
# Note: In production, use OpenWeatherMap or similar with API key
return f"Weather data for {location}: Sunny, 22°C (mock data - integrate real API)"
except Exception as e:
return f"Error getting weather: {str(e)}"
def calculate(expression: str) -> str:
"""Safely evaluate a mathematical expression"""
try:
# Safe evaluation using eval (restricted to math operations)
allowed_chars = set("0123456789+-*/.() ")
if all(c in allowed_chars for c in expression):
result = eval(expression)
return f"Result: {result}"
else:
return "Error: Invalid characters in expression"
except Exception as e:
return f"Error calculating: {str(e)}"
def search_web(query: str) -> str:
"""Simulate web search (integrate real search API in production)"""
# In production, integrate Google Custom Search API or similar
return f"Search results for '{query}': [Mock data - integrate real search API]"
def get_current_time() -> str:
"""Get the current date and time"""
now = datetime.now()
return f"Current time: {now.strftime('%Y-%m-%d %H:%M:%S')}"
def save_to_file(filename: str, content: str) -> str:
"""Save content to a file"""
try:
with open(filename, 'w') as f:
f.write(content)
return f"Saved content to {filename}"
except Exception as e:
return f"Error saving file: {str(e)}"
Step 5: Putting It All Together
Create main.py to run your agent:
from agent import AIAgent
from tools import get_weather, calculate, search_web, get_current_time, save_to_file
def main():
# Initialize the agent
agent = AIAgent(model="gpt-4", temperature=0.7)
# Register tools
agent.add_tool(
name="get_weather",
description="Get current weather for a location",
parameters={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city or location to get weather for"
}
},
"required": ["location"]
},
func=get_weather
)
agent.add_tool(
name="calculate",
description="Calculate a mathematical expression",
parameters={
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The mathematical expression to evaluate"
}
},
"required": ["expression"]
},
func=calculate
)
agent.add_tool(
name="search_web",
description="Search the web for information",
parameters={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
}
},
"required": ["query"]
},
func=search_web
)
agent.add_tool(
name="get_current_time",
description="Get the current date and time",
parameters={
"type": "object",
"properties": {}
},
func=get_current_time
)
agent.add_tool(
name="save_to_file",
description="Save content to a file",
parameters={
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The name of the file to save to"
},
"content": {
"type": "string",
"description": "The content to save"
}
},
"required": ["filename", "content"]
},
func=save_to_file
)
# Run interactive loop
print("š¤ AI Agent is ready! Type 'quit' to exit.\n")
while True:
user_input = input("You: ")
if user_input.lower() in ['quit', 'exit', 'q']:
print("š Goodbye!")
break
if not user_input.strip():
continue
response = agent.run(user_input)
print(f"\nš¤ Agent: {response}\n")
if __name__ == "__main__":
main()
Step 6: Testing Your Agent
Run your agent and test it with various commands:
python main.py
Try these example commands:
- Simple question: “What’s the capital of France?”
- Use calculator: “Calculate 15 * 37 + 124”
- Get weather: “What’s the weather like in Tokyo?”
- Multi-step task: “Get the current time, then save it to a file called time.txt”
- Complex reasoning: “If I have 50 apples and give 12 to each of 3 friends, how many do I have left? Show your work.”
Step 7: Advanced Features
1. Adding Memory Persistence
Save and load conversation history:
import json
def save_memory(agent, filename="memory.json"):
"""Save agent's conversation history"""
with open(filename, 'w') as f:
json.dump(agent.messages, f, indent=2)
def load_memory(agent, filename="memory.json"):
"""Load agent's conversation history"""
try:
with open(filename, 'r') as f:
agent.messages = json.load(f)
print("š§ Memory loaded!")
except FileNotFoundError:
print("š§ No previous memory found.")
2. Streaming Responses
For a more interactive experience, add streaming:
def run_streaming(self, user_input: str):
"""Run agent with streaming response"""
self.add_message("user", user_input)
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
tools=self.tool_schemas if self.tool_schemas else None,
temperature=self.temperature,
stream=True
)
print("š¤ Agent: ", end="", flush=True)
full_response = ""
for chunk in response:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
print(content, end="", flush=True)
full_response += content
print() # New line
self.messages.append({"role": "assistant", "content": full_response})
return full_response
3. Error Handling & Retries
Make your agent more robust:
import time
from openai import RateLimitError, APIError
def run_with_retry(self, user_input: str, max_retries: int = 3):
"""Run agent with automatic retry on rate limits"""
for attempt in range(max_retries):
try:
return self.run(user_input)
except RateLimitError:
wait_time = 2 ** attempt
print(f"ā³ Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
except APIError as e:
print(f"ā API Error: {e}")
if attempt == max_retries - 1:
raise
time.sleep(2)
return None
Real-World Applications
Your AI agent can be extended for:
- Customer Support: Answer FAQs and resolve issues
- Data Analysis: Query databases and generate reports
- Code Assistant: Review code, suggest improvements
- Research Assistant: Summarize papers, gather information
- Workflow Automation: Chain multiple API calls and actions
Best Practices
1. Security
- Never hardcode API keys – use environment variables
- Validate and sanitize all user inputs
- Implement rate limiting for production use
- Audit tool permissions carefully
2. Cost Management
- Use GPT-3.5-turbo for simpler tasks
- Implement response caching
- Set max_tokens limits
- Monitor usage via OpenAI dashboard
3. Reliability
- Add comprehensive error handling
- Implement logging and monitoring
- Test edge cases thoroughly
- Have fallback responses ready
Conclusion
Congratulations! You’ve built a fully functional AI agent with tool-using capabilities. This foundation can be extended with more sophisticated tools, better memory management, and integration with production systems.
The future of AI agents is bright – they’re becoming increasingly capable of handling complex, multi-step tasks that previously required human intervention. By mastering these fundamentals, you’re well-positioned to build the next generation of intelligent automation.
Next Steps
- Add more tools (database queries, API integrations, file operations)
- Implement vector database for long-term memory
- Add user authentication and session management
- Deploy as a REST API using FastAPI or Flask
- Create a web UI for easier interaction
- Implement agent-to-agent communication
Additional Resources
- OpenAI API Documentation
- OpenAI Cookbook (GitHub)
- LangChain Documentation (for advanced agent patterns)