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. do things 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! aren't really 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: autonomy and dynamic decision-making 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 like a GPS with a fixed route—if there's a roadblock, it can't adapt Workflows Agents are like having a local guide who knows all the shortcuts and can change plans on the fly Agents 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: Fetch real-time stock prices using the Yahoo Finance API Find company CEOs from stock data Identify ticker symbols from company names Ask for clarification when queries are ambiguous Fetch real-time stock prices using the Yahoo Finance API Fetch real-time stock prices Find company CEOs from stock data Find company CEOs Identify ticker symbols from company names Identify ticker symbols Ask for clarification when queries are ambiguous Ask for clarification What makes this a true agent is that it autonomously decides: true agent 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 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 Python 3.7 or higher An OpenAI API key (get one at platform.openai.com) 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 mkdir stock-info-agent cd stock-info-agent pip install openai yfinance python-dotenv Create a .env file in your project directory: .env OPENAI_API_KEY=your_api_key_here OPENAI_API_KEY=your_api_key_here {/* */} 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 = [] 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 = [] 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. conversation_history 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 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 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 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 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 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 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 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 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) ] 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) ] 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" }) 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" }) 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. while True 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. 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. The agent autonomously: Recognized "EV company from China" was ambiguous Asked which specific company Found the ticker symbol for BYD Retrieved the CEO information Fetched the current stock price Composed a complete answer Recognized "EV company from China" was ambiguous Asked which specific company Found the ticker symbol for BYD Retrieved the CEO information Fetched the current stock price 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 python stock_agent.py Try these example queries to see different behaviors: Simple query: Simple query: You: What's Apple's stock price? Agent: Apple's current stock price is $182.63 USD. You: What's Apple's stock price? Agent: Apple's current stock price is $182.63 USD. Ambiguous query requiring clarification: 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. 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. Complex multi-tool query: 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. 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. 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}%" } 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}%" } 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]] 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]] Best Practices for Building Agents Clear Tool Descriptions: Write descriptions as if explaining to a colleague Graceful Error Handling: Always handle API failures and missing data Conversation Context: Maintain history for natural interactions User Transparency: Show which tools are being executed Start Simple: Add complexity only when needed Clear Tool Descriptions: Write descriptions as if explaining to a colleague Clear Tool Descriptions Graceful Error Handling: Always handle API failures and missing data Graceful Error Handling Conversation Context: Maintain history for natural interactions Conversation Context User Transparency: Show which tools are being executed User Transparency Start Simple: Add complexity only when needed Start Simple For more helpful tips, check out Anthropic's guide. Anthropic's guide The Complete Code <details> <summary>Click to expand the full implementation</summary> 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() 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() </details> 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 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!