AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


扩展 Dify 内置工具集,打造自己独有的 AI Agent 工作站
发布日期:2024-08-08 17:03:46 浏览次数: 2889 来源:数翼


Dify 提供了很多的内置工具,让我们在创建 Agent 的时候可以做更多的事情。 看着琳琅满目的工具,我们是不是也想接入自己独特的工具呢?

今天演示下如何构建自己的工具并添加到 Dify 内置工具集里面。

运行 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

这个 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-inputtext-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 表示表单类型,目前支持llmform两种类型,分别对应 Agent 自行推理和前端填写。

Agent 自行推理的意思就是有 LLM 确定字段的值。

工具的实现类 shuyi_test.py 最简单的莫过于直接返回一个固定的消息。

from typing import AnyUnion

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[strAny],
                
) -> 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 AnyUnion

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[strAny],
                
) -> 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 消息

文档上说 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+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询