Skip to content
CrewAI 文档版本 跟随上游 源快照 a86bf598 更新于 2026-10-18

发布自定义工具

如何构建、打包并把你自己的 CrewAI 兼容工具发布到 PyPI,让任何 CrewAI 用户都能安装和使用。

概述

CrewAI 的工具系统在设计上就是可扩展的。如果你构建了一个能给他人带来好处的工具,可以把它打包成独立的 Python 库、发布到 PyPI,让任何 CrewAI 用户都能使用——无需向 CrewAI 仓库提交 PR。

本指南会走完整个流程:实现工具契约、组织你的包结构,以及发布到 PyPI。

NOTE

如果你只是为自己的项目需要自定义工具,请改看创建自定义工具指南。

工具契约

每个 CrewAI 工具都必须满足以下两种接口之一:

方式 1:继承 BaseTool

继承 crewai.tools.BaseTool 并实现 _run 方法。定义 name、description,并可选地定义 args_schema 用于输入校验。

python
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class GeolocateInput(BaseModel):
    """Input schema for GeolocateTool."""
    address: str = Field(..., description="The street address to geolocate.")

class GeolocateTool(BaseTool):
    name: str = "Geolocate"
    description: str = "Converts a street address into latitude/longitude coordinates."
    args_schema: type[BaseModel] = GeolocateInput

    def _run(self, address: str) -> str:
        # Your implementation here
        return f"40.7128, -74.0060"

方式 2:使用 @tool 装饰器

对于更简单的工具,@tool 装饰器可以把函数变成 CrewAI 工具。该函数必须有文档字符串(用作工具描述)和类型注解。

python
from crewai.tools import tool

@tool("Geolocate")
def geolocate(address: str) -> str:
    """Converts a street address into latitude/longitude coordinates."""
    return "40.7128, -74.0060"

关键要求

无论采用哪种方式,你的工具都必须:

  • 拥有一个 name —— 一个简短、具描述性的标识符。
  • 拥有一个 description —— 告诉智能体何时以及如何使用该工具。这直接影响智能体使用你的工具的效果,因此要清晰且具体。
  • 实现 _run(BaseTool)或提供一个 函数体(@tool)—— 同步执行逻辑。
  • 在所有参数和返回值上使用 类型注解。
  • 返回 字符串 结果,或为结构化结果定义一个可选的 Pydantic 输出 schema。

可选:异步支持

如果你的工具执行 I/O 密集型工作,请实现 _arun 以进行异步执行:

python
class GeolocateTool(BaseTool):
    name: str = "Geolocate"
    description: str = "Converts a street address into latitude/longitude coordinates."

    def _run(self, address: str) -> str:
        # Sync implementation
        ...

    async def _arun(self, address: str) -> str:
        # Async implementation
        ...

可选:使用 args_schema 进行输入校验

定义一个 Pydantic 模型作为你的 args_schema,即可获得自动输入校验和清晰的错误消息。如果你不提供,CrewAI 会根据你的 _run 方法签名推断出来。

python
from pydantic import BaseModel, Field

class TranslateInput(BaseModel):
    """Input schema for TranslateTool."""
    text: str = Field(..., description="The text to translate.")
    target_language: str = Field(
        default="en",
        description="ISO 639-1 language code for the target language.",
    )

建议为已发布的工具提供显式 schema —— 它们能带来更好的智能体行为,并为你的用户提供更清晰的文档。

可选:使用 result_schema 实现类型化输出

如果你的工具返回结构化数据,请定义一个 Pydantic 输出模型。对于已发布的工具来说,这是一个不错的默认做法,因为用户和智能体都可以依赖具名字段。

直接通过 Python 调用时,仍然会收到你的工具返回的值。当智能体使用该工具时,CrewAI 会基于输出模型向智能体发送 JSON。

CrewAI 可以从 Pydantic 返回注解中推断输出 schema:

python
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class GeolocateResult(BaseModel):
    latitude: float = Field(..., description="Latitude in decimal degrees.")
    longitude: float = Field(..., description="Longitude in decimal degrees.")

class GeolocateTool(BaseTool):
    name: str = "Geolocate"
    description: str = "Converts a street address into latitude/longitude coordinates."

    def _run(self, address: str) -> GeolocateResult:
        if "1600 Pennsylvania" in address:
            return GeolocateResult(latitude=38.8977, longitude=-77.0365)
        return GeolocateResult(latitude=40.7128, longitude=-74.0060)

当你的工具返回字典时,请显式设置 result_schema:

python
class GeolocateTool(BaseTool):
    name: str = "Geolocate"
    description: str = "Converts a street address into latitude/longitude coordinates."
    result_schema: type[BaseModel] = GeolocateResult

    def _run(self, address: str) -> dict[str, float]:
        if "1600 Pennsylvania" in address:
            return {"latitude": 38.8977, "longitude": -77.0365}
        return {"latitude": 40.7128, "longitude": -74.0060}

如果希望智能体收到简短的文本摘要而非 JSON,请在 BaseTool 子类上重写 format_output_for_agent。

python
class GeolocateTool(BaseTool):
    name: str = "Geolocate"
    description: str = "Converts a street address into latitude/longitude coordinates."

    def _run(self, address: str) -> GeolocateResult:
        if "1600 Pennsylvania" in address:
            return GeolocateResult(latitude=38.8977, longitude=-77.0365)
        return GeolocateResult(latitude=40.7128, longitude=-74.0060)

    def format_output_for_agent(self, raw_result: object) -> str:
        result = GeolocateResult.model_validate(raw_result)
        return f"Latitude {result.latitude}, longitude {result.longitude}"

该重写只改变智能体看到的内容。直接使用你的包的用户仍然会从 tool.run(...) 收到常规的值。

可选:环境变量

如果你的工具需要 API 密钥或其他配置,请用 env_vars 声明它们,以便用户知道需要设置什么:

python
from crewai.tools import BaseTool, EnvVar

class GeolocateTool(BaseTool):
    name: str = "Geolocate"
    description: str = "Converts a street address into latitude/longitude coordinates."
    env_vars: list[EnvVar] = [
        EnvVar(
            name="GEOCODING_API_KEY",
            description="API key for the geocoding service.",
            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

toml
[project]
name = "crewai-geolocate"
version = "0.1.0"
description = "A CrewAI tool for geolocating street addresses."
requires-python = ">=3.10"
dependencies = [
    "crewai",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

将 crewai 声明为依赖项,这样用户就能自动获得兼容的版本。

__init__.py

重新导出你的工具类,以便用户可以直接导入它们:

python
from crewai_geolocate.tools import GeolocateTool

__all__ = ["GeolocateTool"]

命名约定

  • 包名:使用前缀 crewai-(例如 crewai-geolocate)。这样用户在 PyPI 上搜索时就能发现你的工具。
  • 模块名:使用下划线(例如 crewai_geolocate)。
  • 工具类名:使用 PascalCase 并以 Tool 结尾(例如 GeolocateTool)。

测试你的工具

发布之前,请先验证你的工具能在 crew 中正常工作:

python
from crewai import Agent, Crew, Task
from crewai_geolocate import GeolocateTool

agent = Agent(
    role="Location Analyst",
    goal="Find coordinates for given addresses.",
    backstory="An expert in geospatial data.",
    tools=[GeolocateTool()],
)

task = Task(
    description="Find the coordinates of 1600 Pennsylvania Avenue, Washington, DC.",
    expected_output="The latitude and longitude of the address.",
    agent=agent,
)

crew = Crew(agents=[agent], tasks=[task])
result = crew.kickoff()
print(result)

发布到 PyPI

一旦你的工具通过测试并准备就绪:

bash
# Build the package
uv build

# Publish to PyPI
uv publish

如果你是首次发布,你需要一个 PyPI 账号 和一个 API token。

发布之后

用户可以通过以下方式安装你的工具:

bash
pip install crewai-geolocate

或使用 uv:

bash
uv add crewai-geolocate

然后在他们的 crew 中使用它:

python
from crewai_geolocate import GeolocateTool

agent = Agent(
    role="Location Analyst",
    tools=[GeolocateTool()],
    # ...
)

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。