Dify 的提供商抽象层设计:我们如何同时支持数十个 LLM 并保持松散耦合
简介
对于企业人工智能平台来说,“使用哪个LLM”并不是一次性的决定。由于成本优化、性能要求的变化、新型号的引入、因法规遵从而导致的供应商变化等,型号切换不断发生。
Dify 的 Provider 抽象层是对这一现实的结构性回答。本文从架构设计的角度阐述了Dify如何抽象LLM Provider,并实现应用层和模型层之间的松耦合。
1.什么是Provider抽象层?
1.1 问题定义
直接调用LLM的应用程序会遇到以下问题:
# 抽象層がない場合の典型的なコード
import openai
# OpenAI に直接依存
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
)
此实现在以下方面容易受到攻击:
- 更改模型时需要修改整个应用程序代码
- API密钥管理分散在应用程序内
- 吸收应用端各提供商的API规范差异(参数名称、响应格式)
- 难以统一处理速率限制、重试和回退
1.2 Dify 解决方案:分离提供者层
Dify 在应用层(Workflow/Agent/Chat)和模型调用层之间放置了一个 Provider 抽象层:
graph TB
subgraph "Application Layer"
W[Workflow]
AG[Agent]
CH[Chat App]
end
subgraph "Provider Abstraction Layer"
direction TB
MI[Model Interface<br/>統一 API]
MC[Model Configuration<br/>Workspace レベル管理]
MR[Model Runtime<br/>実行・リトライ・制御]
end
subgraph "Model Providers"
OP[OpenAI]
AZ[Azure OpenAI]
AN[Anthropic]
GG[Google Gemini]
OL[Ollama / Local]
CU[Custom Provider]
end
W --> MI
AG --> MI
CH --> MI
MI --> MC
MC --> MR
MR --> OP
MR --> AZ
MR --> AN
MR --> GG
MR --> OL
MR --> CU
style MI fill:#69b,stroke:#333,color:#fff
2. 架构细节
2.1 模型提供者注册结构
📖 注:以下 YAML 是 Dify Provider 抽象层的概念模型示意,用于说明设计意图。Dify 1.x 实际的 Plugin Manifest 结构请参考官方仓库:
- 主清单
manifest.yaml(包含 plugin 元信息、版本、依赖)- 工具 / 模型 provider 的具体字段定义见
api/core/plugin/entities/plugin.py的PluginDeclaration- Model Runtime 基类位于
api/core/entities/model_entities.py(ProviderEntity、ModelType、ProviderModel),底层依赖graphon库- 凭证字段实际命名为
credentials_schema(工具 / Provider 通用),ProviderEntity 嵌套字段为provider_credential_schema.credential_form_schemas
# Provider 抽象的概念模型(不要直接照抄到 plugin manifest)
provider:
name: "openai"
label: "OpenAI"
supported_model_types:
- llm
- text-embedding
- speech2text
- tts
credentials_schema: # 实际字段名(注意是复数)
- name: api_key
type: secret
required: true
- name: organization_id
type: string
required: false
models:
- name: "gpt-4o"
type: llm
features:
- function_calling
- vision
context_length: 128000
pricing:
input: 0.005 # per 1K tokens
output: 0.015
本设计有以下意图:
| 设计元素 | 目的 |
|---|---|
supported_model_types | 统一管理同一提供商提供的不同能力(LLM、Embedding、TTS) |
credentials_schema | 定义如何管理每个提供商的 API 密钥并在工作区级别集中管理它们 |
features | 应用层可以通过声明函数调用、视觉等能力来判断兼容性 |
context_length | 在设计工作流程时将上下文长度公开为元数据并可视化约束 |
pricing | 实现成本估算并支持公司的模型选择决策 |
2.2 模型接口统一
Provider抽象层的核心是能够通过统一的接口调用不同Provider的API:
# Dify の Model Interface 概念モデル
class ModelInterface:
"""全 LLM Provider に共通のインターフェース"""
def invoke(
self,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
model_parameters: dict,
tools: list[Tool] | None = None,
stop: list[str] | None = None,
stream: bool = False,
user: str | None = None,
) -> LLMResult | Generator[LLMResultChunk]:
"""
統一的なモデル呼び出しインターフェース。
プロバイダー固有のパラメータ変換は内部で処理される。
"""
pass
def get_num_tokens(
self,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
) -> int:
"""トークン数の事前計算"""
pass
def validate_credentials(
self,
model: str,
credentials: dict,
) -> None:
"""クレデンシャルの事前検証"""
pass
2.3 参数映射实际情况
每个提供商的 API 使用的参数名称和格式略有不同。 Provider 抽象层透明地对此进行了转换:
| 定义统一参数 | 开放人工智能 | 人择 | Azure 开放人工智能 | 奥拉玛 |
|---|---|---|---|---|
temperature | temperature | temperature | temperature | temperature |
max_tokens | max_tokens | max_tokens | max_tokens | num_predict |
stop | stop | stop_sequences | stop | stop |
tools | tools | tools | tools | tools |
stream | stream | stream | stream | stream |
response_format | response_format | 不适用(替代方法) | response_format | format |
3.工作区级模型管理
3.1 为什么是工作区级别?
在 Dify 中,模型提供程序设置是在 工作区基础 上管理的,而不是在应用程序基础上进行管理。这一设计决定有明确的理由:
graph TB
subgraph "Workspace A(営業部門)"
APP1[顧客対応 Bot]
APP2[提案書生成]
APP3[FAQ 検索]
subgraph "Workspace A の Model Config"
M1[OpenAI GPT-4o<br/>API Key: ****]
M2[Azure OpenAI<br/>Endpoint: jpeast]
end
end
subgraph "Workspace B(開発部門)"
APP4[コードレビュー]
APP5[ドキュメント生成]
subgraph "Workspace B の Model Config"
M3[Anthropic Claude<br/>API Key: ****]
M4[Ollama Llama3<br/>Local]
end
end
APP1 --> M1
APP2 --> M2
APP3 --> M1
APP4 --> M3
APP5 --> M4
| 设计意图 | 描述 |
|---|---|
| API 密钥集中管理 | 工作区管理员集中管理 API 密钥,无需将其分发到每个应用程序 |
| 成本可视化 | 了解 Workspace 的代币消耗和成本 |
| 部门治理 | 可用型号可以受部门限制 |
| 更高效的模型切换 | 只需更改工作区设置,它将反映在所有应用程序中 |
3.2 模型切换用例
企业实际切换模式的典型场景:
- 成本优化:从 GPT-4o 切换到 GPT-4o-mini(降低成本,同时保证准确性)
- 监管准备:从外部 API 迁移到本地本地 LLM
- 性能提升:新机型发布时逐步过渡
- 故障响应:在特定提供商发生故障时切换到后备目的地
- 多模型策略:根据任务类型使用最优模型
4. 插件架构:可扩展性设计
4.1 使用插件添加自定义提供程序
Dify 允许您通过插件机制添加不支持开箱即用的模型提供程序:
graph LR
subgraph "Dify Core"
PA[Plugin Architecture]
MR[Model Runtime]
end
subgraph "Built-in Providers"
OP[OpenAI]
AN[Anthropic]
AZ[Azure]
end
subgraph "Custom Plugins"
CP1[社内 LLM Gateway]
CP2[国産 LLM<br/>ELYZA / PLaMo]
CP3[vLLM Cluster]
end
PA --> OP
PA --> AN
PA --> AZ
PA --> CP1
PA --> CP2
PA --> CP3
PA --> MR
4.2 模型设计规则
官方文档中发布的模型设计规则定义了插件开发者必须遵循的接口约定:
# Model Plugin の必須実装項目
model_plugin:
# 1. Provider 定義
provider_manifest:
- プロバイダー名・アイコン・説明
- サポートするモデルタイプ
- クレデンシャルスキーマ
# 2. モデル定義
model_manifest:
- モデル名・バージョン
- 能力宣言 (features)
- パラメータ制約 (context_length, max_tokens)
- 価格情報(任意)
# 3. Runtime 実装
runtime:
- invoke(): 同期/ストリーミング呼び出し
- get_num_tokens(): トークン計算
- validate_credentials(): 認証情報検証
通过遵循此约定,您的自定义提供程序将自动与所有 Dify 功能集成,例如工作流节点中的模型选择、代理中的模型规范、知识库中的嵌入模型选择等。
4.3 插件分离带来的稳定性
📖 前提:进程隔离是 Dify 1.0 引入 Plugin Daemon 之后的特性。0.x 版本的 Provider 不是进程隔离的,自定义 Provider 的崩溃可能影响主进程。Enterprise 部署默认启用 Plugin Daemon。
启用 Plugin Daemon 后,插件作为独立进程运行,自定义 Provider 中的错误或故障不会影响 Dify 核心:
| 建筑特色 | 效果 |
|---|---|
| 进程隔离 (1.0+) | 插件崩溃不会蔓延到核心(前提:启用 Plugin Daemon) |
| 版本无关 | Plugin和Dify Core可独立更新 |
| 资源限制 | 每个插件的内存和 CPU 都可以受到限制 |
| 安全边界 | 插件凭证与核心凭证分离 |
5. 设计权衡
5.1 抽象的限制
Provider抽象层吸收了许多差异,但不能使它们全部透明:
| 差异类型 | 抽象的可能性 | 如何应对 |
|---|---|---|
| 基本聊天完成 | 完全可抽象 | 统一接口 |
| 函数调用 | 几乎可以抽象 | 工具定义的统一格式 |
| 视觉(图像输入) | 条件抽象 | 带有功能标志的功能声明 |
| 上下文长度 | 作为元数据进行管理 | 检查应用程序端的约束 |
| 速率限制 | 依赖于提供商 | 在运行时层重试/排队 |
| JSON 模式 | 实施情况因提供商而异 | 部分抽象+回退 |
| 多模式(音频/视频) | 供应商之间的巨大差异 | 对每种模型类型的单独支持 |
| 微调API | 每个提供商完全不同 | 不抽象 |
5.2 处理“最小公约数”问题
抽象层的一个典型风险是它们只能提供“最低公分母”API,而无法利用每个提供商的独特功能。 Dify 通过 特征标志 + 模型参数扩展 解决了这个问题:
# Feature フラグによる能力の動的チェック
model_config = get_model_config("gpt-4o")
if "function_calling" in model_config.features:
# Function Calling 対応のフローを実行
result = invoke_with_tools(model, tools, prompt)
elif "vision" in model_config.features:
# Vision 対応のフローを実行
result = invoke_with_images(model, images, prompt)
else:
# 基本的な Text Completion のみ
result = invoke_text(model, prompt)
6. Provider抽象层在企业中的战略价值
6.1 避免供应商锁定
graph TB
subgraph "ベンダーロックインの比較"
direction LR
subgraph "直接統合"
A1[App] -->|"OpenAI SDK"| B1[OpenAI]
A1 -.->|"移行コスト: 高"| C1[Anthropic]
end
subgraph "Dify Provider 抽象層"
A2[App] --> D2[Provider Layer]
D2 -->|"設定変更のみ"| B2[OpenAI]
D2 -->|"設定変更のみ"| C2[Anthropic]
D2 -->|"設定変更のみ"| E2[Azure]
end
end
6.2 多模型策略的实现
有远见的公司正在根据任务类型而不是单一模型采用多模型策略:
| 任务类型 | 推荐型号 | 选择理由 |
|---|---|---|
| 复杂的推理与分析 | GPT-4o / Claude Opus | 精度要求高 |
| 大批量常规加工 | GPT-4o-mini / Claude Haiku | 成本效益 |
| 日本专业任务 | 国内LLM / Qwen | 日本表演 |
| 敏感数据处理 | 奥拉马(本地) | 无法向外发送数据 |
| 嵌入 | 文本嵌入-3-小 | 成本与准确性之间的平衡 |
| 图像识别 | GPT-4o / Gemini Pro 视觉 | 多式联运支持 |
Dify 的 Provider 抽象层允许您为同一工作流中的不同节点指定不同的模型。这使得上述多模型策略可以在工作流设计层面得以实现。
6.3 成本管理和治理
工作区级别的提供商管理自然提供以下治理功能:
- 使用情况可视化:按工作空间/应用程序/用户跟踪令牌消耗情况
- 预算控制:设置每个工作区的使用限制
- 模型使用政策:仅允许在特定工作空间中使用特定模型的限制
- 审计跟踪:记录哪些用户向哪些模型发送哪些查询
7. 与其他平台的比较
| 项目 | Dify | 浪链 | 亚马逊基岩 | Azure 人工智能工作室 |
|---|---|---|---|---|
| 抽象层粒度 | 供应商+模型 | 连锁/LLM班 | API网关 | 集线器+端点 |
| GUI 中的模型管理 | 是的 | 没有 | 是的 | 是的 |
| 定制供应商 | 可通过插件添加 | Python 类实现 | 自定义模型导入 | 自定义端点 |
| 工作区单元管理 | 是的 | 没有 | 账户单位 | 资源组单位 |
| 功能标志 | 以模型单位声明 | 无(由代码确定) | 模型单位 | 模型单位 |
| 开源软件 | 是的 | 是的 | 没有 | 没有 |
| 运营成本 | 中型(自托管) | 低(框架) | 高(AWS 计费) | 高(Azure 计费) |
总结
Dify 的 Provider 抽象层在“支持多模型”的表面特征背后有以下设计理念:
- 应用层和模型层清晰分离:应用业务逻辑不依赖于模型选择
- 工作区级统一管理:API密钥管理、成本管理和治理集中于一处
- 使用插件的扩展性:可以使用相同的接口添加标准不支持的模型。
- 特征标志的灵活性:每个模型特定特征的抽象和利用
对于企业来说,Provider抽象层的价值不在于可以连接的模型数量,而在于能够选择、切换和使用模型作为业务决策。 Dify 的设计允许在管理屏幕上做出这些决策,而无需更改任何代码。
参考资料
-Model Providers - Dify Docs -Model Designing Rules -Model API Interface (Schema) -Workspace Overview - Dify Docs