Dify 插件开发实验(07):私有模型网关接入——如何让 Dify 用上私有模型网关?
Dify 插件开发实验(07):私有模型网关接入——如何让 Dify 用上私有模型网关?
Dify 实验系列 · 插件开发 07/12 | 实验编号:DIFY-106-07
基于 Dify 1.16.1 实测(2026-08)
1. 业务场景
先讲一个我们实际遇到的场景。
企业有内部模型网关:OpenAI 兼容协议,统一管理模型密钥与配额,所有对外的大模型调用都必须经过它——合规要审计、成本要管控、数据不出企业边界。客服工单 SaaS 的智能问答、工单摘要、Agent 对话,全都必须走公司网关,不能直连外部模型。但问题来了:Dify 控制台的模型列表里,根本没有「企业网关」这个供应商。
我们第一次接这类需求时,第一反应也是「Dify 不是支持自定义模型吗,配一下不就行了」。真正动手才发现——「模型」在 Dify 里不是填个 API 地址就能用,而是一个完整的供应商插件:要声明供应商、声明模型、实现调用逻辑,还要过凭证校验——控制台里能选到公司模型,背后是一整套 model provider 插件机制。
这不是个例。任何有私有模型/统一网关的企业都是这个模式:金融行业要审计模型调用、政企要求数据不出域、集团统一采购模型配额——「控制台里能选到公司的模型」,是模型接入的第一诉求。
2. 场景痛点
这个流程的痛点,在接入企业网关时体现得最直接:
- 合规与审计过不去:应用直连外部模型,调用记录、数据流向都不受控,审计时根本说不清。
- 密钥管理分散:每个应用各配各的模型密钥,换一次密钥要改遍所有应用,泄露了也无从追溯。
- 控制台选不到公司模型:Dify 内置供应商列表里没有企业网关,团队只能绕开 Dify 单独接模型,能力割裂。
- 配额与成本不可控:没有网关层统一管控,谁的调用都直接打到模型厂商,预算超了才知道。
本质上,企业要的不是「某个模型」,而是「受控的模型入口」——统一网关、统一密钥、统一审计,Dify 必须能消费这个入口。
3. 方案:为什么是模型插件
选模型插件,我们实际对比过:
- 模型插件 = 供应商扩展:Dify 的 model provider 插件机制,让自定义供应商(含旗下模型)直接出现在控制台模型列表;
- 凭证在控制台配置:网关 base_url + api_key 作为 provider 凭证在控制台录入,不写死在代码,密钥统一管理;
- OpenAI 兼容协议复用生态:网关暴露
/v1/models+/v1/chat/completions,插件按兼容协议实现即可,无需私有协议适配。
这篇文章我们就用它把企业私有网关接进 Dify:开发一个 model provider 插件 dify106_07_gateway_provider,让控制台出现「企业网关」供应商及旗下模型 enterprise-gpt,并用本地 mock 网关(OpenAI 兼容)验证「凭证校验 → 模型列表 → LLM 调用(含流式)」全链路。
4. 整体架构
链路很清晰:控制台声明供应商 → 配置凭证(保存即校验)→ 模型列表可见 → LLM 节点/Agent 调用走插件 → OpenAI 兼容协议打到网关。关键设计是凭证校验前置——配置页保存时调 /v1/models 验证,错的密钥当场报错,不拖到运行时。
5. 模块设计
5.1 manifest 声明模型插件
注意 plugins: models: 而非 tools:
plugins:
models:
- provider/gateway.yaml
5.2 供应商声明(provider/gateway.yaml)
supported_model_types + predefined-model + 凭证 schema:
provider: gateway
supported_model_types:
- llm
configurate_methods:
- predefined-model
provider_credential_schema:
credential_form_schemas:
- variable: base_url
type: text-input
required: true
placeholder:
zh_Hans: http://host.docker.internal:8005
- variable: api_key
type: secret-input
required: true
models:
llm:
predefined:
- "models/llm/llm.yaml"
extra:
python:
provider_source: provider/gateway.py
model_sources:
- "models/llm/llm.py"
5.3 模型声明(models/llm/llm.yaml)
features 决定 Agent 能否使用该模型(工单场景必须支持函数调用):
model: enterprise-gpt
model_type: llm
features:
- agent-thought
- tool-call
model_properties:
mode: chat
context_size: 8192
parameter_rules:
- name: temperature
use_template: temperature
- name: max_tokens
use_template: max_tokens
5.4 LLM 实现(models/llm/llm.py)
LargeLanguageModel 子类实现四抽象 _invoke / validate_credentials / get_num_tokens / _invoke_error_mapping。_invoke 把 PromptMessage 转成 OpenAI messages 调 /chat/completions;流式逐行解析 SSE(data: 前缀、[DONE] 终止、delta.content 非空才 yield);validate_credentials 调 /v1/models 验证 base_url/api_key——配置页「保存」即时报错。
6. 运行验证
| 输入 | 预期 | 结果 |
|---|---|---|
| mock 网关 /models + /chat/completions | curl 通过(非流式+流式 SSE) | ✅ 一致 |
| 正确凭证保存 | 201 保存成功 | ✅ 一致 |
| 错误 api_key / 网关不可达 | 保存报错明确(网关返回 401 / 不可达) | ✅ 错误信息可读 |
| 控制台模型列表 | 出现「企业网关」供应商及 enterprise-gpt | ✅ status active,features agent-thought+tool-call |
| LLM 节点调用 | 企业网关回答 | ✅ run succeeded(0.8s) |
| 流式输出 | 增量完整可拼接 | ✅ 本地单测 6 chunks 拼接完整 |
| mock 网关停止 | LLM 调用报错明确 | ✅ ConnectionError 语义与校验层一致 |
环境:Dify 1.16.1(Docker Compose),mock 网关 tmp/mock_gateway.py(127.0.0.1:8005,Bearer mock-secret-106)。Agent 形态经等价验证(106-03 已挂工具,替换模型场景等价;未单独跑 agent+企业网关组合)。
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| model_properties 枚举 | model_properties 写 max_tokens → 上传报「Failed to parse response from plugin daemon to PluginDecodeResponse」(daemon 正常,api 解析失败) | max_tokens 放 parameter_rules(use_template: max_tokens);model_properties 只允许 mode/context_size 等 10 个枚举 |
| 模型凭证 schema | provider yaml 带空 model_credential_schema 数组反而解析失败 | predefined-model 模式可省(对照 deepseek 官方结构确认) |
| provider 声明格式错 | 按工具插件写法声明模型,上传失败 | manifest 用 plugins.models(非 tools);供应商 id = author/plugin/provider 三段 |
| 接口签名不符 | 抽象方法未实现启动失败 | LargeLanguageModel 四抽象全实现;本地单测用 object.new 绕过 init(model_schemas) |
| 流式实现缺陷 | SSE 解析不全丢字/不终止 | 逐行解析 data: 前缀 + [DONE] 终止;delta.content 非空才 yield(含 finish_reason 终止 chunk) |
| 凭证泄漏 | 错误信息带出 base_url/api_key | 错误信息只报状态码+原因,不出凭证 |
| 模型凭证 API | POST 创建同名凭证 400 already exists | 用 PUT 更新(credential_id 从 provider_credentials 表拿);凭证校验失败不保存(保护行为) |
| LLM 节点 DSL | model.provider 缺完整 id / data 缺字段 → 校验错 | provider 用完整 id(dify106/dify106_07_gateway_provider/gateway);data 必须含 context/memory: null/memory_config/variables/vision |
8. 实验文档及源码获取
- 实验文档:DIFY-106-07:模型插件接入私有网关.md
- 验证应用 DSL:dify106_07_验证应用.yml
- 插件安装包:dify106_07_gateway_provider.signed.difypkg
- 源码目录:dify-106/dsl | dify-106/plugins
文章聚焦核心配置与采坑点,完整分步操作与模型接入全链路验证记录见实验文档原文。
下一篇:Dify 插件开发实验(08):外部知识库插件——如何把外部检索能力做成插件?
💬 你在这个实验的场景里踩过什么坑?欢迎评论区分享你的实战经验。
更多推荐

所有评论(0)