How to Build a Production-Ready AI Agent with Python and OpenAI API

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:

  1. Language Model: The “brain” that understands and generates text (GPT-4)
  2. Tools/Functions: Capabilities the agent can use (search, calculate, API calls)
  3. 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:

  1. Simple question: “What’s the capital of France?”
  2. Use calculator: “Calculate 15 * 37 + 124”
  3. Get weather: “What’s the weather like in Tokyo?”
  4. Multi-step task: “Get the current time, then save it to a file called time.txt”
  5. 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

  1. Add more tools (database queries, API integrations, file operations)
  2. Implement vector database for long-term memory
  3. Add user authentication and session management
  4. Deploy as a REST API using FastAPI or Flask
  5. Create a web UI for easier interaction
  6. Implement agent-to-agent communication

Additional Resources

Leave a Comment