Agentic Step 3: Building a Research Agent

Build an agent that can search the web for information.

What You'll Learn

  • Multiple tools in one agent
  • The Wikipedia API
  • Tool dispatch (routing to the right function)
  • Handling multiple tool calls in one response

Step 1: Add a Wikipedia Search Tool

Add to agent.py:

import requests


def search_wikipedia(query: str) -> str:
    """Search Wikipedia and return a summary."""
    try:
        url = "https://en.wikipedia.org/api/rest_v1/page/summary/" + requests.utils.quote(query)
        response = requests.get(url, timeout=10)

        if response.status_code == 200:
            data = response.json()
            return f"Title: {data['title']}\nSummary: {data['extract'][:500]}"
        elif response.status_code == 404:
            return f"No Wikipedia page found for '{query}'"
        else:
            return f"Wikipedia error: {response.status_code}"
    except Exception as e:
        return f"Error searching Wikipedia: {str(e)}"

Step 2: Add the Tool Schema

tools = [
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Evaluate a mathematical expression",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Math expression like '2 + 2' or '3 * (4 + 5)'"
                    }
                },
                "required": ["expression"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_wikipedia",
            "description": "Search Wikipedia for a topic and get a summary",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "The topic to search for"
                    }
                },
                "required": ["query"]
            }
        }
    }
]

Step 3: Update the Agent Loop with Tool Dispatch

def run_tool(tool_call):
    """Execute the right function based on tool name."""
    args = json.loads(tool_call.function.arguments)

    if tool_call.function.name == "calculate":
        return calculate(args["expression"])
    elif tool_call.function.name == "search_wikipedia":
        return search_wikipedia(args["query"])
    else:
        return f"Unknown tool: {tool_call.function.name}"


def run_agent(user_message: str) -> str:
    messages = [
        {"role": "system", "content": "You are a research assistant with access to a calculator and Wikipedia."},
        {"role": "user", "content": user_message},
    ]

    max_turns = 5  # Prevent infinite loops

    for _ in range(max_turns):
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            tools=tools,
            tool_choice="auto",
        )

        message = response.choices[0].message

        if not message.tool_calls:
            return message.content

        messages.append(message)

        for tool_call in message.tool_calls:
            print(f"  → Calling: {tool_call.function.name}({tool_call.function.arguments})")
            result = run_tool(tool_call)
            print(f"  → Result: {result[:100]}...")
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result,
            })

    return "Agent stopped after maximum turns."

Key changes: - Tool dispatch — run_tool() routes to the correct function - Max turns — prevents infinite loops if the AI keeps calling tools - Debug printing — shows what the AI is doing

Step 4: Test the Tools and Dispatch

Test the Wikipedia Tool

Add to advanced/agentic/tests/test_tools.py:

from agent import search_wikipedia


def test_search_wikipedia_returns_summary():
    result = search_wikipedia("Python (programming language)")
    assert "Python" in result
    assert len(result) > 50


def test_search_wikipedia_handles_not_found():
    result = search_wikipedia("XyzzyNotARealTopic12345")
    assert "No Wikipedia page found" in result

Test the Tool Dispatch

Create advanced/agentic/tests/test_agent.py:

import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from agent import run_tool


class FakeToolCall:
    def __init__(self, name, args):
        self.function = FakeFunction(name, args)
        self.id = "test_123"


class FakeFunction:
    def __init__(self, name, args):
        self.name = name
        self.arguments = args


def test_run_tool_dispatches_calculate():
    tool = FakeToolCall("calculate", '{"expression": "10 * 5"}')
    result = run_tool(tool)
    assert result == "50"


def test_run_tool_unknown():
    tool = FakeToolCall("nonexistent", "{}")
    result = run_tool(tool)
    assert "Unknown tool" in result

Run all tests:

cd aiproject
python -m pytest advanced/agentic/tests/ -v

Step 5: Test the Research Agent

if __name__ == "__main__":
    queries = [
        "What is the capital of France and what is its population?",
        "Search Wikipedia for Python programming language and tell me about it",
        "What is 2 + 2?",
        "Who is Marie Curie and what year was she born?",
    ]

    for query in queries:
        print(f"\n{'='*50}")
        print(f"User: {query}")
        print("-" * 50)
        result = run_agent(query)
        print(f"AI: {result}")

How the Research Agent Works

Ask "Who is Marie Curie and what year was she born?"

  1. AI: "I should search Wikipedia for Marie Curie"
  2. → Calls search_wikipedia("Marie Curie")
  3. → Returns summary including birth year
  4. AI: "Marie Curie was born in 1867..."

Ask "What's the population of France divided by 1000?"

  1. AI: "First, I'll search for the population of France"
  2. → Calls search_wikipedia("France") → gets ~68 million
  3. AI: "Now I'll divide by 1000"
  4. → Calls calculate("68000000 / 1000") → "68000"
  5. AI: "The population of France divided by 1000 is 68,000"

The agent chains tools together naturally — this is the core of agentic AI.