Files
dify-docs/plugin-dev-zh/0222-tool-plugin.mdx
2025-09-30 21:07:13 +08:00

387 lines
16 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
dimensions:
type:
primary: implementation
detail: standard
level: intermediate
standard_title: Tool Plugin
language: zh
title: 工具插件
description: 本文档介绍如何开发 Dify 的工具插件,以 Google Search 为例实现了一个完整的工具插件开发流程。内容包括插件初始化、模板选择、模板选择、工具供应商配置文件定义、添加第三方服务凭证、工具功能代码实现、调试和打包发布等完整环节。
---
工具指的是能够被 Chatflow / Workflow / Agent 类型应用所调用的第三方服务,提供完整的 API 实现能力,用于增强 Dify 应用的能力。例如为应用添加在线搜索、图片生成等额外功能。
![Tool Plugin Example](https://assets-docs.dify.ai/2024/12/7e7bcf1f9e3acf72c6917ea9de4e4613.png)
在本文中,**"工具插件"** 指的是一个完整的项目,其中包含工具供应商文件、功能代码等结构。一个工具供应商内允许包含多个 Tools可以理解为单个工具中提供的额外功能结构如下
```
- 工具供应商
- Tool A
- Tool B
```
![工具插件结构](https://assets-docs.dify.ai/2025/02/60c4c86a317d865133aa460592eac079.png)
本文将以 `Google Search` 为例,介绍如何快速开发一个工具插件。
### 前置准备
- Dify 插件脚手架工具
- Python 环境,版本号 ≥ 3.12
关于如何准备插件开发的脚手架工具,详细说明请参考[初始化开发工具](/plugin-dev-zh/0221-initialize-development-tools)。如果你是初次开发插件,建议先阅读[Dify 插件开发Hello World 指南](/plugin-dev-zh/0211-getting-started-dify-tool)。
### 创建新项目
运行脚手架命令行工具,创建一个新的 dify 插件项目。
```bash
./dify-plugin-darwin-arm64 plugin init
```
如果你已将该二进制文件重命名为 `dify` 并拷贝至 `/usr/local/bin` 路径下,可以运行以下命令创建新的插件项目:
```bash
dify plugin init
```
> 下文将使用 `dify` 作为命令行示例。如遇到问题,请将 `dify` 命令替换为命令行工具的所在路径。
### 选择插件类型和模板
脚手架工具内的所有模板均已提供完整的代码项目。在本文实例中,选择 `Tool` 插件。
> 如果你已熟悉插件开发,无需借助模板,可参考[通用规范](/plugin-dev-zh/0411-general-specifications)指引完成不同类型的插件开发。
![插件类型:工具](https://assets-docs.dify.ai/2024/12/dd3c0f9a66454e15868eabced7b74fd6.png)
#### 配置插件权限
插件还需读取 Dify 平台的权限,为该示例插件授予以下权限:
- Tools
- Apps
- 启用持久化存储 Storage分配默认大小存储
- 允许注册 Endpoint
> 在终端内使用方向键选择权限,使用 "Tab" 按钮授予权限。
勾选所有权限项后,轻点回车完成插件的创建。系统将自动生成插件项目代码。
![插件权限](https://assets-docs.dify.ai/2024/12/9cf92c2e74dce55e6e9e331d031e5a9f.png)
### 开发工具插件
#### 1. 创建工具供应商文件
工具供应商文件为 yaml 格式文件,可以理解为工具插件的基础配置入口,用于向工具提供必要的授权信息。
前往插件模板项目中的 `/provider` 路径,将其中的 yaml 文件重命名为 `google.yaml`。该 `yaml` 文件将包含工具供应商的信息,包括供应商名称、图标、作者等详情。这部分信息将在安装插件时进行展示。
**示例代码**
```yaml
identity: # 工具供应商的基本信息
author: Your-name # 作者
name: google # 名称,唯一,不允许和其他供应商重名
label: # 标签,用于前端展示
en_US: Google # 英文标签
zh_Hans: Google # 中文标签
description: # 描述,用于前端展示
en_US: Google # 英文描述
zh_Hans: Google # 中文描述
icon: icon.svg # 工具图标,需要放置在 _assets 文件夹下
tags: # 标签,用于前端展示
- search
```
确保该文件路径位于 `/tools` 目录,完整的路径如下:
```yaml
plugins:
tools:
- 'google.yaml'
```
其中 `google.yaml` 文件需要使用其在插件项目的绝对路径。在本例中它位于项目根目录。YAML 文件中的 identity 字段解释如下:`identity` 包含了工具供应商的基本信息,包括作者、名称、标签、描述、图标等。
- 图标需要属于附件资源,需要将其放置在项目根目录的 `_assets` 文件夹下。
- 标签可以帮助用户通过分类快速找到插件,以下是目前所支持的所有标签。
```python
class ToolLabelEnum(Enum):
SEARCH = 'search'
IMAGE = 'image'
VIDEOS = 'videos'
WEATHER = 'weather'
FINANCE = 'finance'
DESIGN = 'design'
TRAVEL = 'travel'
SOCIAL = 'social'
NEWS = 'news'
MEDICAL = 'medical'
PRODUCTIVITY = 'productivity'
EDUCATION = 'education'
BUSINESS = 'business'
ENTERTAINMENT = 'entertainment'
UTILITIES = 'utilities'
OTHER = 'other'
```
#### **2. 补全第三方服务凭据**
为了便于开发,选择采用第三方服务 `SerpApi` 所提供的 Google Search API 。 `SerpApi` 要求填写 API Key 进行使用,因此需要在 `yaml` 文件内添加 `credentials_for_provider` 字段。
完整代码如下:
```yaml
identity:
author: Dify
name: google
label:
en_US: Google
zh_Hans: Google
pt_BR: Google
description:
en_US: Google
zh_Hans: GoogleSearch
pt_BR: Google
icon: icon.svg
tags:
- search
credentials_for_provider: #添加 credentials_for_provider 字段
serpapi_api_key:
type: secret-input
required: true
label:
en_US: SerpApi API key
zh_Hans: SerpApi API key
placeholder:
en_US: Please input your SerpApi API key
zh_Hans: 请输入你的 SerpApi API key
help:
en_US: Get your SerpApi API key from SerpApi
zh_Hans: 从 SerpApi 获取您的 SerpApi API key
url: https://serpapi.com/manage-api-key
tools:
- tools/google_search.yaml
extra:
python:
source: google.py
```
- 其中 `credentials_for_provider` 的子级结构需要满足 [通用规范](/plugin-dev-zh/0411-general-specifications) 的要求。
- 需要指定该供应商包含了哪些工具。本示例仅包含了一个 `tools/google_search.yaml` 文件。
- 作为供应商,除了定义其基础信息外,还需要实现一些它的代码逻辑,因此需要指定其实现逻辑,在本例子中,将功能的代码文件放在了 `google.py` 中,但是暂时不实现它,而是先编写 `google_search` 的代码。
#### 3. 填写工具 yaml 文件
一个工具插件下可以有多个工具功能,每个工具功能需要一个 `yaml` 文件进行描述,包含工具功能的基本信息、参数、输出等。
仍以 `GoogleSearch` 工具为例,在 `/tools`文件夹内新建一个 `google_search.yaml` 文件。
```yaml
identity:
name: google_search
author: Dify
label:
en_US: GoogleSearch
zh_Hans: 谷歌搜索
pt_BR: GoogleSearch
description:
human:
en_US: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
zh_Hans: 一个用于执行 Google SERP 搜索并提取片段和网页的工具。输入应该是一个搜索查询。
pt_BR: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
llm: A tool for performing a Google SERP search and extracting snippets and webpages.Input should be a search query.
parameters:
- name: query
type: string
required: true
label:
en_US: Query string
zh_Hans: 查询语句
pt_BR: Query string
human_description:
en_US: used for searching
zh_Hans: 用于搜索网页内容
pt_BR: used for searching
llm_description: key words for searching
form: llm
extra:
python:
source: tools/google_search.py
```
- `identity` 包含了工具的基本信息,包括名称、作者、标签、描述等。
- `parameters` 参数列表
- `name` (必填)参数名称,唯一,不允许和其他参数重名。
- `type` (必填)参数类型,目前支持`string`、`number`、`boolean`、`select`、`secret-input`、`file`、`files`、`model-selector`、`app-selector` 九种类型,分别对应字符串、数字、布尔值、下拉框、加密输入框、文件、文件集、模型选择、应用选择,对于敏感信息,请使用 `secret-input` 类型。
- `label`(必填)参数标签,用于前端展示。
- `form` (必填)表单类型,目前支持`llm`、`form`两种类型。
- 在 Agent 应用中,`llm` 表示该参数 LLM 自行推理,`form` 表示要使用该工具可提前设定的参数。
- 在 Workflow 应用中,`llm`和`form` 均需要前端填写,但 `llm` 的参数会做为工具节点的输入变量。
- `required` 是否必填
- 在 `llm` 模式下,如果参数为必填,则会要求 Agent 必须要推理出这个参数。
- 在 `form` 模式下,如果参数为必填,则会要求用户在对话开始前在前端填写这个参数。
- `options` 参数选项
- 在 `llm` 模式下Dify 会将所有选项传递给 LLMLLM 可以根据这些选项进行推理。
- 在 `form` 模式下,`type` 为 `select` 时,前端会展示这些选项。
- `default` 默认值。
- `min` 最小值,当参数类型为`number`时可以设定。
- `max` 最大值,当参数类型为`number`时可以设定。
- `human_description` 用于前端展示的介绍,支持多语言。
- `placeholder` 字段输入框的提示文字,在表单类型为`form`,参数类型为`string`、`number`、`secret-input`时,可以设定,支持多语言。
- `llm_description` 传递给 LLM 的介绍。为了使得 LLM 更好理解这个参数,请在这里写上关于这个参数尽可能详细的信息,以便 LLM 能够理解该参数。
#### 4. 准备工具代码
填写工具的配置信息以后,可以开始编写工具的功能代码,实现工具的逻辑目的。在`/tools`目录下创建`google_search.py`,内容如下:
```python
from collections.abc import Generator
from typing import Any
import requests
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
SERP_API_URL = "https://serpapi.com/search"
class GoogleSearchTool(Tool):
def _parse_response(self, response: dict) -> dict:
result = {}
if "knowledge_graph" in response:
result["title"] = response["knowledge_graph"].get("title", "")
result["description"] = response["knowledge_graph"].get("description", "")
if "organic_results" in response:
result["organic_results"] = [
{
"title": item.get("title", ""),
"link": item.get("link", ""),
"snippet": item.get("snippet", ""),
}
for item in response["organic_results"]
]
return result
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
params = {
"api_key": self.runtime.credentials["serpapi_api_key"],
"q": tool_parameters["query"],
"engine": "google",
"google_domain": "google.com",
"gl": "us",
"hl": "en",
}
response = requests.get(url=SERP_API_URL, params=params, timeout=5)
response.raise_for_status()
valuable_res = self._parse_response(response.json())
yield self.create_json_message(valuable_res)
```
该例子的含义为请求 `serpapi`,并使用 `self.create_json_message` 返回一串 `json` 的格式化数据,如果想了解更多的返回数据类型,可以参考[远程调试插件](/plugin-dev-zh/0411-remote-debug-a-plugin)和[持久化存储 KV](/plugin-dev-zh/0411-persistent-storage-kv)文档。
#### 5. 完成工具供应商代码
最后需要创建一个供应商的实现代码,用于实现凭据验证逻辑。如果凭据验证失败,将会抛出`ToolProviderCredentialValidationError`异常。验证成功后,将正确请求 `google_search` 工具服务。
在 `/provider` 目录下创建 `google.py` 文件,代码的内容如下:
```python
from typing import Any
from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
from tools.google_search import GoogleSearchTool
class GoogleProvider(ToolProvider):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
for _ in GoogleSearchTool.from_credentials(credentials).invoke(
tool_parameters={"query": "test", "result_type": "link"},
):
pass
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
```
### 调试插件
插件开发完成后接下来需要测试插件是否可以正常运行。Dify 提供便捷地远程调试方式,帮助你快速在测试环境中验证插件功能。
前往["插件管理"](https://cloud.dify.ai/plugins)页获取远程服务器地址和调试 Key。
![Remote Debug Key](https://assets-docs.dify.ai/2024/12/053415ef127f1f4d6dd85dd3ae79626a.png)
回到插件项目,拷贝 `.env.example` 文件并重命名为 `.env`,将获取的远程服务器地址和调试 Key 等信息填入其中。
`.env` 文件:
```bash
INSTALL_METHOD=remote
REMOTE_INSTALL_URL=debug.dify.ai:5003
REMOTE_INSTALL_KEY=********-****-****-****-************
```
运行 `python -m main` 命令启动插件。在插件页即可看到该插件已被安装至 Workspace 内,团队中的其他成员也可以访问该插件。
![](https://assets-docs.dify.ai/2024/11/0fe19a8386b1234755395018bc2e0e35.png)
### 打包插件(可选)
确认插件能够正常运行后,可以通过以下命令行工具打包并命名插件。运行以后你可以在当前文件夹发现 `google.difypkg` 文件,该文件为最终的插件包。
```bash
# 将 ./google 替换为插件项目的实际路径
dify plugin package ./google
```
恭喜,你已完成一个工具类型插件的完整开发、调试与打包过程!
### 发布插件(可选)
如果想要将插件发布至 Dify Marketplace请确保你的插件遵循了[发布到 Dify 市场](/plugin-dev-zh/0322-release-to-dify-marketplace)中的规范。审核通过后,代码将合并至主分支并自动上线至 [Dify Marketplace](https://marketplace.dify.ai/)。
[发布概览](/plugin-dev-zh/0321-release-overview)
### 探索更多
#### **快速开始:**
- [开发 Extension 插件](/plugin-dev-zh/9231-extension-plugin)
- [开发 Model 插件](/plugin-dev-zh/0211-getting-started-new-model)
- [Bundle 插件:将多个插件打包](/plugin-dev-zh/9241-bundle)
#### **插件接口文档:**
- [一般规范定义](/plugin-dev-zh/0411-general-specifications) - Manifest 结构和工具规范
- [端点](/plugin-dev-zh/0432-endpoint) - Endpoint 详细定义
- [反向调用](/plugin-dev-zh/9241-reverse-invocation) - 反向调用 Dify 能力
- [模型架构](/plugin-dev-zh/0412-model-schema) - 模型
- [Agent 插件](/plugin-dev-zh/9232-agent) - 扩展 Agent 策略
## 下一步学习
- [远程调试插件](/plugin-dev-zh/0411-remote-debug-a-plugin) - 了解更高级的调试技巧
- [持久化存储](/plugin-dev-zh/0411-persistent-storage-kv) - 学习如何在插件中使用数据存储
- [Slack 机器人插件开发示例](/plugin-dev-zh/0432-develop-a-slack-bot-plugin) - 查看更复杂的插件开发案例
- [工具插件](/plugin-dev-zh/0411-tool) - 探索工具插件的高级功能
{/*
Contributing Section
DO NOT edit this section!
It will be automatically generated by the script.
*/}
---
[编辑此页面](https://github.com/langgenius/dify-docs/edit/main/plugin-dev-zh/0222-tool-plugin.mdx) | [提交问题](https://github.com/langgenius/dify-docs/issues/new?template=docs.yml)