Hey @paco,
First off, you’re not doing anything wrong. Your code is perfectly fine. Your tool definition is pretty solid, I’d say. You even tested it standalone (with _run
or, even better, run
), and it worked, so your code is great.
Now, about the error you’re running into – like I said, it’s not anything you did wrong. It seems to be something the framework isn’t handling correctly.
You’re trying to use a CrewStructuredTool
in the tools
argument of your Task
. From a user’s perspective, that makes perfect sense. But let’s take a look at the definition of that argument in the source code:
class Task(BaseModel):
"""Class that represents a task to be executed.
Each task must have a description, an expected output and an agent responsible for execution.
Attributes:
[...]
tools: List of tools/resources limited for task execution.
"""
# [...]
tools: Optional[List[BaseTool]] = Field(
default_factory=list,
description="Tools the agent is limited to use for this task.",
)
So, Task.tools
expects a list of BaseTool
instances, right? Let’s check if the new CrewStructuredTool
meets that requirement.
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
is_subclass = issubclass(CrewStructuredTool, BaseTool)
print(f"Is `CrewStructuredTool` a subclass of `BaseTool`? {is_subclass}!")
Nope, it doesn’t. Just to be absolutely sure, let’s look at the definition of CrewStructuredTool
:
class CrewStructuredTool:
"""A structured tool that can operate on any number of inputs.
This tool intends to replace StructuredTool with a custom implementation
that integrates better with CrewAI's ecosystem.
"""
So, CrewStructuredTool
is definitely the third way to declare tools (do we really need so many ways?
) and it’s not a subclass of BaseTool
. That’s why you’re getting that error when you try to pass a CrewStructuredTool
to the Task.tools
argument.
Of course, my deduction here might be limited by my own understanding, so please double-check my reasoning. And if you think it makes sense, I’d suggest opening a GitHub issue, referencing this thread, and suggesting/requesting the following:
- That
Task.tools
be updated to accept objects inheriting from both BaseTool
and CrewStructuredTool
.
- If
CrewStructuredTool
is intended to be a more robust way (and I have to say, it does look pretty solid!) to replace one of the other two methods for defining tools, then the method it’s meant to replace should start issuing deprecation warnings, signaling its removal in a future version of the framework to improve the Developer Experience (DX).
- That
CrewStructuredTool
adopt better error handling and reporting practices (like using PydanticCustomError
if it becomes a pydantic.BaseModel
, or RuntimeError
otherwise). This would improve error communication with the user and make debugging easier.
Hope this helps shed some light on things! This thread definitely helped clarify things for me too.