AI知识库

53AI知识库

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


独立、灵活、可扩展:Dify 插件机制的设计与实现

发布日期:2025-03-12 17:40:29 浏览次数: 1534 来源:Dify
推荐语

探索Dify插件机制,解锁系统灵活性和可扩展性的新高度。

核心内容:
1. Dify插件机制的核心价值与系统变革
2. 插件化带来的用户体验与技术实现细节
3. 针对用户需求,实现Dify工具与模型的独立安装与按需选择

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

Dify v1.0.0 版本的发布,标志着插件机制正式成为系统核心组成部分。作为该机制的主要设计者及 Dify 后端工程师 Yeuoly,我将带大家了解插件在 Dify 系统中的作用与价值。插件机制的核心在于将 Dify 中可横向扩展的模块解耦并独立为运行时,这带来了几个显著的变化:

  • 模块解耦:在插件化之前,Dify 中的模型和工具需要与 Dify 一起全量安装,代码与主仓库紧密耦合。而插件化后,这些模块变成了可以独立安装、卸载和运行的插件包,极大提升了灵活性。
  • 插件市场与分享机制:引入了插件市场,用户和社区可以自由创作并分享插件。
  • 插件 Endpoint 新增了 Extension 插件 Endpoint,这使得更多实际场景能够在 Dify 内部闭环落地。

虽然这些变化在用户体验上是显而易见的,但对于大多数用户来说,插件机制的技术实现仍然是一个相对模糊的黑盒。本文将带你逐步深入了解插件机制的设计理念、用户价值和技术实现细节。

用户需求分析

在设计插件机制之前,我们主要面临以下几个问题:

  1. 代码强耦合:在 Dify 中添加新模型和工具的过程非常繁琐,且会带来过多的工程依赖,导致工具和模型的版本管理问题。
  2. 用户需求无法闭环:某些需求,例如接入 IM 服务,必须在 Dify 之外再封装一层服务。
  3. 定制化模块固定性:例如,Dify 的 PDF 解析器效果不理想,且 RAG 等定制化模块无法灵活调整。

针对这些问题,我们决定实现一套统一的框架,拆分 Dify 的工具和模型,使它们可以独立安装并按需选择。同时,文档解析器、OCR 等 RAG 相关的功能也实现插件化,从而满足不同的场景需求。对于 Dify 内无法闭环的场景,插件机制通过开放接口实现与外部系统的对接,比如支持对接 IM 平台的 Outgoing Webhook。

这些产品需求看起来比较简单,但在工程实现过程中却极具挑战。在初期的设计阶段不到一周,我们就遇到了以下一系列问题:

  • 多 Workspace 设计:Dify 是多 Workspace 的设计,这意味着不能仅仅通过将 Python 源码挂载至 tools/models 目录的方式来实现功能,这样做还会面临依赖冲突的问题。
  • 插件环境一致性:我们希望同一个插件在不同的环境中表现一致。虽然 Docker 可以解决这个问题,但为每个插件分配一个 Docker 容器将大大增加部署复杂度。
  • 高并发的云服务负载:Dify 的 SaaS 服务有几十万用户,如果每 10 个用户就有一个自定义插件,Dify 将面临数万个插件的运行时负载,这对云服务成本造成很大压力。
  • 插件开发调试:作为开发者,每次修改代码都要重新打包并安装插件,同时日志也要跑到 Dify 后台,这会极大影响了开发体验。
  • 插件长期运行:例如,用户需要长期运行一个 HTTP 服务器来监听 IM 平台的 Webhook 事件,Dify 插件是否应该支持?

解决方案

调试体验

在考虑如何实现插件机制之前,我们首先思考了如何优化调试体验,尤其是针对开发者而言。理想的调试体验应该具备以下两个要求:

  1. 所见即所得:每次修改代码后,不需要安装过程,直接在 Dify 中生效。
  2. 本地调试:插件的代码需要在本地运行,以便能够通过断点等方式调试,查错过程更加便捷。

我们参考了老牌调试器如 GDB 的设计,采用了调试器和运行时分离的方式:调试器等待运行时主动连接。一旦连接建立,本地插件就可以与 Dify 建立长连接,Dify 会将其视为已安装插件,标记为调试模式。用户请求通过长连接转发给本地插件,插件的返回结果也通过长连接发送给 Dify,从而实现了流畅的调试体验。

然而,这种设计面临一个问题:长连接是带状态的,而 Dify 目前的服务是无状态的。在 Kubernetes 集群中,负载均衡会将请求路由到不同的 Dify Pod 上。例如,Plugin 1 连接到 Dify 1,Plugin 2 连接到 Dify 2。当用户请求调用 Plugin 1 时,请求可能会被负载均衡路由到 Dify 2,导致用户无法访问 Plugin 1。因此,我们需要在后续实现流量转发机制来解决这一问题。

Endpoint 插件

我们调研了许多现有的即时通讯(IM)工具和办公协作软件,并综合当前需求,明确了要解决的关键问题:如何让 Dify 接收来自这些平台的 Webhook 请求,并让插件能够处理这些 HTTP 请求。例如,使用 Dify 的 App 来处理用户消息。

为了解决这个问题,我们设计了生成随机 URL 的机制,并将该 URL 与 Discord 等 IM 平台集成。这种方式避免了在每个插件中长期运行一个服务器的问题,因为 Dify 承担了 HTTP 请求转发的责任。Dify 通过生成的 URL 来接收来自这些平台的 Webhook 请求,而插件则处理转发的请求。

解决了如何接收消息的问题后,紧接着是如何处理消息。设想我正在开发一个 Discord Bot,想让 Dify 的某个 Chatflow 来回复用户消息,代码可以是这样的:

class Webhook:    def _invoke(self, r: Request) -> Response:        message = r.json()['message']        respose = invoke_app(app_id, message)        return Response(response)

在这个插件中,我需要能够调用 Dify 的 App 来处理请求,这样就可以实现 Bot 功能。此时,我们引出了 Dify v1.0.0 中的一个重要概念:反向调用。

反向调用

反向调用是 Dify 插件机制中的重要概念,允许插件调用 Dify 内部的服务。例如,插件可以调用已经鉴权的模型、工具,或 Dify 的 App。在以下几个场景中,反向调用发挥了重要作用:

  • LlamaIndex 的实现:LlamaIndex 实现了多种 Agentic RAG 策略,通过 LLM 对检索到的列表进行总结。在 Dify 中,它作为一个工具使用,用户只需配置模型参数和输入列表,工具即可安装卸载。
  • 模型作为工具:以前,OCR、ASR 和 TTS 模型只能作为独立模型使用,无法灵活调用。现在,它们可以作为工具使用,例如将 Gemini 作为 OCR 工具,简化了操作流程。
  • OpenAI 兼容 API:通过 Endpoint 插件,Dify 提供 OpenAI 兼容格式,插件可调用 Dify 的 App,并返回统一格式的响应,支持不同模型如 Claude 或 Gemini。
  • Agent 插件化:如果支持反向调用工具,那么就可以实现 Agent 的插件化,自动接收参数执行操作并返回结果,并根据需求自定义 Agent 策略。

实现细节

首先,插件运行时的设计是我们需要解决的首要问题。最终,插件运行时到底是 Docker 容器、进程、虚拟机,还是 Serverless 运行时?在对 Dify 用户群体进行评估后,我们决定实现四种完全不同的运行时,分别为:

  • 本地部署:面向中小型团队和个人开发者,部署需求相对较低,关注高可用性和大规模使用的需求较小。
  • SaaS 服务:面向数十万用户,Dify 需要考虑大量用户负载。
  • 企业版本:与 SaaS 类似,企业版本也要求高可用性,但企业用户需要更高的可控性、隐私保护和私有部署。
  • 远程调试:支持调试模式,也需考虑为其提供运行时支持。

本地部署

本地部署版本的实现注重“一键部署”和开箱即用。用户可以通过一句 `docker compose up -d` 命令运行整个 Dify,并且安装插件时无需额外配置。在这种环境下,插件的运行时设计为一个子进程,由父进程管理所有生命周期控制,包括插件依赖包的安装,两者通过标准输入输出管道进行通信。在理论上,这种设计是可行的。

SaaS 服务

考虑到大规模用户的需求,SaaS 版本的设计采用类似 Serverless 的架构,能够根据使用量自动弹性伸缩,从而确保高并发、高资源利用率和高可用性。最终,我们选择了 AWS Lambda 作为解决方案,AWS 作为 Dify 的合作伙伴,已经支持现有 SaaS 业务,且 Dify 与 Lambda 使用网络进行通信,提供了一个非常合适的架构。

企业版本

考虑到企业客户在解决方案选择上的多样性,因此我们设计了一种可控、可信的运行时。这个版本可以为企业提供高度的可控性和隐私保护,支持企业内部私有部署。

远程调试

对于调试模式,Dify 支持通过 TCP 网络长连接调试插件,并解决了有状态问题。我们采用类似 etcd 的设计,通过 Redis HashMap 管理插件的连接状态,确保插件的请求能够正确转发到相应的 Pod。为了管理 IP,我们设置了如下两项机制:

  • 集群需要维护一个 IP 池,Pod 会将其 IP 加入池中。在生产环境中,一台机器可能有多个 IP,并且位于多个子网,因此,Pod 之间采用投票机制进行可达性测试,标记可用 IP。
  • 集群需要有一个 Master 节点,定期检查 Pod 的存活情况,清理掉已退出的节点状态,确保集群的稳定性。

安全

系统安全

插件机制的安全性主要依赖于密码学中的签名,而不是类似 Sandbox 的强制性安全限制。Sandbox 的限制非常严格,导致很多依赖包无法安装和使用,因为这些包可能会涉及未经许可的系统操作,严重影响插件使用体验。相较之下,插件是已经编写完成的代码包,在安装插件之前,人工审核能够为插件包打上“安全”标签,极大地降低风险。我们采用了基于公钥密码学的签名策略:如果插件通过审核,我们将使用私钥对插件进行签名,标记为“已认证”,如果未通过审查,用户将看到“不安全”的提示。所有未签名的插件无法安装,除非用户手动更改设置。

隐私策略

所有插件均须明确声明其使用的权限、数据存储等隐私政策,尤其是涉及敏感数据的插件。对于简单的权限声明,插件开发者必须显式声明插件的功能权限,未声明的权限 Dify 会直接拒绝使用。而对于复杂的隐私政策,我们要求开发者提供详细的隐私策略,并在 Manifest 中引用,所有上架 Marketplace 的插件都需要经过隐私策略审核。

总结

本文简要介绍了 Dify 插件机制的设计与技术实现。通过插件化,Dify 提供了更高的灵活性和可定制性,支持多种使用场景,同时也为开发者提供了更好的调试和开发体验。我们已开源相关代码,欢迎大家参与其中并贡献新的插件需求:

  • https://github.com/langgenius/dify-plugin-daemon
  • https://github.com/langgenius/dify-plugin-sdks/tree/main/python
  • https://github.com/langgenius/dify

期待与社区共同进步,探索更多创新应用。


?

如果你喜欢 Dify,欢迎:

  • 体验 Dify 云端版本:https://dify.ai/
  • 在 GitHub 上给我们点亮:支持我们的开源项目 https://github.com/langgenius/dify

  • 贡献代码,和我们一起打造更强大的 Dify:你的每一行代码都能让 Dify 更加完美。
  • 通过社交媒体和线下活动:分享 Dify 与你的使用心得,让更多人受益于这个强大的工具。
  • 扫码添加 Dify 小助手:加入我们的用户群,和大家一起分享经验、交流问题,探索更多可能!

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

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

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询