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?"
- AI: "I should search Wikipedia for Marie Curie"
- → Calls
search_wikipedia("Marie Curie") - → Returns summary including birth year
- AI: "Marie Curie was born in 1867..."
Ask "What's the population of France divided by 1000?"
- AI: "First, I'll search for the population of France"
- → Calls
search_wikipedia("France")→ gets ~68 million - AI: "Now I'll divide by 1000"
- → Calls
calculate("68000000 / 1000")→ "68000" - AI: "The population of France divided by 1000 is 68,000"
The agent chains tools together naturally — this is the core of agentic AI.