Chapter 4: Tool - Equipping Your Agents
In Chapter 3: Task, we learned how to define specific assignments (Task
s) for our AI Agent
s. We told the ‘Travel Researcher’ agent to find sunny cities and the ‘Activity Planner’ agent to create an itinerary.
But wait… how does the ‘Travel Researcher’ actually find those cities? Can it browse the web? Can it look at weather data? By default, an Agent’s “brain” (LLM) is great at reasoning and generating text based on the information it already has, but it can’t interact with the outside world on its own.
This is where Tool
s come in! They are the special equipment and abilities we give our agents to make them more capable.
Why Do We Need Tools?
Imagine you hire a brilliant researcher. They can think, analyze, and write reports. But if their task is “Find the best coffee shop near me right now,” they need specific tools: maybe a map application, a business directory, or a review website. Without these tools, they can only guess or rely on outdated knowledge.
Similarly, our AI Agents need Tool
s to perform actions beyond simple text generation.
- Want your agent to find current information? Give it a web search tool.
- Need it to perform calculations? Give it a calculator tool.
- Want it to read a specific document? Give it a file reading tool.
- Need it to ask another agent for help? Use the built-in delegation tool (AgentTools).
Problem Solved: Tool
s extend an Agent’s capabilities beyond its built-in knowledge, allowing it to interact with external systems, perform specific computations, or access real-time information.
What is a Tool?
Think of a Tool
as a function or capability that an Agent can choose to use while working on a Task. Each Tool
has a few key parts:
name
: A short, unique name for the tool (e.g.,web_search
,calculator
).description
: This is very important! It tells the Agent what the tool does and when it should be used. The agent’s LLM reads this description to decide if the tool is appropriate for the current step of its task. A good description is crucial for the agent to use the tool correctly. Example: “Useful for searching the internet for current events or information.”args_schema
(Optional): Defines the inputs the tool needs to work. For example, aweb_search
tool would likely need aquery
argument (the search term). This is often defined using Pydantic models._run
method: This is the actual code that gets executed when the agent uses the tool. It takes the arguments defined inargs_schema
and performs the action (like calling a search API or performing a calculation).
Agents are given a list of Tool
s they are allowed to use. When an agent is working on a task, its internal thought process might lead it to conclude that it needs a specific capability. It will then look through its available tools, read their descriptions, and if it finds a match, it will figure out the necessary arguments and execute the tool’s _run
method.
Equipping an Agent with a Tool
CrewAI integrates with many existing toolkits, like crewai_tools
(install separately: pip install 'crewai[tools]'
). Let’s give our ‘Travel Researcher’ agent a web search tool. We’ll use SerperDevTool
as an example, which uses the Serper.dev API for Google Search results.
(Note: Using tools like this often requires API keys. You’ll need to sign up for Serper.dev and set the SERPER_API_KEY
environment variable for this specific example to run.)
# Make sure you have crewai and crewai_tools installed
# pip install crewai crewai_tools
import os
from crewai import Agent
from crewai_tools import SerperDevTool
# Set up your API key (replace with your actual key or environment variable setup)
# IMPORTANT: Do NOT hardcode keys in production code! Use environment variables.
# os.environ["SERPER_API_KEY"] = "YOUR_SERPER_API_KEY"
# 1. Instantiate the tool
# (It automatically gets a name and description)
search_tool = SerperDevTool()
# 2. Define the agent and provide the tool in the 'tools' list
researcher = Agent(
role='Expert Travel Researcher',
goal='Find the three most exciting and sunny European cities for a birthday trip in late May.',
backstory=(
"You are a world-class travel researcher with deep knowledge of "
"European destinations. You excel at finding hidden gems and understanding "
"weather patterns. Your recommendations are always insightful and tailored. "
"You MUST use the web search tool to find the most up-to-date information." # Added instruction
),
verbose=True,
allow_delegation=False,
tools=[search_tool] # <-- Give the agent access to the tool!
# llm=your_llm # Assumes an LLM is configured (see Chapter 6)
)
# (You would then create a Task and a Crew as shown in previous chapters)
# task1 = Task(description="Find 3 sunny European cities for May...", agent=researcher, ...)
# trip_crew = Crew(agents=[researcher], tasks=[task1], ...)
# result = trip_crew.kickoff()
# print(result)
Explanation:
- We import
SerperDevTool
fromcrewai_tools
. - We create an instance of the tool:
search_tool = SerperDevTool()
. This object now holds thename
(“Search”),description
(“A tool that can be used to search the internet…”), and the_run
logic to call the Serper API. - When defining our
researcher
Agent, we pass a list containingsearch_tool
to thetools
parameter:tools=[search_tool]
. - We also updated the agent’s
backstory
to explicitly encourage using the tool, which can sometimes help guide the agent.
Expected Outcome (Conceptual):
When this researcher
agent runs a Task like “Find 3 sunny European cities for May…”, because verbose=True
, you would see its thought process. It might look something like this (simplified):
Thought: I need to find sunny European cities for May. The best way to get current information is to search the web. I have a 'Search' tool available. I should use it.
Action: Search
Action Input: {"query": "best sunny European cities May weather culture food"}
[... Agent waits for the tool to run ...]
Observation: [Search results mentioning Lisbon, Seville, Malta, Athens, etc. with details]
Thought: Okay, the search results suggest Lisbon, Seville, and Malta are good options based on sun, culture, and food. I will summarize these findings as requested.
Final Answer: Here are the top 3 sunny European cities for May... 1. Lisbon... 2. Seville... 3. Malta...
The agent used the tool’s description
to know when to use it, formulated the necessary input (query
), executed the tool, received the Observation
(the tool’s output), and then used that information to generate its Final Answer
.
How Tools Work “Under the Hood”
When an Agent equipped with tools runs a Task, a fascinating interaction happens between the Agent, its LLM brain, and the Tools.
- Task Received: The Agent gets the task description and any context.
- Initial Thought: The Agent’s LLM thinks about the task and its profile (
role
,goal
,backstory
). It formulates an initial plan. - Need for Capability: The LLM might realize it needs information it doesn’t have (e.g., “What’s the weather like right now?”) or needs to perform an action (e.g., “Calculate 5 factorial”).
- Tool Selection: The Agent provides its LLM with the list of available
Tool
s, including theirname
s and crucially, theirdescription
s. The LLM checks if any tool description matches the capability it needs. - Tool Invocation Decision: If the LLM finds a suitable tool (e.g., it needs to search, and finds the
Search
tool whose description says “Useful for searching the internet”), it decides to use it. It outputs a special message indicating the tool name and the arguments (based on the tool’sargs_schema
). - Tool Execution: The CrewAI framework intercepts this special message. It finds the corresponding
Tool
object and calls itsrun()
method, passing the arguments the LLM provided. - Action Performed: The tool’s
_run()
method executes its code (e.g., calls an external API, runs a calculation). - Result Returned: The tool’s
_run()
method returns its result (e.g., the text of the search results, the calculated number). - Observation Provided: The CrewAI framework takes the tool’s result and feeds it back to the Agent’s LLM as an “Observation”.
- Continued Thought: The LLM now has new information from the tool. It incorporates this observation into its thinking and continues working on the task, potentially deciding to use another tool or generate the final answer.
Let’s visualize this flow for our researcher using the search tool:
sequenceDiagram
participant A as Agent
participant LLM as Agent's Brain
participant ST as Search Tool
A->>LLM: Task: "Find sunny cities..." Plan?
LLM-->>A: Plan: Need current info. Search web for "sunny European cities May".
A->>A: Check tools: Found 'Search' tool (description matches).
A->>LLM: Format request for 'Search' tool. Query?
LLM-->>A: Output: Use Tool 'Search' with args {"query": "sunny European cities May"}
A->>ST: run(query="sunny European cities May")
Note right of ST: ST._run() calls Serper API...
ST-->>A: Return results: "Lisbon (Sunny...), Seville (Hot...), Malta (Warm...)"
A->>LLM: Observation: Got results "Lisbon...", "Seville...", "Malta..."
LLM-->>A: Thought: Use these results to formulate the final list.
LLM-->>A: Final Answer: "Based on recent web search, the top cities are..."
Diving into the Code (tools/base_tool.py
)
The foundation for all tools is the BaseTool
class (found in crewai/tools/base_tool.py
). When you use a pre-built tool or create your own, it typically inherits from this class.
# Simplified view from crewai/tools/base_tool.py
from abc import ABC, abstractmethod
from typing import Type, Optional, Any
from pydantic import BaseModel, Field
class BaseTool(BaseModel, ABC):
# Configuration for the tool
name: str = Field(description="The unique name of the tool.")
description: str = Field(description="What the tool does, how/when to use it.")
args_schema: Optional[Type[BaseModel]] = Field(
default=None, description="Pydantic schema for the tool's arguments."
)
# ... other options like caching ...
# This method contains the actual logic
@abstractmethod
def _run(self, *args: Any, **kwargs: Any) -> Any:
"""The core implementation of the tool's action."""
pass
# This method is called by the agent execution framework
def run(self, *args: Any, **kwargs: Any) -> Any:
"""Executes the tool's core logic."""
# Could add logging, error handling, caching calls here
print(f"----- Executing Tool: {self.name} -----") # Example logging
result = self._run(*args, **kwargs)
print(f"----- Tool {self.name} Finished -----")
return result
# Helper method to generate a structured description for the LLM
def _generate_description(self):
# Creates a detailed description including name, args, and description
# This is what the LLM sees to decide if it should use the tool
pass
# ... other helper methods ...
# You can create a simple tool using the 'Tool' class directly
# or inherit from BaseTool for more complex logic.
from typing import Type
class SimpleTool(BaseTool):
name: str = "MySimpleTool"
description: str = "A very simple example tool."
# No args_schema needed if it takes no arguments
def _run(self) -> str:
return "This simple tool was executed successfully!"
Key takeaways:
BaseTool
requiresname
anddescription
.args_schema
defines the expected input structure (using Pydantic).- The actual logic lives inside the
_run
method. - The
run
method is the entry point called by the framework. - The framework (
crewai/tools/tool_usage.py
andcrewai/agents/executor.py
) handles the complex part: presenting tools to the LLM, parsing the LLM’s decision to use a tool, callingtool.run()
, and feeding the result back.
A special mention goes to AgentTools
(crewai/tools/agent_tools/agent_tools.py
), which provides tools like Delegate work to coworker
and Ask question to coworker
, enabling agents within a Crew to collaborate.
Creating Your Own Simple Tool (Optional)
While CrewAI offers many pre-built tools, sometimes you need a custom one. Let’s create a very basic calculator.
from crewai.tools import BaseTool
from pydantic import BaseModel, Field
from typing import Type
import math # Using math module for safety
# 1. Define the input schema using Pydantic
class CalculatorInput(BaseModel):
expression: str = Field(description="The mathematical expression to evaluate (e.g., '2 + 2 * 4').")
# 2. Create the Tool class, inheriting from BaseTool
class CalculatorTool(BaseTool):
name: str = "Calculator"
description: str = "Useful for evaluating simple mathematical expressions involving numbers, +, -, *, /, and parentheses."
args_schema: Type[BaseModel] = CalculatorInput # Link the input schema
def _run(self, expression: str) -> str:
"""Evaluates the mathematical expression."""
allowed_chars = "0123456789+-*/(). "
if not all(c in allowed_chars for c in expression):
return "Error: Expression contains invalid characters."
try:
# VERY IMPORTANT: eval() is dangerous with arbitrary user input.
# In a real application, use a safer parsing library like 'numexpr' or build your own parser.
# This is a simplified example ONLY.
result = eval(expression, {"__builtins__": None}, {"math": math}) # Safer eval
return f"The result of '{expression}' is {result}"
except Exception as e:
return f"Error evaluating expression '{expression}': {e}"
# 3. Instantiate and use it in an agent
calculator = CalculatorTool()
math_agent = Agent(
role='Math Whiz',
goal='Calculate the results of mathematical expressions accurately.',
backstory='You are an expert mathematician agent.',
tools=[calculator], # Give the agent the calculator
verbose=True
)
# Example Task for this agent:
# math_task = Task(description="What is the result of (5 + 3) * 6 / 2?", agent=math_agent)
Explanation:
- We define
CalculatorInput
using Pydantic to specify that the tool needs anexpression
string. Thedescription
here helps the LLM understand what kind of string to provide. - We create
CalculatorTool
inheriting fromBaseTool
. We setname
,description
, and linkargs_schema
to ourCalculatorInput
. - The
_run
method takes theexpression
string. We added a basic safety check and used a slightly safer version ofeval
. Again,eval
is generally unsafe; prefer dedicated math parsing libraries in production. It returns the result as a string. - We can now instantiate
CalculatorTool()
and add it to an agent’stools
list.
Conclusion
You’ve learned about Tool
s – the essential equipment that gives your AI Agents superpowers! Tools allow agents to perform actions like searching the web, doing calculations, or interacting with other systems, making them vastly more useful than agents that can only generate text. We saw how to equip an agent with pre-built tools and even how to create a simple custom tool by defining its name
, description
, args_schema
, and _run
method. The description
is key for the agent to know when and how to use its tools effectively.
Now that we have Agents equipped with Tools and assigned Tasks, how does the whole Crew actually coordinate the work? Do agents work one after another? Is there a manager? That’s determined by the Process
. Let’s explore that next!
Next: Chapter 5: Process - Orchestrating the Workflow
Generated by AI Codebase Knowledge Builder