Now that you understand why tools are important for extending an agent's abilities and how to define a simple tool like a calculator, it's time to get your hands dirty with a more dynamic capability: adding web search to your agent. This will allow your agent to find information that isn't part of its initial training data, such as current events or highly specific facts.
In this practical exercise, we'll guide you through integrating a search tool. You'll learn how to:
Let's begin by building the search functionality.
To perform a web search, our agent needs a way to interact with a search engine. This is typically done through an API (Application Programming Interface). Many services offer search APIs, such as SerpApi, Google Custom Search API, or Bing Search API. For this exercise, we'll outline a generic approach. You'll need to sign up for one of these services and obtain an API key. Most offer a free tier that's sufficient for learning purposes.
1. Install Necessary Libraries
If you're interacting with a web API, you'll likely need the requests
library in Python to make HTTP requests. If you don't have it installed, open your terminal or command prompt and run:
pip install requests
2. Create the Search Function
Let's write a Python function that takes a search query, calls an external search API, and returns a summary of the results. Below is a template. You'll need to replace "YOUR_API_KEY"
and the API_ENDPOINT
with details from your chosen search API provider. The structure of the API response will also vary, so you'll need to adjust how results
are extracted and formatted.
import requests
import json # Used for parsing the API response
# Replace with your actual API key and endpoint
SEARCH_API_KEY = "YOUR_SEARCH_API_KEY"
SEARCH_API_ENDPOINT = "https://api.example-search.com/search" # Replace with actual API endpoint
def web_search(query: str) -> str:
"""
Performs a web search for the given query using an external API
and returns a formatted string of search results.
"""
if SEARCH_API_KEY == "YOUR_SEARCH_API_KEY":
return "Search API key not configured. Please set it up to use the search tool."
params = {
"q": query,
"api_key": SEARCH_API_KEY,
# Add any other parameters required by your search API
# e.g., 'num_results': 3
}
try:
response = requests.get(SEARCH_API_ENDPOINT, params=params)
response.raise_for_status() # Raises an HTTPError for bad responses (4XX or 5XX)
search_data = response.json() # Assumes the API returns JSON
# Process the search_data. This part is highly dependent on your API's response format.
# For example, you might extract titles and snippets.
snippets = []
if "organic_results" in search_data: # Example for SerpApi-like structure
for result in search_data["organic_results"][:3]: # Get top 3 results
snippet = f"Title: {result.get('title', 'N/A')}\nSnippet: {result.get('snippet', 'N/A')}\nLink: {result.get('link', 'N/A')}"
snippets.append(snippet)
elif "webPages" in search_data and "value" in search_data["webPages"]: # Example for Bing-like structure
for result in search_data["webPages"]["value"][:3]:
snippet = f"Title: {result.get('name', 'N/A')}\nSnippet: {result.get('snippet', 'N/A')}\nLink: {result.get('url', 'N/A')}"
snippets.append(snippet)
else:
# Fallback or more generic parsing if the structure is unknown or different
# This might involve just returning a string representation of the JSON
# or trying to find common keys. For simplicity, we'll indicate if results are not found.
return "No specific results found in the expected format. Raw data might be available."
if not snippets:
return "No search results found."
return "\n\n".join(snippets)
except requests.exceptions.RequestException as e:
return f"Error during search: {e}"
except json.JSONDecodeError:
return "Error: Could not decode search results from API."
# Example usage (optional, for testing the function directly)
# if __name__ == "__main__":
# test_query = "latest advancements in LLM agents"
# search_results = web_search(test_query)
# print(f"Search results for '{test_query}':\n{search_results}")
Important: Remember to replace placeholders with your actual API credentials and adapt the result parsing logic to match the specific API you choose. Read the documentation for your chosen search API carefully.
Now that we have a web_search
function, we need to tell our LLM agent about it. As discussed in "Prompting Agents to Utilize Tools," the LLM needs a clear name and description for each tool to understand what it does and when to use it.
Let's define a description for our search tool:
search_tool_description = {
"name": "WebSearch",
"description": "Use this tool to find current information on the internet, answer questions about recent events, or get up-to-date details on any topic. Input should be a clear search query.",
"function": web_search, # Reference to our Python function
"input_schema": { # Describes the expected input for the tool
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query to look up on the internet."
}
},
"required": ["query"]
}
}
This dictionary provides:
name
: A short, identifiable name for the tool (e.g., WebSearch
).description
: A clear explanation of what the tool does and when the agent should consider using it. This is very important for the LLM's decision-making process.function
itself: So the agent framework can call it.input_schema
: This tells the LLM (and the agent framework) what arguments the tool expects. Here, it expects a single string argument named query
.Your agent framework will use this information. When the LLM decides to use "WebSearch", the framework will know to call the web_search
function and will try to extract a query
argument from the LLM's generated parameters.
How you integrate this tool depends on the specific agent framework you might be using or the structure of your custom agent. For this practical, let's assume you have a basic agent loop that involves:
Here's a simplified conceptual flow of how your agent might use this. Imagine you have a core function run_agent_turn(user_request, available_tools)
:
# This is a simplified representation. Your actual agent logic might be more complex.
# Assume an LLM interaction function like `get_llm_decision(prompt, tools_descriptions)`
# which returns either a direct answer or a tool call instruction.
def get_llm_decision(prompt_text, tools_list_for_llm):
"""
Placeholder for your LLM call.
This function would send the prompt and tool descriptions to the LLM.
The LLM would respond with either:
1. A direct answer.
2. A JSON object indicating a tool call, e.g.,
{'tool_name': 'WebSearch', 'tool_input': {'query': 'current AI trends'}}
"""
# In a real scenario, this involves an API call to an LLM.
# For this example, we'll simulate the LLM's decision based on keywords.
print(f"\n[LLM Input Prompt]:\n{prompt_text}\n") # Show what LLM sees
if "weather in Paris" in prompt_text.lower() and "WebSearch" in str(tools_list_for_llm):
print("[LLM Simulated Decision]: Use WebSearch for 'current weather in Paris'")
return {"tool_name": "WebSearch", "tool_input": {"query": "current weather in Paris"}}
elif "latest news on AI" in prompt_text.lower() and "WebSearch" in str(tools_list_for_llm):
print("[LLM Simulated Decision]: Use WebSearch for 'latest news on AI'")
return {"tool_name": "WebSearch", "tool_input": {"query": "latest news on AI"}}
else:
print("[LLM Simulated Decision]: Answer directly (or I don't know).")
return {"answer": "I can only answer if I decide to use a tool for specific queries like weather or news in this simulation."}
# Our list of available tools for the agent
available_tools = {
"WebSearch": search_tool_description
# You could add other tools here, like the calculator from a previous section
}
def run_agent_turn(user_request: str):
print(f"User: {user_request}")
# Prepare the list of tool descriptions for the LLM
tools_for_llm_prompt = []
for tool_name, tool_details in available_tools.items():
tools_for_llm_prompt.append({
"name": tool_details["name"],
"description": tool_details["description"],
"parameters": tool_details["input_schema"] # LLM needs to know what parameters to generate
})
# Construct a prompt for the LLM
# This is a very basic prompt. Real agent prompts are more sophisticated.
prompt = f"""You are a helpful assistant. You have access to the following tools:
{json.dumps(tools_for_llm_prompt, indent=2)}
User query: "{user_request}"
Based on the user query and the available tools, decide if a tool should be used.
If yes, respond with a JSON object specifying the tool name and its input.
For example: {{"tool_name": "WebSearch", "tool_input": {{"query": "your search query"}}}}
If no tool is needed, or you can answer directly, provide the answer.
"""
llm_response = get_llm_decision(prompt, tools_for_llm_prompt)
if "tool_name" in llm_response:
tool_name = llm_response["tool_name"]
tool_input_args = llm_response["tool_input"]
if tool_name in available_tools:
selected_tool = available_tools[tool_name]
tool_function = selected_tool["function"]
# Here, we assume the LLM provides arguments matching the function's needs.
# For `web_search`, it expects a 'query' argument.
query_arg = tool_input_args.get("query")
if query_arg is None:
print(f"Agent: LLM decided to use {tool_name} but didn't provide a 'query'.")
return
print(f"Agent: Using tool '{tool_name}' with query: '{query_arg}'")
tool_output = tool_function(query_arg)
print(f"Tool '{tool_name}' output:\n{tool_output}")
# Now, we would typically send this output back to the LLM for a final synthesis.
# For simplicity in this step-by-step, we'll just print it.
# In a full ReAct loop, the prompt for this second LLM call would be:
# final_prompt = f"{prompt}\nObservation: {tool_output}\nThought: Now I will use this information to answer the user.\nFinal Answer:"
# final_answer_from_llm = get_llm_decision(final_prompt, []) # No tools needed for final answer
# print(f"Agent (final answer based on tool): {final_answer_from_llm.get('answer', 'Could not process tool output.')}")
print(f"Agent: Based on the search, here's what I found: {tool_output}")
else:
print(f"Agent: LLM tried to use an unknown tool: {tool_name}")
elif "answer" in llm_response:
print(f"Agent: {llm_response['answer']}")
else:
print("Agent: I'm not sure how to proceed.")
# Let's try it out!
# First, ensure your SEARCH_API_KEY in web_search function is set,
# otherwise it will return "Search API key not configured."
run_agent_turn("What is the latest news on AI?")
print("\n" + "="*50 + "\n")
run_agent_turn("Tell me a joke.") # This query likely won't trigger the search tool in our simulation
Diagram: Agent Tool Usage Flow
To visualize how the agent, LLM, and tool interact, consider the following diagram:
This diagram illustrates the sequence of operations when a user's request leads the agent to use the WebSearch tool. The agent core orchestrates interactions between the user, the LLM, and the tool, which in turn communicates with an external API.
To fully test this:
web_search
function with a valid API key and endpoint.get_llm_decision
function provided is a simulation. In a real agent, this function would make an API call to an LLM (like OpenAI's GPT models, Anthropic's Claude, or a locally run model via an interface like Ollama). You would pass the user's query, the system message, and the JSON descriptions of available tools to the LLM, and it would decide whether to call a tool.Observe how your agent (or your simulated LLM decision logic) decides whether to use the WebSearch
tool. If it uses the tool, check the output.
print(response.json())
inside your web_search
function during testing to understand the structure and adapt your parsing logic (snippets
creation) accordingly.description
in search_tool_description
is ambiguous or doesn't clearly convey when to use the tool, the LLM might ignore it or use it incorrectly.web_search
: The example function has basic error handling. Real-world tools should be more robust, handling network issues, timeouts, and unexpected API responses gracefully.This hands-on exercise provides a foundational step in making your LLM agents more capable and connected to the world's information. By equipping them with tools like search, you significantly expand the range of tasks they can perform and the quality of their responses. As you build more complex agents, you'll find yourself creating and integrating a variety of tools to suit different needs.
Was this section helpful?
© 2025 ApX Machine Learning