微信扫码
与创始人交个朋友
我要投稿
Dify 提供了很多的内置工具,让我们在创建 Agent 的时候可以做更多的事情。 看着琳琅满目的工具,我们是不是也想接入自己独特的工具呢?
今天演示下如何构建自己的工具并添加到 Dify 内置工具集里面。
如何运行 Dify(源码和容器运行)我这里就不赘述了,大家可以参考前面这两篇文章:
• Dify + FastAPI 创建自定义工具
• Dify+谷歌翻译 使用中文提示词实现本地文生图
首先我们了解下 Dify 内置工具的两个概念:厂商
和 工具
。
你要扩展工具的话,首先要注册一个厂商,一个厂商下面可以有多个工具,在你选择工具的时候,工具会按照厂商分组。
扩展 Dify 工具和厂商分了两部分:
• 使用 yaml
文件描述其参数的功能
• 使用 python
文件编写厂商注册函数和工具实现
不如定义了两个工具,最终的目录结构大概是这样:
├── _assets
│ └── icon.svg
├── shuyi.py
├── shuyi.yaml
└── tools
├── shuyi_excel.py
├── shuyi_excel.yaml
├── shuyi_test.py
└── shuyi_test.yaml
这个 yaml 将包含工具供应商的信息,包括供应商名称、图标、作者等详细信息,以帮助前端灵活展示。
在 core/tools/provider/builtin
目录下创建一个文件夹,比如 shuyi
,然后软件 shuyi.yaml
。
在文件里面增加关于工具的描述、以及展示的图标。
identity:
author: Shuyi
name: shuyi
label:
en_US: Shuyi
zh_Hans: 数翼
pt_BR: Shuyi
description:
en_US: Shuyi tools
zh_Hans: 数翼自定义工具集
pt_BR: Shuyi tools
icon: icon.svg
tags:
- search
- productivity
如果工具需要授权,那么我们还需要定义授权参数。
授权一般都是填写这个工具的密钥(API Key),比如谷歌搜索、DALL-E 绘画,通过授权可以完成工具的安全调用。
如果配置了授权在工具详情就会出现一个授权按钮:
点击可以看到授权参数的填写:
我们自定义工具增加两个授权参数:
• 服务器地址:我们自己部署服务的地址
• API Key:用于验证调用的合法性
定义授权参数 shuyi_api_key
和 shuyi_api_url
,表单类型分别是secret-input
和text-input
。Yaml 增加如下内容:
credentials_for_provider:
shuyi_api_key:
type: secret-input
required: true
default: "123456"
label:
en_US: Shuyi API key
zh_Hans: Shuyi API key
pt_BR: Shuyi API key
placeholder:
en_US: Please input your Shuyi API key
zh_Hans: 请输入你的 Shuyi API key
pt_BR: Please input your Shuyi API key
help:
en_US: Get your Shuyi API key from Shuyi
zh_Hans: 从 Shuyi 获取您的 Shuyi API key
pt_BR: Get your Shuyi API key from Shuyi
url: https://blog.tupu.work/
shuyi_api_url:
type: text-input
required: true
default: http://localhost:8000
label:
en_US: Shuyi API url
zh_Hans: Shuyi API 地址
pt_BR: Shuyi API url
placeholder:
en_US: Please input your Shuyi API url
zh_Hans: 请输入你的 Shuyi API 服务地址
pt_BR: Please input your Shuyi API url
help:
en_US: Get your Shuyi API url from Shuyi
zh_Hans: 从 Shuyi 获取您的 Shuyi API url
pt_BR: Get your Shuyi API url from Shuyi
url: https://blog.tupu.work/
这时候我们就可以看到工具的授权参数了:
点击可以看到授权表单:
自定义工具当然不是必须需要授权或者调用服务的。
你可以把所有逻辑都在你的工具代码里面实现,这样比较简单,但是也有一些弊端:
• 工具代码过于复杂,耦合性过高,也不利于测试
• 插件依赖等问题可能和 Dify 冲突,升级安装也不好保证
• 在 Dify 中计算不方便分布式扩容
如果我们的工具功能比较简单,可以不用认证这些的,就比如 JSON 相关的工具:
如果要实现的工具功能复杂,还是建议单独写一个服务,让 Dify 来调用。
工具实现也是分为定义文件 Yaml 和功能实现类两部分。
我们再 shuyi/tools
目录下创建 shuyi_test.yaml
文件如下,内容主要就是工具描述、以及参数两部分。
identity:
name: shuyi_test
author: Shuyi
label:
en_US: ShuyiTest
zh_Hans: 数翼工具测试
pt_BR: ShuyiTest
description:
human:
en_US: A tool for testing Dify message type and input parameters.Input should be a search query.
zh_Hans: 用于测试Dify消息类型和输入参数的工具。
pt_BR: A tool for testing Dify message type and input parameters.Input should be a search query.
llm: A tool for testing Dify message type and input parameters.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
Dify 支持的参数有四种类型:
• string
• number
• boolean
• select
这块儿我们不在赘述,需要的话可以参考官方文档[2]。
值得注意的是 form
这个参数, form
表示表单类型,目前支持llm
、form
两种类型,分别对应 Agent 自行推理和前端填写。
Agent 自行推理的意思就是有 LLM 确定字段的值。
工具的实现类 shuyi_test.py
最简单的莫过于直接返回一个固定的消息。
from typing import Any, Union
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool
class ShuyiTestTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
return self.create_link_message('hello')
当然现实中肯定不会这么简单,我们一般要获取插件认证的参数:
api_url = self.runtime.credentials['shuyi_api_url'],
params = {
"api_key": self.runtime.credentials['shuyi_api_key'],
"type": tool_parameters['type'],
"query": tool_parameters['query'],
}
调用外部服务:
response = requests.get(url=api_url, params=params)
解析调用结果的服务返回一般逻辑比较复杂,我们可以使用一个单独的函数实现:
def _parse_response(self, response: dict) -> dict:
result = {}
# logics
return result
然后返回:
response.raise_for_status()
valuable_res = self._parse_response(response.json())
return self.create_json_message(valuable_res)
我写了这样一个模板,大家可以基于这个模板来创建自己的工具:
from typing import Any, Union
import requests
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool
class ShuyiTestTool(BuiltinTool):
def _parse_response(self, response: dict) -> dict:
result = {}
# logics
return result
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
api_url = self.runtime.credentials['shuyi_api_url'],
params = {
"api_key": self.runtime.credentials['shuyi_api_key'],
"type": tool_parameters['type'],
"query": tool_parameters['query'],
}
response = requests.get(url=api_url, params=params)
response.raise_for_status()
valuable_res = self._parse_response(response.json())
return self.create_json_message(valuable_res)
Dify 提供了几种消息类型[3], 在我看来都是文本(Markdown)类型的封装,下面是对应的封装方法:
• 图片:create_image_message(self, image: str, save_as: str = '')
• 链接:create_link_message(self, link: str, save_as: str = '')
• 文本:create_text_message(self, text: str, save_as: str = '')
• Blob: create_blob_message(self, blob: bytes, meta: dict = None, save_as: str = '')
• JSON: create_json_message(self, object: dict)
当然,也支持返回多个消息,用数组表示即可。
文本类型其实是 Markdown
。
我们创建一个包含图片、列表、标题的 Markdown 消息:
return self.create_text_message(
"""### User message
%s
### 生成式 AI 应用创新引擎
开源的 LLM 应用开发平台,轻松构建和运营生成式 AI 原生应用。
- 生成式 AI
- 应用开发框架
#### RAG Pipeline
""" % params['query'])
效果如下:
图片消息需要传递一个 地址:
image_path = 'https://buffer.com/cdn-cgi/image/w=1000,fit=contain,q=90,f=auto/library/content/images/size/w1200/2023/10/free-images.jpg'
return self.create_image_message(image_path)
效果如下:
文档上说 Blob 消息可以返回文件的原始数据,如图片、音频、视频、PPT、Word、Excel 等。
但是实际测试下来效果不太对。
with open(file_path, 'rb') as mp4:
byte_arr = mp4.read()
return self.create_blob_message(
blob=byte_arr,
meta={
# 'mime_type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'mime_type': 'video/mp4',
'file_name': 'xxx.mp4',},
save_as=self.VARIABLE_KEY.IMAGE.value
)
效果并没有显示出文件内容,或者下载链接:
经过尝试,如果想要返回文档,发现只有使用 Markdown 来实现一个下载超链接。
[something.xlsx](https://yourhost/xxx.xlsx
连接类型的消息只需要出入一个地址,
return self.create_link_message(link)
返回的内容就是:Link:
后面跟上你的链接:
Dify 还支持多条消息的返回,使用数组即可:
messages = [
self.create_text_message("""### User message ...""" % params['query']),
self.create_json_message({'result': 'success', 'text': 'This is a message'}),
self.create_image_message(image_path),
self.create_link_message(link),
...
]
效果也是多条消息的拼接:
当我们编写好工具类,重启服务就可以使用刚刚创建的工具类了。
添加节点的时候找到并选择自己的工具:
就可以和 Dify 提供的默认工具一样用了。
相比于在 UI 上创建基于 OpenAPI 的自定义工具,通过代码来扩展供应商和工具集更使能方便我们完成 工具的积累和传播,在使用上也有更大的可能性。
通过扩展工具,基于 Dify 打造自己独特的 AI 工作站,同时还可以复用自己之前的工具积累,而不仅仅是通用的 AI Agent 工作站。
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-04-25
2024-04-24
2024-07-20
2024-07-16
2024-05-08
2024-05-07
2024-05-09
2024-06-21
2024-04-25
2024-08-06