Building Your First CrewAI Tool: Tavily Search Walkthrough

AI agents are software entities that perform tasks, powered by LLMs. They take in inputs, reason, make decisions, and take action. However, their potential is unlocked when we extend their capabilities beyond their inherent knowledge. AI agents use tools to perform their tasks, including external data sets, web searches, APIs, and even other agents. They can query APIs to request additional information or execute an action based on an end user's request. AI agents can also remember past interactions and behaviors, and use this information to inform their future actions. In this blog post, we'll learn how to extend the capabilities of AI agents built using CrewAI by creating our tool using the Tavily Search API.

I have written a similar blog post for the AutoGen framework: Coding Your First AutoGen Tool: Tavily Search Walkthrough

If you are in a rush and just want to see the code head over to GitHub.

Setting Up Our Environment

Before we start building the Tavily Search tool, we need to set up our development environment.

Python Installation

This tutorial assumes you're using Python 3.8 or later. Ensure your Python installation is up to date before proceeding.

If you don't have Python installed, visit the official Python website and follow the installation instructions for your operating system.

Installing the dependencies

The first step is to install CrewAI via the command line

pip install crewai

This will install CrewAI globally and make the CrewAI CLI available for us to use. The next step would be to then create our project via the CLI

crewai create crew crewai-tavily-tool-demo

This will create a project with a folder name like crewai_tavily_tool_demo.

Getting the TAVILY_API_KEY

To use the Tavily search API, you'll need to obtain an API key. Follow these steps:

  1. Go to the Tavily website at https://tavily.com/
  2. If you don't have an account, you'll need to sign up first. Look for a "Dashboard" button on the homepage.
  3. Once you're logged in, navigate to your account dashboard.
  4. Look for an option to generate or view your API key. It might be labeled as "API Keys".
  5. Generate a new API key if one isn't already available.
  6. Copy the API key and store it securely. You'll need to use this key in your code to authenticate requests to the Tavily search API.
  7. Be aware that Tavily may offer different pricing tiers or a free trial. Check their pricing page for current offers and any usage limits.

Remember to keep your API key confidential and never share it publicly. You may want to set it as an environment variable in your development environment for added security.

Getting the GROQ_API_KEY

Groq will provide our language model. Follow these steps:

With these steps completed, your environment should be ready for building our RAG chatbot!

Building our search tool

Now that we have everything setup, we can start building the search tool. Inside the crewai_tavily_tool_demo directory, create a .env file to store our API keys

TAVILY_API_KEY=tvly-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
GROQ_API_KEY=gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Import Required Libraries

First we are going to create the Tavily Search Tool inside the tools folder in the project. The path should look something like tools/tavily_search.py . Let’s start by importing the required libraries

# crewai_tavily_tool_demo/src/crewai_tavily_tool_demo/tools/tavily_search.py

from crewai_tools import BaseTool
from pydantic import BaseModel, Field
from tavily import TavilyClient
from typing import Annotated, Optional, Any, Type

The code starts by importing necessary libraries:

  • crewai_tools: Likely a framework providing the BaseTool class for building tools.
  • pydantic: Used for data validation and creating structured data models.
  • tavily: This is the core library that interacts with the Tavily Search API.
  • typing: Provides type hints for improved code readability and type safety.

Defining the Search Input: TavilySearchInput Class

class TavilySearchInput(BaseModel):
    query: Annotated[str, Field(description="The search query string")]
    max_results: Annotated[
        int, Field(description="Maximum number of results to return", ge=1, le=10)
    ] = 5
    search_depth: Annotated[
        str,
        Field(
            description="Search depth: 'basic' or 'advanced'",
            choices=["basic", "advanced"],
        ),
    ] = "basic"

This class defines the structure of the user's search input. It inherits from BaseModel provided by pydantic.

  • Three fields are defined:
    • query: This is a mandatory string field (indicated by Annotated[str]) representing the search term the user wants to explore.
    • max_results: This optional integer field (default value 5) allows users to specify the desired number of search results (between 1 and 10). Pydantic also provides validation (ge=1, le=10) to ensure valid values.
    • search_depth: Another optional string field (default value "basic") allows customization of the search depth. Users can choose between "basic" or "advanced" searches depending on their needs.

Building the Search Tool: TavilySearchTool Class

class TavilySearchTool(BaseTool):
    name: str = "Tavily Search"
    description: str = (
        "Use the Tavily API to perform a web search and get AI-curated results."
    )
    args_schema: Type[BaseModel] = TavilySearchInput
    client: Optional[Any] = None

    def __init__(self, api_key: Optional[str] = None):
        super().__init__()
        self.client = TavilyClient(api_key=api_key)

This class represents the search tool itself. It inherits from the BaseTool class, likely from the crewai_tools library.

The class has several attributes:

  • name (str): A human-readable name for the tool ("Tavily Search").
  • description (str): A description explaining the tool's functionality ("Use the Tavily API to perform a web search and get AI-curated results.").
  • args_schema (Type[BaseModel]): This specifies the type of data the tool expects as input, which is defined as the TavilySearchInput class we saw earlier.
  • client (Optional[Any]): This variable will hold the connection to the Tavily Search API. It's initially set to None and will be initialized with an API key in the constructor.
    def _run(self, query: str, max_results=5, search_depth="basic") -> str:
        if not self.client.api_key:
            raise ValueError("TAVILY_API_KEY environment variable not set")

        try:
            response = self.client.search(
                query=query, max_results=max_results, search_depth=search_depth
            )
            return self._process_response(response)
        except Exception as e:
            return f"An error occurred while performing the search: {str(e)}"

    def _process_response(self, response: dict) -> str:
        if not response.get("results"):
            return "No results found."

        results = []
        for item in response["results"][:5]:  # Limit to top 5 results
            title = item.get("title", "No title")
            content = item.get("content", "No content available")
            url = item.get("url", "No URL available")
            results.append(f"Title: {title}\\nContent: {content}\\nURL: {url}\\n")

        return "\\n".join(results)

The _run method in the TavilySearchTool class executes the search using the Tavily API, handles potential errors, and processes the response. It checks for a valid API key, sends the search query, and returns the formatted results. The _process_response method extracts and formats the search results from the API response.

This completes the search tool and it’s now ready to be used in our application.

Creating the Crew

Now that the tool is ready to be used, we need to create a Crew that sets up agents and tasks that will use the tool.

First we need to set the role, goal, and backstory for the agent. It’s configured in agents.yaml files in the config folder .

# crewai_tavily_tool_demo/src/crewai_tavily_tool_demo/config/agents.yaml

assistant:
  role: >
    AI Assistant
  goal: >
    Answwer user question: {user_input}
  backstory: >
    You are a helpful AI assistant with access to internet search capabilities.

We also need to set a task for the agent to execute. This is also done in a tasks.yaml file in the config folder. This file defines the name of the task, description, expected_output and the agent assigned to it.

# crewai_tavily_tool_demo/src/crewai_tavily_tool_demo/config/tasks.yaml

assistant_task:
  description: >
    Reply the user who sent the input: {user_input}
  expected_output: >
    An answer to the user input: {user_input}
  agent: assistant

Importing required libraries

Head over into project directory and edit the crew.py file. This is where we configure the Crew that will use our newly created tool.

# crewai_tavily_tool_demo/src/crewai_tavily_tool_demo/crew.py

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai_tavily_tool_demo.tools.tavily_search import TavilySearchTool

tavily_search = TavilySearchTool()

This code imports the necessary libraries from crewai are imported for defining agents, crews, tasks, and processes. It also imports the tool, TavilySearchTool so that we can initialise it. We can initialize the tool in 2 ways:

tavily_search = TavilySearchTool()

This way required us to load the TAVILY_API_KEY into the environment. Alter natively we can initialize the tool and pass the api_key at the same time. Using it this way means we can skip setting the TAVILY_API_KEY in the .env file.

tavily_search = TavilySearchTool(api_key="tvly-xxxxxxxxxxxxxxxxxxxxxx")

Building the Crew

To build the crew we use a decorator which sets up most of what’s needed. We define the agent and the llm it will use, the task and the crew. The CrewAI documentation has enough detail to get you started here so I won’t cover everything.

The important thing here is we need to assign our tool to an agent. In this case we assign the tool in a list to the tools attribute of the assistant agent.

@CrewBase
class CrewaiTavilyToolDemoCrew:
    """CrewaiTavilyToolDemo crew"""

    @agent
    def assistant(self) -> Agent:
        return Agent(
            config=self.agents_config["assistant"],
            tools=[tavily_search],
            verbose=True,
            llm="groq/llama3-8b-8192",
        )

    @task
    def assistant_task(self) -> Task:
        return Task(
            config=self.tasks_config["assistant_task"],
        )

    @crew
    def crew(self) -> Crew:
        """Creates the CrewaiTavilyToolDemo crew"""
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=True,
        )

Creating the chatloop

Next step is to execute the crew in a chat loop and display the results of the interaction between the user and assistant.

#!/usr/bin/env python
import sys
from crewai_tavily_tool_demo.crew import CrewaiTavilyToolDemoCrew
from dotenv import load_dotenv

load_dotenv()

def run():
    while True:
        user_input = input("You: ")
        if user_input.lower() in ["exit", "quit", "bye"]:
            print("Chatbot: Goodbye! It was nice talking to you.")
            break

        inputs = {
            "user_input": f"{user_input}",
        }

        response = CrewaiTavilyToolDemoCrew().crew().kickoff(inputs=inputs)

        print(f"Assistant: {response}")

This runs a runs a conversational AI application using the CrewaiTavilyToolDemoCrew. It continuously prompts the user for input, sends the input to the Crewai crew, and prints the assistant's response until the user exits.

Running the crew

To run the crew, navigate to the root folder of the application and enter the command below.

crewai run

This will start the chatbot and you should be able to ask it questions. You should be able to see the agent making search calls using the tool.

Conclusion

In this tutorial, we've walked through the process of creating a custom tool for CrewAI using the Tavily Search API. This demonstrates how we can extend the capabilities of AI agents beyond their inherent knowledge, allowing them to access real-time information from the web. This approach opens up a world of possibilities for creating specialized AI agents that can perform complex tasks by combining their base knowledge with up-to-date information from various sources.

Some key takeaways from this tutorial:

  1. The importance of proper environment setup and API key management
  2. How to structure a custom tool using CrewAI's framework
  3. The process of integrating the tool with an AI agent and defining its tasks
  4. Creating a simple but effective chat interface for interacting with the AI assistant

As you continue to explore and build with CrewAI, consider other APIs or tools you could integrate to further enhance your AI agents' capabilities. The possibilities are vast, from natural language processing tools to specialized databases or even other AI models.

Remember, the code is available on GitHub. If you have any questions, you can reach out to me on X (formerly Twitter) or LinkedIn.

AI should drive results, not complexity. AgentemAI helps businesses build scalable, efficient, and secure AI solutions. See how we can help.