发布自定义工具
如何构建、打包并将你自己的 CrewAI 兼容工具发布到 PyPI,以便任何 CrewAI 用户都可以安装和使用它们。
概览
CrewAI 的工具系统是为扩展而设计的。如果你构建了一个可能对其他人也有帮助的工具,你可以将它打包为一个独立的 Python 库,发布到 PyPI,并让任何 CrewAI 用户都能使用它——无需向 CrewAI 仓库提交 PR。
本指南将带你完成整个流程:实现工具契约、组织你的包结构,以及发布到 PyPI。
工具契约
每个 CrewAI 工具都必须满足以下两种接口之一:
选项 1:继承 BaseTool
继承 crewai.tools.BaseTool 并实现 _run 方法。定义 name、description,并可选定义 args_schema 以进行输入校验。
from crewai.tools import BaseToolfrom pydantic import BaseModel, Fieldclass GeolocateInput(BaseModel):"""GeolocateTool 的输入模式。"""address: str = Field(..., description="需要进行地理编码的街道地址。")class GeolocateTool(BaseTool):name: str = "Geolocate"description: str = "将街道地址转换为纬度 / 经度坐标。"args_schema: type[BaseModel] = GeolocateInputdef _run(self, address: str) -> str:# 在这里实现你的逻辑return f"40.7128, -74.0060"
选项 2:使用 @tool 装饰器
对于更简单的工具,@tool 装饰器可以将一个函数转换为 CrewAI 工具。该函数必须包含 docstring(会被用作工具描述)以及类型注解。
from crewai.tools import tool@tool("Geolocate")def geolocate(address: str) -> str:"""将街道地址转换为纬度 / 经度坐标。"""return "40.7128, -74.0060"
关键要求
无论你使用哪种方式,你的工具都必须:
- 具有
name—— 一个简短且描述明确的标识符。 - 具有
description—— 告诉代理何时以及如何使用该工具。这会直接影响代理使用你工具的效果,因此请写得清晰且具体。 - 实现
_run(BaseTool)或提供 函数体(@tool)—— 即同步执行逻辑。 - 为所有参数和返回值添加 类型注解。
- 返回一个 字符串 结果(或可以被有意义地转换为字符串的内容)。
可选:异步支持
如果你的工具执行的是 I/O 密集型工作,可以实现 _arun 以支持异步执行:
class GeolocateTool(BaseTool):name: str = "Geolocate"description: str = "将街道地址转换为纬度 / 经度坐标。"def _run(self, address: str) -> str:# 同步实现...async def _arun(self, address: str) -> str:# 异步实现...
可选:使用 args_schema 进行输入校验
将一个 Pydantic 模型定义为你的 args_schema,即可获得自动输入校验和清晰的错误信息。如果你不提供它,CrewAI 会根据 _run 方法的签名自动推断。
from pydantic import BaseModel, Fieldclass TranslateInput(BaseModel):"""TranslateTool 的输入模式。"""text: str = Field(..., description="要翻译的文本。")target_language: str = Field(default="en",description="目标语言的 ISO 639-1 语言代码。",)
对于已发布的工具,推荐显式定义 schema——这样可以带来更好的代理行为,以及更清晰的用户文档。
可选:环境变量
如果你的工具需要 API 密钥或其他配置,可以通过 env_vars 声明它们,以便用户知道需要设置什么:
from crewai.tools import BaseTool, EnvVarclass GeolocateTool(BaseTool):name: str = "Geolocate"description: str = "将街道地址转换为纬度 / 经度坐标。"env_vars: list[EnvVar] = [EnvVar(name="GEOCODING_API_KEY",description="地理编码服务的 API 密钥。",required=True,),]def _run(self, address: str) -> str:...
包结构
请将你的项目组织为标准的 Python 包。下面是推荐的目录结构:
crewai-geolocate/├── pyproject.toml├── LICENSE├── README.md└── src/└── crewai_geolocate/├── __init__.py└── tools.py
pyproject.toml
[project]name = "crewai-geolocate"version = "0.1.0"description = "一个用于对街道地址进行地理定位的 CrewAI 工具。"requires-python = ">=3.10"dependencies = ["crewai",][build-system]requires = ["hatchling"]build-backend = "hatchling.build"
将 crewai 声明为依赖项,这样用户会自动获得兼容版本。
__init__.py
重新导出你的工具类,这样用户就可以直接导入它们:
from crewai_geolocate.tools import GeolocateTool__all__ = ["GeolocateTool"]
命名约定
- 包名:使用前缀
crewai-(例如crewai-geolocate)。这样当用户在 PyPI 中搜索时更容易发现你的工具。 - 模块名:使用下划线(例如
crewai_geolocate)。 - 工具类名:使用 PascalCase,并以
Tool结尾(例如GeolocateTool)。
测试你的工具
在发布之前,请确认你的工具可以在一个 crew 中正常工作:
from crewai import Agent, Crew, Taskfrom crewai_geolocate import GeolocateToolagent = Agent(role="Location Analyst",goal="为给定地址找到坐标。",backstory="一位地理空间数据专家。",tools=[GeolocateTool()],)task = Task(description="找出 Washington, DC 的 1600 Pennsylvania Avenue 的坐标。",expected_output="该地址的纬度和经度。",agent=agent,)crew = Crew(agents=[agent], tasks=[task])result = crew.kickoff()print(result)
发布到 PyPI
当你的工具测试完成并准备就绪后:
# 构建包uv build# 发布到 PyPIuv publish
如果这是你第一次发布,你需要一个 PyPI 账户 和一个 API token。
发布之后
用户可以通过以下方式安装你的工具:
pip install crewai-geolocate
或者使用 uv:
uv add crewai-geolocate
然后在他们的 crew 中使用它:
from crewai_geolocate import GeolocateToolagent = Agent(role="Location Analyst",tools=[GeolocateTool()],# ...)
