dify插件简述

插件逻辑

dify 的插件管理中,整个体系逻辑比较混乱,这里使用该文本记录一下 dify 的插件管理逻辑。

插件概述

一个插件以及插件对应的工具的组成部分如下图所示:

---
title: 插件
---
flowchart LR

plugin_id("plugin_id"):::green
organization1("organization"):::animate
plugin_name1("plugin_name"):::animate
slash1("/"):::error

plugin_id --> organization1 --> slash1 --> plugin_name1


plugin_unique_identifier("plugin_unique_identifier"):::green

organization2("organization"):::animate
slash2("/"):::error
symbol2(":"):::error
at2("@"):::error
plugin_name2("plugin_name"):::animate
version1("version"):::animate
checksum1("checksum"):::animate

plugin_unique_identifier --> organization2 --> slash2 --> plugin_name2 --> symbol2 --> version1 --> at2 --> checksum1

provider("full_provider"):::green

organization3("organization"):::animate
slash3_1("/"):::error
plugin_name3("plugin_name"):::animate
slash3_2("/"):::error
provider_name3("provider"):::animate

provider --> organization3 --> slash3_1 --> plugin_name3 --> slash3_2 --> provider_name3

classDef pink 1,fill:#FFCCCC,stroke:#333, color: #fff, font-weight:bold;
classDef green fill: #696,color: #fff,font-weight: bold;
classDef purple fill:#969,stroke:#333, font-weight: bold;
classDef error fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
classDef coral fill:#f9f,stroke:#333,stroke-width:4px;
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;

插件在数据库中的管理逻辑

插件在数据库中,会涉及到以下几个重要的表:

plugin_declarations/plugins

plugin_declarations 表和 plugins 中,声明了插件的相关信息,其中最主要的字段有三个:

  1. plugin_unique_identifier 插件的唯一ID,这个ID存在唯一索引不能重复;
  2. plugin_id 是标志插件的类型,但是他只是标志了插件的类型,实际上可以同时存在多个不同的版本;
  3. declaration 声明了插件的元数据信息。例如,他是一个 model 还是 tools 等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE public.plugin_declarations (
id uuid DEFAULT public.uuid_generate_v4() NOT NULL,
created_at timestamp with time zone,
updated_at timestamp with time zone,
plugin_unique_identifier character varying(255),
plugin_id character varying(255),
declaration text
);


ALTER TABLE ONLY public.plugin_declarations
ADD CONSTRAINT plugin_declarations_pkey PRIMARY KEY (id);

ALTER TABLE ONLY public.plugin_declarations
ADD CONSTRAINT uni_plugin_declarations_plugin_unique_identifier UNIQUE (plugin_unique_identifier);

CREATE INDEX idx_plugin_declarations_plugin_id ON public.plugin_declarations USING btree (plugin_id);

例如,对于 langgenius/dingtalk 这个插件的 0.0.4 版本,他的数据如下:

1
2
3
SELECT *
FROM plugin_declarations
WHERE plugin_unique_identifier = 'langgenius/dingtalk:0.0.4@fe34a564a52c3a295e60ed97280ef47abd459d96310239cf9df5f04a5b1681b3'\G
1
2
3
4
5
6
7
-[ RECORD 1 ]-------------------------
id | ca7ac146-7eec-4194-9597-ffd0097b382d
created_at | 2025-03-25 20:30:53.741513+08
updated_at | 2025-03-25 20:30:53.741513+08
plugin_unique_identifier | langgenius/dingtalk:0.0.4@fe34a564a52c3a295e60ed97280ef47abd459d96310239cf9df5f04a5b1681b3
plugin_id | langgenius/dingtalk
declaration | {"version":"0.0.4","type":"plugin","author":"langgenius","name":"dingtalk","label":{"en_US":"DingTalk","zh_Hans":"钉钉","pt_BR":"DingTalk"},"description":{"en_US":"DingTalk group robot","zh_...

在这里,我们需要仔细的查看一下 declaration 的内容,我保留了主干内容后如下:

由于这是一个工具类型的插件,所以从声明中我们可以看得出来,他在配置文件中声明了他在这个工具中所包含的 action,并且指明了每个 action 的输入和输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"version": "0.0.4",
"type": "plugin",
"author": "langgenius",
"name": "dingtalk",
"label":{},
"description":{},
"resource":{},
"plugins":
{
"tools":
[
"provider/dingtalk.yaml"
],
},
"tool":
{
"identity":{},
"credentials_schema":[],
"tools":
[
{
"identity":{},
"description":{},
"parameters":
[
{
"name": "access_token",
"type": "secret-input",
"required": true,
},
// ...
],
"output_schema": null,
"has_runtime_parameters": false
}
]
}
}

整体的结构如下:

---
title: 插件配置
---
flowchart LR

declaration("declaration"):::purple

tool("tool"):::purple
tools("tools"):::purple

subgraph tool_array
    tool_01("tool_1"):::pink
    tool_02("tool_2"):::pink
    tool_03("..."):::pink
    tool_04("tool_n"):::pink
end

subgraph tool_info
    direction LR
    identity("identity"):::green
    description("description"):::green
    parameters("parameters"):::pink
    output_schema("output_schema"):::green
    has_runtime_parameters("has_runtime_parameters"):::green
end

subgraph parameter_info_list
    direction LR
    param_01("param_1"):::green
    param_02("param_2"):::green
    param_03("..."):::green
    param_04("param_N"):::green
end

subgraph parameter_info
    direction LR
    name("name"):::pink
    required("required"):::pink
    human_description("human_description"):::pink
    default("default"):::pink
end

declaration --> tool --> tools --> tool_array

tool_01 --> tool_info
tool_02 --> tool_info
tool_03 --> tool_info
tool_04 --> tool_info

parameters --> parameter_info_list
param_01 --> parameter_info
param_02 --> parameter_info
param_03 --> parameter_info
param_04 --> parameter_info

classDef pink 1,fill:#FFCCCC,stroke:#333, color: #fff, font-weight:bold;
classDef green fill: #696,color: #fff,font-weight: bold;
classDef purple fill:#969,stroke:#333, font-weight: bold;
classDef error fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
classDef coral fill:#f9f,stroke:#333,stroke-width:4px;
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;

插件安装

dify 执行安装之后,会根据对应的类型,在对应的表中写入一行数据,这个比较直观,直接从表名就可以看出来具体类型,这里就不展开说了:唯一需要注意的是,有的插件可能不能提供工具调用,就不会在 tool_installations 插入数据。

---
title: 插件
---
flowchart LR

classDef pink 1,fill:#FFCCCC,stroke:#333, color: #fff, font-weight:bold;
classDef green fill: #696,color: #fff,font-weight: bold;
classDef purple fill:#969,stroke:#333, font-weight: bold;
classDef error fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
classDef coral fill:#f9f,stroke:#333,stroke-width:4px;
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;


plugin_declarations("plugin_id"):::green

agent_strategy_installations("agent_strategy_installations"):::animate
ai_model_installations("ai_model_installations"):::animate
plugin_installations("plugin_installations"):::animate
tool_installations("tool_installations"):::animate

plugin_declarations --> agent_strategy_installations
plugin_declarations --> ai_model_installations
plugin_declarations --> plugin_installations
plugin_declarations --> tool_installations

插件和工具有什么区别?

先说结论:工具是插件的一个子集,看我们前面的 json 我们可以看到,一个插件的 declaration 中可能包含了一个 tools 数组,其实这个数组我个人倾向于是dify在实现的过程中实现了错误的逻辑。

因为这个 tools 在前端展示时显示的名称为 action,代表了一个函数调用,我认为这里命名为 actions 是更合理的。

在我们安装插件的过程中,如果该插件声明了 tools,那么会向 tool_installations 表写入一行数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE public.tool_installations (
id uuid DEFAULT public.uuid_generate_v4() NOT NULL,
created_at timestamp with time zone,
updated_at timestamp with time zone,
tenant_id uuid NOT NULL,
provider character varying(127) NOT NULL,
plugin_unique_identifier character varying(255),
plugin_id character varying(255)
);

ALTER TABLE public.tool_installations OWNER TO postgres;
ALTER TABLE ONLY public.tool_installations
ADD CONSTRAINT tool_installations_pkey PRIMARY KEY (id);
CREATE INDEX idx_tool_installations_plugin_id ON public.tool_installations USING btree (plugin_id);
CREATE INDEX idx_tool_installations_plugin_unique_identifier ON public.tool_installations USING btree (plugin_unique_identifier);
CREATE INDEX idx_tool_installations_provider ON public.tool_installations USING btree (provider);
CREATE INDEX idx_tool_installations_tenant_id ON public.tool_installations USING btree (tenant_id);

可以看到,在这里除了 plugin_idplugin_unique_identifier 这两个关键字段之外,我们还多出来了一个额外的 provider 字段。

再回到我们前面的描述,我们会发现 dify 提供了一个接口用于查询 tool

1
curl 'https://test-aiflow.woa.com/backend-api/console/api/workspaces/current/tool-provider/builtin/aiflow/google_api/google_api/tools'

可以看到在这个 curl 中我们的参数是 /aiflow/google_api/google_api 而不是单纯的 ``/aiflow/google_api`