← 返回文章列表

Dify 插件开发实验(07):私有模型网关接入——如何让 Dify 用上私有模型网关?

1. 业务场景

先讲一个我们实际遇到的场景。

企业有内部模型网关:OpenAI 兼容协议,统一管理模型密钥与配额,所有对外的大模型调用都必须经过它——合规要审计、成本要管控、数据不出企业边界。客服工单 SaaS 的智能问答、工单摘要、Agent 对话,全都必须走公司网关,不能直连外部模型。但问题来了:Dify 控制台的模型列表里,根本没有「企业网关」这个供应商。

我们第一次接这类需求时,第一反应也是「Dify 不是支持自定义模型吗,配一下不就行了」。真正动手才发现——「模型」在 Dify 里不是填个 API 地址就能用,而是一个完整的供应商插件:要声明供应商、声明模型、实现调用逻辑,还要过凭证校验——控制台里能选到公司模型,背后是一整套 model provider 插件机制。

这不是个例。任何有私有模型/统一网关的企业都是这个模式:金融行业要审计模型调用、政企要求数据不出域、集团统一采购模型配额——「控制台里能选到公司的模型」,是模型接入的第一诉求。

2. 场景痛点

这个流程的痛点,在接入企业网关时体现得最直接:

本质上,企业要的不是「某个模型」,而是「受控的模型入口」——统一网关、统一密钥、统一审计,Dify 必须能消费这个入口。

3. 方案:为什么是模型插件

选模型插件,我们实际对比过:

这篇文章我们就用它把企业私有网关接进 Dify:开发一个 model provider 插件 dify106_07_gateway_provider,让控制台出现「企业网关」供应商及旗下模型 enterprise-gpt,并用本地 mock 网关(OpenAI 兼容)验证「凭证校验 → 模型列表 → LLM 调用(含流式)」全链路。

4. 整体架构

graph TD subgraph app["【验证应用】"] start["开始(question)"] --> llm["LLM 节点(企业网关模型 enterprise-gpt)"] --> out["输出"] --> end1["结束"] end plugin["模型插件(model provider)"] --> list["控制台模型列表"] plugin --> decl["声明:供应商(企业网关)+ 模型(LLM 类型,predefined-model 模式)"] plugin --> cred["凭证:base_url + api_key(validate_credentials 校验有效性)"] plugin --> models["get_models:返回可用模型列表"] plugin --> call["调用:LLM 节点/Agent → 插件 _invoke → OpenAI 兼容协议 → 网关(mock)"]

链路很清晰:控制台声明供应商 → 配置凭证(保存即校验)→ 模型列表可见 → 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. 实验文档及源码获取

文章聚焦核心配置与采坑点,完整分步操作与模型接入全链路验证记录见实验文档原文。

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过