Dify 插件开发实验(11):打包分发与离线安装——插件如何打包签名、分发与离线安装?
Dify 插件开发实验(11):打包分发与离线安装——插件如何打包签名、分发与离线安装?
Dify 实验系列 · 插件开发 11/12 | 实验编号:DIFY-106-11
基于 Dify 1.16.1 实测(2026-08)
1. 业务场景
先讲一个我们实际遇到的场景。
客服工单 SaaS 的插件集开发完了:01 的时间工具、02 的工单查询、04 的企业对接……九个插件各司其职。但交付的客户环境是离线内网——不能访问 marketplace,不能访问外网 PyPI,安装升级全靠本地 .difypkg 文件。交付物不再是一堆代码,而是一组签名包 + 安装顺序说明 + 升级方案,客户环境装完要能正常跑,升级不能破坏已有应用,出了问题还得能回滚。
我们第一次做这种交付时,第一反应也是「打包上传,客户自己装不就行了」。真正动手才发现——「插件能跑」和「插件能交付」,是两回事:离线环境装不上、升级把客户已配置的工作流弄坏了、升级失败没法回滚,任何一环出问题,前面十篇的开发成果都交付不出去——可靠性不在开发环境,而在客户环境。
这不是个例。任何 ToB 交付场景都是这个模式:客户内网环境、不能访问公网市场、安装升级的可靠性直接决定交付质量——「插件能跑」只是第一步,「插件能交付」才是分水岭。
2. 场景痛点
这个流程的痛点,在离线交付时体现得最直接:
- 客户环境离线:不能访问 marketplace 在线安装,插件只能靠本地 .difypkg 导入——离线安装链路不跑通,交付直接卡死。
- 升级破坏已有应用:插件升级后,已配置旧工具的工作流会不会失配?不敢升,版本就永远停在 0.0.1。
- 回滚无预案:升级失败怎么办?没有预案就只能现场救火,客户环境越拖越糟。
- 版本信息错乱:manifest、meta、pyproject 三处版本号不同步,客户控制台看到的版本信息乱七八糟,售后解释不清。
本质上,交付的可靠性不在开发环境,而在客户环境——离线安装、升级兼容、可回滚,这三件事是插件交付的及格线。
3. 方案:为什么是 difypkg 生命周期管理
选 difypkg 生命周期管理,我们实际对比过:
- 打包签名规范:.difypkg 打包 + 第三方签名(白名单公钥),离线环境本地导入即可安装,签名校验保证包可信;
- plugin_id 匹配升级:应用依赖按 plugin_id 匹配(非严格 sha)——实测升级后旧应用自动兼容无需重配,升级不再可怕;
- 保留旧包即可回滚:装回旧版本原包即回滚,回归冒烟验证恢复——回滚有预案,升级才敢做。
这篇文章我们就用它把 106-01~10 的插件整理为「客服工单插件集」,模拟交付到离线客户环境,跑通 打包 → 签名 → 离线安装 → 升级 0.0.1→0.1.0 → 回滚 的完整生命周期。
4. 整体架构
链路很清晰:开发环境打包签名 → 客户环境本地导入 → 签名校验安装 → 回归冒烟 → 升级/回滚闭环。关键设计是「回归基线」——106-01 应用作为每次安装/升级/回滚后的冒烟基线,任何一步破坏功能都能立刻暴露。
5. 模块设计
5.1 打包元数据三处同步
manifest.yaml 的 version、meta.version、pyproject.toml 的 version 三处必须一致,客户控制台才显示清晰信息:
# manifest.yaml —— 打包产物元数据(客户控制台可见)
name: dify106_01_time_tool
version: 0.1.0 # ① manifest version
meta:
version: 0.1.0 # ② meta.version(升级 0.0.1 → 0.1.0 时与 ① 同步)
③ pyproject.toml 的 version = "0.1.0" 与上面两处保持一致——三处任一遗漏,安装/升级时版本信息错乱(实测坑)。
5.2 版本切换流程(升级/回滚实测路径)
# 升级 0.0.1 → 0.1.0(同一 plugin_id 同时只能有一个版本 → 升级 = 卸载旧 + 装新)
dify plugin uninstall --installation_id <旧实例 id> # 卸载旧(用 installation_id,不是 plugin_installation_id)
# 上传 dify106_01_time_tool-0.1.0.signed.difypkg(新 sha/新版本号)
# 控制台插件 → 本地导入 → install → tasks 轮询 success
# 106-01 应用回归冒烟(回归基线)
# 回滚:保留 0.0.1 原包 → 装回即回滚 → 回归冒烟验证
5.3 升级兼容性(本实验最重要结论)
应用依赖按 plugin_id 匹配(非严格 sha)——插件升级后,已配置旧工具的工作流自动兼容无需重配(实测:依赖 0.0.1 旧 uid 的应用在 0.1.0 下运行正常)。但同一 plugin_id 同时只能有一个版本(装 0.1.0 时 0.0.1 被替换/需先卸载)。
6. 运行验证
| 验证项 | 场景 | 预期 | 结果 |
|---|---|---|---|
| 打包 | 插件集 9 个 .difypkg | 命名/版本语义化规范 | ✅ 0.0.1/0.1.0 |
| 干净安装 | 卸载 01/02/04 → 按清单装回 | 全部 success | ✅ |
| 回归冒烟 | 106-01 应用 | 装回后功能正常(回归基线) | ✅ |
| 升级 | 0.0.1 → 0.1.0(新增 format 参数) | 升级成功且已有应用不破坏 | ✅ 旧应用自动兼容(plugin_id 匹配) |
| 回滚 | 装回 0.0.1 | 回归恢复基线 | ✅ |
| 离线安装 | 控制台本地导入 .difypkg | upload → 签名校验 → install → tasks 全链路 | ✅ |
在线 vs 离线差异表:来源——marketplace 官方市场(需外网)/ 本地 .difypkg;校验——官方签名 / 第三方签名(白名单公钥);更新——市场检查更新 / 手动升级(卸载+装新);依赖——daemon 自动处理 / 同上(需 PyPI 可达或预置缓存);适用——公网环境 / 内网客户环境。
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| 依赖声明缺失 | 插件依赖 sdk 版本(pyproject 锁 dify_plugin>=0.7.4,<0.8),客户环境由 daemon uv sync 处理 | 完全离线环境需预置依赖(daemon uv-cache 或内网 PyPI 镜像)——记录为离线边界 |
| 升级破坏已有应用 | 担心升级后旧工作流失配 | 实测应用依赖按 plugin_id 匹配(非严格 sha),升级后旧应用自动兼容无需重配;同一 plugin_id 同时只能一个版本(升级=卸载旧+装新) |
| 回滚无预案 | 升级失败不知如何恢复 | 保留旧版本原包即可回滚(装回旧包)→ 回归冒烟验证(0.1.0→0.0.1 实测恢复) |
| 打包元数据不全 | manifest/pyproject 版本号不同步,控制台版本信息错乱 | 版本号三处同步(manifest version + meta.version + pyproject version),0.1.0 升级时三处一致 |
| 离线校验失败 | 本地导入 .difypkg 报 bad signature 拒绝 | 白名单公钥前置配置(dify106_key.public.pem 随交付包提供) |
8. 实验文档及源码获取
- 实验文档:DIFY-106-11:打包分发与离线安装.md
- 回归基线应用 DSL:dify106_01_验证应用.yml(0.1.0 升级包随交付包提供)
- 插件安装包:dify106_01_time_tool.signed.difypkg
- 批次交付说明:dify-106/delivery/_交付说明.md
- 源码目录:dify-106/dsl | dify-106/plugins
文章聚焦核心配置与采坑点,完整分步操作与升级/回滚全流程验证记录见实验文档原文。
下一篇:Dify 插件开发实验(12):企业级交付验收——插件交付如何做验收?
💬 你在这个实验的场景里踩过什么坑?欢迎评论区分享你的实战经验。
更多推荐

所有评论(0)