AI Agents for Beginners: Building Your First AI Agent
Slaknoah

Slaknoah @slaknoah

About: CEO CTO at Nexaloom LTD | AI Powered Booking Assistant for Beauty and Wellness industries. . I have a passion for learning new technologies and I am always looking for new challenges.

Location:
Birmingham, UK
Joined:
Dec 5, 2019

AI Agents for Beginners: Building Your First AI Agent

Publish Date: Jun 7
1 0

AI agents are everywhere right now, and for good reason. They're transforming how we interact with software, moving beyond simple question-answer systems to tools that can actually do things for us.

From customer support bots that can process refunds to coding assistants that can fix bugs across multiple files, agents are becoming the new interface between humans and complex systems.

But here's the thing: most "agents" you see aren't really agents at all. They're just chatbots following a script. Today, we're going to build a real agent—one that can think, decide, and act on its own. And don't worry, we'll keep it simple so even beginners can easily follow along!

Let's dive in! But first,

What Makes a True AI Agent?

What do we mean by a "real agent"?

The key distinction between a true agent and a simple automation lies in autonomy and dynamic decision-making. Let's illustrate with an analogy:

  • Workflows are like a GPS with a fixed route—if there's a roadblock, it can't adapt
  • Agents are like having a local guide who knows all the shortcuts and can change plans on the fly

Workflows are great for simple, well-defined tasks, but they often can't handle the complexity of very dynamic queries, unlike agents.

What we'll build

With the aid of the OpenAI SDK, we'll build a simple stock information agent that can answer questions about stocks and companies. It will be able to:

  1. Fetch real-time stock prices using the Yahoo Finance API
  2. Find company CEOs from stock data
  3. Identify ticker symbols from company names
  4. Ask for clarification when queries are ambiguous

What makes this a true agent is that it autonomously decides:

  • Which tool to use for each query
  • When to chain multiple tools together
  • When to ask the user for more information
  • How to handle errors and retry with different approaches

Prerequisites

Here's what you'll need:

  • Python 3.7 or higher
  • An OpenAI API key (get one at platform.openai.com)
  • Basic Python knowledge

Create a project directory and install the required packages:

mkdir stock-info-agent
cd stock-info-agent
pip install openai yfinance python-dotenv
Enter fullscreen mode Exit fullscreen mode

Create a .env file in your project directory:

OPENAI_API_KEY=your_api_key_here
Enter fullscreen mode Exit fullscreen mode

{/* */}

Building the Agent: A Step-by-Step Walkthrough

Let's build this agent from the ground up, understanding each component along the way.

1. Setting Up the Agent Class

First, we create our agent class with OpenAI integration:

import json
from typing import Optional, Dict, Any, List
from openai import OpenAI
import yfinance as yf
from dotenv import load_dotenv
import os

load_dotenv()

class StockInfoAgent:
    def __init__(self):
        self.client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
        self.conversation_history = []
Enter fullscreen mode Exit fullscreen mode

The conversation_history is crucial – it allows our agent to maintain context across multiple interactions, understanding, for example, that "their CEO" refers to the company discussed earlier.

2. Creating the Tools

Our agent needs tools to interact with the real world. Let's create them:

Stock Price Tool

def get_stock_price(self, ticker_symbol: str) -> Optional[str]:
    """Fetches the current stock price for the given ticker_symbol."""
    try:
        stock = yf.Ticker(ticker_symbol.upper())
        info = stock.info
        current_price = info.get('currentPrice') or info.get('regularMarketPrice')

        if current_price:
            return f"{current_price:.2f} USD"
        return None
    except Exception as e:
        print(f"Error fetching stock price: {e}")
        return None
Enter fullscreen mode Exit fullscreen mode

This tool uses Yahoo Finance to fetch real-time stock prices. Notice the error handling—robust agents must handle failures gracefully.

CEO Finder Tool

def get_company_ceo(self, ticker_symbol: str) -> Optional[str]:
    """Fetches the name of the CEO for the company associated with the ticker_symbol."""
    try:
        stock = yf.Ticker(ticker_symbol.upper())
        info = stock.info

        # Look for CEO in various possible fields
        ceo = None
        for field in ['companyOfficers', 'officers']:
            if field in info:
                officers = info[field]
                if isinstance(officers, list):
                    for officer in officers:
                        if isinstance(officer, dict):
                            title = officer.get('title', '').lower()
                            if 'ceo' in title or 'chief executive' in title:
                                ceo = officer.get('name')
                                break
        return ceo
    except Exception as e:
        print(f"Error fetching CEO info: {e}")
        return None
Enter fullscreen mode Exit fullscreen mode

This tool searches through different data structures to find the CEO, as Yahoo Finance doesn't have a standardized format.

Ticker Symbol Finder Tool

This tool is crucial for handling natural language queries about companies:

def find_ticker_symbol(self, company_name: str) -> Optional[str]:
    """Tries to identify the stock ticker symbol for a given company_name."""
    try:
        # Use yfinance Lookup to search for the company
        lookup = yf.Lookup(company_name)

        stock_results = lookup.get_stock(count=5)

        if not stock_results.empty:
            return stock_results.index[0]

        # If no stocks found, try all instruments
        all_results = lookup.get_all(count=5)

        if not all_results.empty:
            return all_results.index[0]

    except Exception as e:
        print(f"Error searching for ticker: {e}")

    return None
Enter fullscreen mode Exit fullscreen mode

This tool allows users to refer to companies by name ("Apple", "Tesla", "that EV company") instead of needing to know ticker symbols.

The Clarification Tool

This is what makes our agent truly interactive, allowing it to ask the user for clarification when needed.

def ask_user_for_clarification(self, question_to_user: str) -> str:
    """Poses the question_to_user to the actual user and returns their typed response."""
    print(f"\nAgent needs clarification: {question_to_user}")
    response = input("Your response: ")
    return response
Enter fullscreen mode Exit fullscreen mode

3. Defining Tools for OpenAI

The agent needs to know what tools are available and how to use them. We define them in OpenAI's function calling format:

def create_tool_definitions(self) -> List[Dict[str, Any]]:
    """Creates OpenAI function calling definitions for the tools."""
    return [
        {
            "type": "function",
            "function": {
                "name": "get_stock_price",
                "description": "Fetches the current stock price for the given ticker symbol",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "ticker_symbol": {
                            "type": "string",
                            "description": "The stock ticker symbol (e.g., 'AAPL', 'MSFT')"
                        }
                    },
                    "required": ["ticker_symbol"]
                }
            }
        },
        # ... (other tool definitions)
    ]
Enter fullscreen mode Exit fullscreen mode

These definitions are like instruction manuals for the AI—they tell it what each tool does and what parameters it needs.

4. The Brain: Processing User Queries

Here's where the magic happens—the agent's decision-making loop:

def process_user_query(self, user_query: str) -> str:
    """Processes a user query using the OpenAI API with function calling."""
    self.conversation_history.append({"role": "user", "content": user_query})

    system_prompt = """You are a helpful stock information assistant. You have access to tools that can:
                    1. Get current stock prices
                    2. Find company CEOs
                    3. Find ticker symbols for company names
                    4. Ask users for clarification when needed

                    Use these tools to help answer user questions about stocks and companies. 
                    If information is ambiguous, ask for clarification."""

    while True:
        messages = [
            {"role": "system", "content": system_prompt},
            *self.conversation_history
        ]

        # Call OpenAI API with function calling
        response = self.client.chat.completions.create(
            model="gpt-4-turbo-preview",
            messages=messages,
            tools=self.create_tool_definitions(),
            tool_choice="auto"  # Let the model decide which tool to use
        )

        response_message = response.choices[0].message

        # If no tool calls, we're done
        if not response_message.tool_calls:
            self.conversation_history.append({"role": "assistant", "content": response_message.content})
            return response_message.content

        # Execute the tool the agent chose
        tool_call = response_message.tool_calls[0]
        function_name = tool_call.function.name
        function_args = json.loads(tool_call.function.arguments)

        print(f"\nExecuting tool: {function_name} with args: {function_args}")

        # Execute the tool
        result = self.execute_tool(function_name, function_args)

        # Add everything to conversation history
        self.conversation_history.append({
            "role": "assistant",
            "content": None,
            "tool_calls": [{
                "id": tool_call.id,
                "type": "function",
                "function": {
                    "name": function_name,
                    "arguments": json.dumps(function_args)
                }
            }]
        })

        self.conversation_history.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": function_name,
            "content": str(result) if result is not None else "No result found"
        })
Enter fullscreen mode Exit fullscreen mode

The key insight here is the while True loop—the agent keeps thinking and acting until it has a complete answer. It might use one tool, or five, or ask for clarification multiple times. This is true autonomy.

See Our Agent In Action

Here's a real conversation that demonstrates our agent's capabilities very well:

You: Who is the CEO of the EV company from China and what is its stock price?

Executing tool: ask_user_for_clarification with args: {'question_to_user': 'Are you referring to NIO, XPeng, or another Chinese EV company?'}
Agent needs clarification: Are you referring to NIO, XPeng, or another Chinese EV company?

Your response: BYD

Executing tool: find_ticker_symbol with args: {'company_name': 'BYD'}
Executing tool: get_company_ceo with args: {'ticker_symbol': 'BYDDF'}
Executing tool: get_stock_price with args: {'ticker_symbol': 'BYDDF'}

Agent: The CEO of BYD, the Chinese EV company, is Mr. Chuan-Fu Wang, and its current stock price is $59.50 USD.
Enter fullscreen mode Exit fullscreen mode

The agent autonomously:

  1. Recognized "EV company from China" was ambiguous
  2. Asked which specific company
  3. Found the ticker symbol for BYD
  4. Retrieved the CEO information
  5. Fetched the current stock price
  6. Composed a complete answer

How to Use the Agent

Running the agent is simple, navigate to the project directory and run the following command:

python stock_agent.py
Enter fullscreen mode Exit fullscreen mode

Try these example queries to see different behaviors:

Simple query:

You: What's Apple's stock price?
Agent: Apple's current stock price is $182.63 USD.
Enter fullscreen mode Exit fullscreen mode

Ambiguous query requiring clarification:

You: Who runs that big EV company?
Agent: Are you asking about the CEO of Tesla?
You: Yes
Agent: The CEO of Tesla is Mr. Elon R. Musk.
Enter fullscreen mode Exit fullscreen mode

Complex multi-tool query:

You: Compare the stock prices of Microsoft and Apple
Agent: Microsoft (MSFT) is currently trading at $415.26 USD, while Apple (AAPL) is trading at $182.63 USD.
Enter fullscreen mode Exit fullscreen mode

Extending Your Agent

The modular design makes it easy to add new capabilities:

Add Market Analysis

def get_price_change(self, ticker_symbol: str, period: str = "1d") -> Dict[str, Any]:
    """Get price change over a period"""
    stock = yf.Ticker(ticker_symbol)
    hist = stock.history(period=period)
    if len(hist) >= 2:
        start_price = hist['Close'].iloc[0]
        end_price = hist['Close'].iloc[-1]
        change = end_price - start_price
        percent_change = (change / start_price) * 100
        return {
            "change": f"${change:.2f}",
            "percent": f"{percent_change:.2f}%"
        }
Enter fullscreen mode Exit fullscreen mode

Add News Integration

def get_company_news(self, ticker_symbol: str) -> List[Dict[str, str]]:
    """Get recent news about the company"""
    stock = yf.Ticker(ticker_symbol)
    news = stock.news
    return [{"title": item['title'], "link": item['link']} for item in news[:5]]
Enter fullscreen mode Exit fullscreen mode

Best Practices for Building Agents

  1. Clear Tool Descriptions: Write descriptions as if explaining to a colleague
  2. Graceful Error Handling: Always handle API failures and missing data
  3. Conversation Context: Maintain history for natural interactions
  4. User Transparency: Show which tools are being executed
  5. Start Simple: Add complexity only when needed

For more helpful tips, check out Anthropic's guide.

The Complete Code

Click to expand the full implementation

import json
from typing import Optional, Dict, Any, List
from openai import OpenAI
import yfinance as yf
from dotenv import load_dotenv
import os

# Load environment variables
load_dotenv()

class StockInfoAgent:
    def __init__(self):
        self.client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
        self.conversation_history = []

    def get_stock_price(self, ticker_symbol: str) -> Optional[str]:
        """Fetches the current stock price for the given ticker_symbol."""
        try:
            stock = yf.Ticker(ticker_symbol.upper())
            info = stock.info
            current_price = info.get('currentPrice') or info.get('regularMarketPrice')

            if current_price:
                return f"{current_price:.2f} USD"
            return None
        except Exception as e:
            print(f"Error fetching stock price: {e}")
            return None

    def get_company_ceo(self, ticker_symbol: str) -> Optional[str]:
        """Fetches the name of the CEO for the company associated with the ticker_symbol."""
        try:
            stock = yf.Ticker(ticker_symbol.upper())
            info = stock.info

            # Look for CEO in various possible fields
            ceo = None
            for field in ['companyOfficers', 'officers']:
                if field in info:
                    officers = info[field]
                    if isinstance(officers, list):
                        for officer in officers:
                            if isinstance(officer, dict):
                                title = officer.get('title', '').lower()
                                if 'ceo' in title or 'chief executive' in title:
                                    ceo = officer.get('name')
                                    break

            # Fallback to general company info
            if not ceo and 'longBusinessSummary' in info:
                ceo = None  

            return ceo
        except Exception as e:
            print(f"Error fetching CEO info: {e}")
            return None

    def find_ticker_symbol(self, company_name: str) -> Optional[str]:
        """Tries to identify the stock ticker symbol for a given company_name."""
        try:
            # Use yfinance Lookup to search for the company
            lookup = yf.Lookup(company_name)

            stock_results = lookup.get_stock(count=5)

            if not stock_results.empty:
                return stock_results.index[0]

            # If no stocks found, try all instruments
            all_results = lookup.get_all(count=5)

            if not all_results.empty:
                return all_results.index[0]

        except Exception as e:
            print(f"Error searching for ticker: {e}")

        return None

    def ask_user_for_clarification(self, question_to_user: str) -> str:
        """Poses the question_to_user to the actual user and returns their typed response."""
        print(f"\nAgent needs clarification: {question_to_user}")
        response = input("Your response: ")
        return response

    def create_tool_definitions(self) -> List[Dict[str, Any]]:
        """Creates OpenAI function calling definitions for the tools."""
        return [
            {
                "type": "function",
                "function": {
                    "name": "get_stock_price",
                    "description": "Fetches the current stock price for the given ticker symbol",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "ticker_symbol": {
                                "type": "string",
                                "description": "The stock ticker symbol (e.g., 'AAPL', 'MSFT')"
                            }
                        },
                        "required": ["ticker_symbol"]
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "get_company_ceo",
                    "description": "Fetches the name of the CEO for the company associated with the ticker symbol",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "ticker_symbol": {
                                "type": "string",
                                "description": "The stock ticker symbol"
                            }
                        },
                        "required": ["ticker_symbol"]
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "find_ticker_symbol",
                    "description": "Tries to identify the stock ticker symbol for a given company name",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "company_name": {
                                "type": "string",
                                "description": "The name of the company"
                            }
                        },
                        "required": ["company_name"]
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "ask_user_for_clarification",
                    "description": "Poses a question to the user and returns their response",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "question_to_user": {
                                "type": "string",
                                "description": "The question to ask the user"
                            }
                        },
                        "required": ["question_to_user"]
                    }
                }
            }
        ]

    def execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
        """Executes the specified tool with given arguments."""
        if tool_name == "get_stock_price":
            return self.get_stock_price(arguments["ticker_symbol"])
        elif tool_name == "get_company_ceo":
            return self.get_company_ceo(arguments["ticker_symbol"])
        elif tool_name == "find_ticker_symbol":
            return self.find_ticker_symbol(arguments["company_name"])
        elif tool_name == "ask_user_for_clarification":
            return self.ask_user_for_clarification(arguments["question_to_user"])
        else:
            return None

    def process_user_query(self, user_query: str) -> str:
        """Processes a user query using the OpenAI API with function calling."""
        # Add user message to conversation history
        self.conversation_history.append({"role": "user", "content": user_query})

        system_prompt = """You are a helpful stock information assistant. You have access to tools that can:
                        1. Get current stock prices
                        2. Find company CEOs
                        3. Find ticker symbols for company names
                        4. Ask users for clarification when needed

                        Use these tools to help answer user questions about stocks and companies. If information is ambiguous, ask for clarification."""

        while True:
            messages = [
                {"role": "system", "content": system_prompt},
                *self.conversation_history
            ]

            # Call OpenAI API with function calling
            response = self.client.chat.completions.create(
                model="gpt-4-turbo-preview",
                messages=messages,
                tools=self.create_tool_definitions(),
                tool_choice="auto"
            )

            response_message = response.choices[0].message

            # If no tool calls, we're done
            if not response_message.tool_calls:
                self.conversation_history.append({"role": "assistant", "content": response_message.content})
                return response_message.content

            # Execute the first tool call
            tool_call = response_message.tool_calls[0]
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)

            print(f"\nExecuting tool: {function_name} with args: {function_args}")

            # Execute the tool
            result = self.execute_tool(function_name, function_args)

            # Add the assistant's message with tool calls to history
            self.conversation_history.append({
                "role": "assistant",
                "content": None,
                "tool_calls": [{
                    "id": tool_call.id,
                    "type": "function",
                    "function": {
                        "name": function_name,
                        "arguments": json.dumps(function_args)
                    }
                }]
            })

            # Add tool result to history
            self.conversation_history.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": str(result) if result is not None else "No result found"
            })

    def chat(self):
        """Interactive chat loop."""
        print("Stock Information Agent")
        print("Ask me about stock prices, company CEOs, or any stock-related questions!")
        print("Type 'quit' to exit.\n")

        while True:
            user_input = input("You: ")

            if user_input.lower() in ['quit', 'exit', 'bye']:
                print("Goodbye!")
                break

            try:
                response = self.process_user_query(user_input)
                print(f"\nAgent: {response}\n")
            except Exception as e:
                print(f"\nError: {e}\n")

if __name__ == "__main__":
    agent = StockInfoAgent()
    agent.chat()
Enter fullscreen mode Exit fullscreen mode

Conclusion

Congratulations! You've built a true AI agent that can think, decide, and act autonomously. This isn't just a chatbot following a script—it's an intelligent system that can handle ambiguous queries, ask for clarification, and chain multiple tools together to solve complex problems.

The key takeaways:

  • True agents make their own decisions about how to solve problems
  • Simple, composable patterns beat complex frameworks
  • Start with basic tools and add complexity only when needed
  • Conversation memory and error handling are crucial

With this foundation, you can build agents for any domain, from financial analysis to customer support to personal assistants. The possibilities are endless when you give AI the ability to think and act autonomously.

Best of luck, and we can't wait to see what you build!

Comments 0 total

    Add comment