Building Your First AI Agent

Everything covered so far — LLMs, prompts, tools, memory, and the agent loop — comes together in this topic. A complete, working AI Agent will be built from scratch, step by step. By the end, there will be a real agent capable of taking a task, reasoning through it, calling tools, and delivering an answer.

The agent being built here is a Simple Research Assistant that can search the web and answer questions.

What the Agent Will Do

  • Accept a user question in plain English
  • Decide if it needs to search the web
  • Call a web search tool if needed
  • Use the search results to form an answer
  • Respond to the user with a clear, concise answer

Project Structure

first_agent/
├── .env                ← API keys
├── agent.py            ← Main agent logic
├── tools.py            ← Tool definitions and implementations
└── run.py              ← Entry point to run the agent

Step 1 — Define the Tools

Create tools.py with a simulated web search tool. In production, a real search API (like SerpAPI or Tavily) would be used here.

# tools.py

import json

def web_search(query: str) -> str:
    """
    Simulate a web search. In production, replace this with
    a call to SerpAPI, Tavily, or Google Custom Search API.
    """
    # Simulated results for demonstration
    mock_results = {
        "python programming language": {
            "title": "Python (programming language) - Wikipedia",
            "summary": "Python is a high-level, general-purpose programming language "
                       "created by Guido van Rossum in 1991. It is known for its simple "
                       "syntax, readability, and wide use in AI, web development, "
                       "data science, and automation."
        },
        "openai gpt-4": {
            "title": "GPT-4 - OpenAI",
            "summary": "GPT-4 is a large multimodal model released by OpenAI in March 2023. "
                       "It can accept image and text inputs and produce text outputs. "
                       "It is currently available via the OpenAI API."
        }
    }

    # Check if query matches any mock result
    for key in mock_results:
        if key in query.lower():
            result = mock_results[key]
            return json.dumps({
                "query": query,
                "result": result["summary"],
                "source": result["title"]
            })

    # Default response for unmatched queries
    return json.dumps({
        "query": query,
        "result": f"Search results for '{query}': Several relevant articles found. "
                  f"Key finding: This topic has extensive documentation and community "
                  f"support available online.",
        "source": "web"
    })


# Tool definitions for the OpenAI API
TOOL_DEFINITIONS = [
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": (
                "Search the web for information on any topic. "
                "Use this when the user asks a question that requires "
                "looking up current or factual information."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "The search query to look up"
                    }
                },
                "required": ["query"]
            }
        }
    }
]

# Tool router — maps tool names to actual functions
TOOL_MAP = {
    "web_search": web_search
}

Step 2 — Build the Agent Core

Create agent.py with the full agent loop:

# agent.py

import os
import json
from dotenv import load_dotenv
import openai
from tools import TOOL_DEFINITIONS, TOOL_MAP

load_dotenv()

client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

SYSTEM_PROMPT = """You are ResearchBot, a helpful research assistant.

Your job is to answer the user's questions accurately.

RULES:
- If the question requires factual or current information, use the web_search tool first.
- After getting search results, synthesise the information and give a clear answer.
- If the question is conversational or does not need research, answer directly.
- Always be concise and informative.
- If you cannot find relevant information, say so honestly."""


def run_agent(user_question: str, verbose: bool = True) -> str:
    """
    Run the agent loop for a given user question.
    Returns the final answer as a string.
    """
    # Initialise conversation
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user",   "content": user_question}
    ]

    if verbose:
        print(f"\n{'='*50}")
        print(f"User: {user_question}")
        print(f"{'='*50}")

    # Agent loop — maximum 5 iterations to prevent infinite loops
    max_steps = 5

    for step in range(max_steps):
        if verbose:
            print(f"\n[Step {step + 1}] Agent is thinking...")

        # Call the LLM
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=TOOL_DEFINITIONS,
            tool_choice="auto",    # Let the model decide when to use tools
            temperature=0.3,
            max_tokens=800
        )

        message = response.choices[0].message

        # Add the assistant's response to conversation history
        messages.append(message)

        # Check if the model wants to call a tool
        if message.tool_calls:
            for tool_call in message.tool_calls:
                tool_name = tool_call.function.name
                tool_args = json.loads(tool_call.function.arguments)

                if verbose:
                    print(f"[Step {step + 1}] Tool called: {tool_name}")
                    print(f"[Step {step + 1}] Arguments: {tool_args}")

                # Execute the tool
                if tool_name in TOOL_MAP:
                    tool_result = TOOL_MAP[tool_name](**tool_args)
                else:
                    tool_result = json.dumps({"error": f"Unknown tool: {tool_name}"})

                if verbose:
                    result_preview = tool_result[:150] + "..." if len(tool_result) > 150 else tool_result
                    print(f"[Step {step + 1}] Tool result: {result_preview}")

                # Add the tool result back to conversation
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": tool_result
                })

        else:
            # No tool call — the agent has a final answer
            final_answer = message.content

            if verbose:
                print(f"\n{'='*50}")
                print(f"Agent: {final_answer}")
                print(f"{'='*50}\n")

            return final_answer

    # If max steps reached without a final answer
    return "I was unable to complete the task within the allowed steps."

Step 3 — Create the Entry Point

Create run.py to test the agent:

# run.py

from agent import run_agent

if __name__ == "__main__":
    # Test 1: Question requiring web search
    run_agent("What is the Python programming language?")

    # Test 2: Conversational question (no search needed)
    run_agent("What is 15 multiplied by 24?")

    # Test 3: Another factual question
    run_agent("Tell me about GPT-4 by OpenAI.")

Step 4 — Run the Agent

python run.py

Expected Output for Test 1

==================================================
User: What is the Python programming language?
==================================================

[Step 1] Agent is thinking...
[Step 1] Tool called: web_search
[Step 1] Arguments: {'query': 'python programming language'}
[Step 1] Tool result: {"query": "python programming language", "result": "Python is a
high-level, general-purpose programming language created by Guido van Rossum in 1991...

[Step 2] Agent is thinking...

==================================================
Agent: Python is a high-level, general-purpose programming language developed by 
Guido van Rossum in 1991. It is widely used in AI and machine learning, web 
development, data science, and automation. Python is known for its clean syntax 
and readability, making it one of the most beginner-friendly languages in the world.
==================================================

Understanding What Happened

StepWhat the Agent Did
Step 1 — Receive inputRead the user's question about Python
Step 1 — ReasonDecided a web search was needed for factual info
Step 1 — ActCalled web_search("python programming language")
Step 2 — ObserveReceived search results
Step 2 — ReasonSynthesised the results into a clear answer
Step 2 — Final answerResponded to the user without calling another tool

Adding a Second Tool — Calculator

Let us add a calculator tool to handle maths questions:

# Add to tools.py

def calculate(expression: str) -> str:
    """Safely evaluate a mathematical expression."""
    try:
        # Use eval with restricted scope for safety
        result = eval(expression, {"__builtins__": {}}, {})
        return json.dumps({"expression": expression, "result": result})
    except Exception as e:
        return json.dumps({"error": f"Could not evaluate: {str(e)}"})


# Add to TOOL_DEFINITIONS list:
{
    "type": "function",
    "function": {
        "name": "calculate",
        "description": "Evaluate a mathematical expression. Use for arithmetic calculations.",
        "parameters": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "A Python math expression, e.g., '15 * 24' or '(100 - 20) * 0.18'"
                }
            },
            "required": ["expression"]
        }
    }
}

# Add to TOOL_MAP:
"calculate": calculate
# Test the new tool
run_agent("What is 18% GST on a bill of ₹12,500?")

# Agent calls: calculate("12500 * 0.18") → 2250
# Agent responds: "18% GST on ₹12,500 is ₹2,250. 
#                  The total bill including GST would be ₹14,750."

Connecting a Real Search API (Production Setup)

Replace the mock search with a real one using the Tavily API (a search API designed for LLM agents):

# Install: pip install tavily-python

from tavily import TavilyClient
import os

tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))

def web_search(query: str) -> str:
    """Real web search using Tavily API."""
    result = tavily.search(query=query, search_depth="basic", max_results=3)
    
    summaries = []
    for item in result.get("results", []):
        summaries.append({
            "title": item.get("title"),
            "snippet": item.get("content", "")[:300],
            "url": item.get("url")
        })
    
    return json.dumps({"query": query, "results": summaries})

Summary

The first complete AI Agent is now built and running. It follows the Observe → Think → Act loop, uses tool definitions and a tool router, manages conversation history across multiple steps, and delivers a final answer when the task is complete. Adding more tools is as simple as defining a new function and registering it in the tool definitions and tool map. This foundation will be expanded significantly in the upcoming topics.

Leave a Comment

Your email address will not be published. Required fields are marked *