Building My First AI Agent with CrewAI and Gemini

Siva Gollapalli
6 min readFeb 13, 2025

--

Hey everyone, in this post, I’m excited to share my journey into the world of AI agents. I’ll walk you through how I built my first agent using the powerful combination of CrewAI and Gemini. We’ll cover what agents are, why they matter, and how you can start building them yourself. Let’s dive in!

What Exactly is an AI Agent?

So, what do we mean by an “agent” in this context? Think of them as autonomous programs capable of thinking and acting on your behalf to complete a given task. Unlike traditional programs that follow a rigid, predefined path, an AI agent leverages Large Language Models (LLMs) to dynamically determine its course of action. We call this an agentic workflow.

Instead of following a predefined set of steps like traditional software, an agentic workflow dynamically adapts its actions based on the output of a Large Language Model (LLM). This means the path taken by the program is not fixed, but rather determined by the LLM’s interpretation of the situation and the desired goal.

For example, consider an agent designed to reply to emails. A traditional system might use a set of predefined templates. An agent, on the other hand, can analyze the content of the email, determine the most appropriate response, and even pull information from other sources to craft the reply. This makes them incredibly flexible and capable of handling complex tasks with much more nuance.

Why Should We Care About AI Agents?

Now, you might be thinking, “Okay, that’s interesting, but why do I need one?” The truth is, agentic systems aren’t mandatory for every organization. However, they offer incredible potential for automating those mundane tasks that require a level of thought or human intervention. These tasks, though often boring, are frequently prerequisites for completing higher-priority work.

Take, for example, a recruiter. Their job involves a mix of tasks, like sourcing potential candidates and interacting with them. An AI agent can handle the sourcing part — gathering profiles based on a set of requirements — freeing up the recruiter to focus on engaging with candidates, which is a higher-value, more human-centric activity. Ultimately, this saves time and boosts productivity.

How Do We Build These Agents?

There are several frameworks out there for building AI agents, each with its own strengths and weaknesses. We have tools like Autogen, Langgraph, CrewAI, smolagents, etc. For this example, we’ll be using CrewAI. So, before we jump into the code, let’s clarify some of the core concepts behind CrewAI:

  • Crew: This is the top-level entity. Think of it as the manager of the entire operation, orchestrating agents, tasks, and ensuring we get the desired outcome.
  • Agent: These are specialized team members, each with a specific role and a set of tools. They are the workhorses of the crew, getting things done.
  • Task: This is the main objective to be accomplished. In our case, it’s to generate traffic to a specific API endpoint.
  • Tools: These are like utility functions that agents use to accomplish their tasks, such as making API calls or interacting with the filesystem.

CrewAI also uses YAML configuration files to define agents and tasks. This allows for a structured approach to defining agents and their capabilities. Here’s an example of how agents.yaml could look like:

And here’s an example of how tasks.yaml could look like:

performance_test_engineer_task:
description: >
Please extract input params no_of_requests, method and URL from {task_description} and complete the task
expected_output: >
Please display success if all requests are successfull else print fail
agent: performance_test_engineer

Let’s Get Our Hands Dirty: Building an API Performance Testing Agent

Okay, so let’s get our hands dirty with some code! Our goal is to build an agent that can perform API performance testing by sending a specified number of requests to an API endpoint based on user input. Let’s see how we can bring that to life.

Here’s the breakdown of the code:

1. crew.py:

This file defines our agent and the tasks it needs to accomplish. We also specify that our agent will run in sequential order.

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from performance_testing.tools.custom_tool import MyCustomTool

@CrewBase
class PerformanceTesting():
"""PerformanceTesting crew"""
agents_config = 'config/agents.yaml'
tasks_config = 'config/tasks.yaml'
@agent
def performance_test_engineer(self) -> Agent:
return Agent(
config=self.agents_config['performance_test_engineer'],
tools=[MyCustomTool()],
verbose=True
)
@task
def performance_test_engineer_task(self) -> Task:
return Task(
config=self.tasks_config['performance_test_engineer_task'],
)
@crew
def crew(self) -> Crew:
"""Creates the PerformanceTesting crew"""
return Crew(
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential, # We are using sequential approach
verbose=True,
)

Here, @agent decorator creates the agent object, and @task creates a task object, and finally @crew creates the crew which manages the agent and tasks.

2. custom_tool.py:

Since the agent does not know how to send parallel requests we define the logic here using custom tool.

from crewai.tools import BaseTool
from typing import Type
from pydantic import BaseModel, Field
from concurrent.futures import ThreadPoolExecutor
import json
import requests

class MyCustomToolInput(BaseModel):
"""Input schema for MyCustomTool."""
num_requests: int = Field(..., description="No of requests that need to be sent")
url: str = Field(..., description="HTTP API endpoint to which request has to be sent")
method: str = Field(..., description="Type of HTTP Method to be used to send API request")
class MyCustomTool(BaseTool):
name: str = "ParallelHTTPRequestsTool"
description: str = (
"""
Execute parallel HTTP requests.

Args:
num_requests (int): Number of parallel requests to send.
url (str): The target URL for the requests.
method (str): HTTP method (GET or POST).

Returns:
str: A summary of responses.
"""
)
args_schema: Type[BaseModel] = MyCustomToolInput
def _run(self, num_requests: int, url: str, method: str) -> str:
def send_request(index):
"""
Helper function to send a single HTTP request.
"""
try:
if method.upper() == "GET":
response = requests.get(url)
elif method.upper() == "POST":
data={
'post': {
'title': 'simple title',
'description': 'simple description'
}
}
response = requests.post(url, json=data)
else:
return f"Request {index}: Unsupported HTTP method: {method}"
return f"Request {index}: Status {response.status_code}, Response: {response.text}"
except Exception as e:
return f"Request {index}: Error: {str(e)}"
with ThreadPoolExecutor(max_workers=num_requests) as executor:
results = list(executor.map(send_request, range(1, num_requests + 1)))
return "\n".join(results)

Here we define the request type and create multi-threading to send concurrent requests to API endpoint.

3. main.py:

This is our main application to kickoff the process.

from crew import PerformanceTesting
def run():
"""
Run the crew.
"""
inputs = {
"task_description": "Please do send 5 GET requests to an API endpoint http://localhost:3000/posts.json"
}
PerformanceTesting().crew().kickoff(inputs=inputs)

Here we use the PerformanceTesting crew object to kick off the process, we pass in task input here.

How does it all work?

The crew.py file defines our agents and their corresponding tasks. Each agent can be configured with a set of tools. In our example, the agent has only one custom tool: MyCustomTool, which provides the logic to send parallel requests to an API. When the crew starts, the LLM analyses the task and determines the most suitable tool to be used based on the task description. In this case, it chooses our custom tool to send multiple HTTP requests.

As you can see, our task description is just a simple sentence, yet the agent can understand the context and extract necessary details to complete the task. With traditional tools, we’d need to provide specific parameters, but here, we just pass a descriptive task, making the workflow far more streamlined.

Prompt Engineering and Task Description

The effectiveness of an agent depends heavily on the quality of its task descriptions, which are essentially prompts for the LLM. A clear, specific description guides the LLM and ensures the agent performs as intended.

Error Handling

We’ve included a basic try-except block within our custom tool. In real-world scenarios, more robust error handling and logging mechanisms would be necessary to ensure the system is reliable.

Wrapping Up

This is a basic example, but it lays the foundation for building powerful AI agents. By combining the strengths of LLMs and custom tools, you can automate complex tasks and boost your productivity significantly.

Feel free to explore CrewAI further and experiment with your own agents! You can find the final code for this example here

Let me know if you have any questions or if there is anything I have missed. I am always open to discussion!

Enjoyed this article? Please share it with your network to help others learn.

Stay connected for more insights:

  • Follow me on Medium for the latest articles.
  • Connect with me on LinkedIn for professional updates.
  • Join the conversation on Twitter and share your thoughts.

--

--

Responses (1)