Read OSS

模型提供商、工具生态与插件架构

高级

前置知识

  • 第 1-3 篇:架构、请求流程与工作流引擎
  • 了解 LLM API 及基于 token 的计费方式
  • 熟悉 LLM 中的函数调用 / 工具使用机制

模型提供商、工具生态与插件架构

Dify 的核心价值在于抽象能力——通过统一接口连接任意 LLM 提供商、工具和数据源,让工作流构建者和 API 消费者无需关心底层细节。在本系列的最后一篇文章中,我们将深入探讨三个相互关联的系统:对 100 余家 LLM 提供商进行标准化的模型提供商抽象层、为 Agent 和工作流提供外部能力的工具分类体系,以及在隔离进程中安全运行不受信任扩展的插件守护进程架构。

ProviderManager 与凭据解析

Dify 中的每次模型调用都始于凭据解析。ProviderManager 负责根据租户、提供商类型和配置层级,解析当前模型调用应使用哪套凭据。

flowchart TD
    Request[Model invocation request] --> PM[ProviderManager]
    PM --> TenantLookup[Lookup tenant providers]
    TenantLookup --> CredCheck{Credential source?}
    CredCheck -->|Custom| Custom[Custom provider credentials<br/>user-supplied API keys]
    CredCheck -->|System| System[System-configured credentials<br/>admin-provisioned]
    CredCheck -->|Plugin| Plugin[Plugin daemon credentials<br/>from installed plugins]
    Custom --> PC[ProviderConfiguration]
    System --> PC
    Plugin --> PC
    PC --> PMB[ProviderModelBundle<br/>credentials + model type instance]
    PMB --> MI[ModelInstance]

ProviderManager 从多张数据库表中读取数据:Provider(租户与提供商的绑定关系)、ProviderCredential(加密凭据存储)、ProviderModel(单模型配置)以及 LoadBalancingModelConfig(多端点负载分配)。

凭据解析遵循以下优先级链:

  1. 自定义模型凭据 — 用户为特定模型设置的 API key
  2. 自定义提供商凭据 — 用户在提供商层级设置的 API key
  3. 系统凭据 — 管理员配置的共享凭据
  4. 托管配额 — Dify Cloud 内置的模型访问权限,分为试用和付费层级

凭据通过 ProviderCredentialsCache 进行缓存,采用 TTL 过期策略,避免每次模型调用都直接访问数据库。

ModelInstance:统一的 LLM 抽象

ModelInstance 类为任意 LLM 提供商提供一致的调用接口。无论是 OpenAI、Anthropic、本地部署的 Ollama,还是通过插件提供的自定义模型,调用方看到的都是同一套 API。

classDiagram
    class ModelInstance {
        +provider_model_bundle: ProviderModelBundle
        +model_name: str
        +credentials: dict
        +load_balancing_manager
        +invoke_llm()
        +invoke_text_embedding()
        +invoke_rerank()
        +invoke_tts()
        +invoke_speech2text()
        +invoke_moderation()
    }

    class ProviderModelBundle {
        +configuration: ProviderConfiguration
        +model_type_instance: ModelTypeInstance
    }

    class ProviderConfiguration {
        +provider: ProviderEntity
        +get_current_credentials()
    }

    class LoadBalancingManager {
        +invoke_with_fallback()
    }

    ModelInstance --> ProviderModelBundle
    ProviderModelBundle --> ProviderConfiguration
    ModelInstance --> LoadBalancingManager

ModelInstance 的核心功能包括:

  • 负载均衡 — 当存在 LoadBalancingModelConfig 配置时,管理器会将请求分发到同一模型的多个 API 端点,有效应对生产环境中单个 API key 触达速率限制的问题。
  • 凭据管理 — 从 ProviderModelBundle 获取凭据并在实例上缓存。
  • 模型 Schema 解析get_model_schema() 返回描述模型能力的 AIModelEntity,包含上下文窗口大小、支持的功能和定价信息。
  • 统一调用接口invoke_llm()invoke_text_embedding()invoke_rerank() 等方法提供一致的调用规范。

HostingConfiguration 负责管理 Dify Cloud 的托管模型提供商,支持带用量限制的试用配额和付费层级。

提示: 当模型调用抛出 ProviderTokenNotInitError 时,问题几乎都出在凭据解析环节——该租户没有为对应的提供商和模型组合配置有效凭据。可以直接检查 ProviderProviderCredential 表。

工具类型分类体系

Dify 支持五类工具,每类工具具有不同的运行时特性:

classDiagram
    class Tool {
        <<abstract>>
        +entity: ToolEntity
        +runtime: ToolRuntime
        +invoke()
        +tool_provider_type()
    }

    class BuiltinTool {
        Built-in tools shipped with Dify
    }
    class PluginTool {
        Tools from installed plugins
    }
    class ApiTool {
        OpenAPI/Swagger defined tools
    }
    class MCPTool {
        Model Context Protocol tools
    }
    class WorkflowTool {
        Workflows exposed as tools
    }

    Tool <|-- BuiltinTool
    Tool <|-- PluginTool
    Tool <|-- ApiTool
    Tool <|-- MCPTool
    Tool <|-- WorkflowTool

基类 Tool 定义了以下核心契约:

  • invoke() — 公开入口点,负责合并运行时参数、转换参数类型,并委托给 _invoke() 执行
  • _invoke() — 抽象方法,由各工具子类具体实现
  • tool_provider_type() — 标识工具所属类别
  • fork_tool_runtime() — 基于不同运行时上下文创建工具副本

ToolManager 负责跨五种类型解析工具提供商,其发现来源分别为:

  • 内置工具 — 来自 core/tools/builtin_tool/providers/ 目录
  • 插件工具 — 通过 PluginToolManager 从插件守护进程获取
  • API 工具 — 来自数据库中的 ApiToolProvider 记录
  • MCP 工具 — 来自已配置的 MCP 服务器连接
  • 工作流工具 — 来自数据库中的 WorkflowToolProvider 记录

每种工具类型都有对应的 provider controller(如 BuiltinToolProviderControllerApiToolProviderControllerMCPToolProviderController),分别负责凭据管理、参数 Schema 解析和工具实例化。

ToolEngine:工具的运行时执行

ToolEngine 提供工具执行的运行时环境,统一处理参数解析、调用执行、文件管理和错误处理等复杂逻辑。

sequenceDiagram
    participant Agent/Node
    participant ToolEngine
    participant Tool
    participant Callback

    Agent/Node->>ToolEngine: agent_invoke(tool, parameters)
    ToolEngine->>ToolEngine: Parse parameters (str → dict)
    ToolEngine->>Callback: on_tool_start()
    ToolEngine->>Tool: invoke(user_id, parameters)
    Tool-->>ToolEngine: Generator[ToolInvokeMessage]
    ToolEngine->>ToolEngine: Process messages<br/>transform files, extract text
    ToolEngine->>Callback: on_tool_end()
    ToolEngine-->>Agent/Node: (text, file_ids, meta)

tool_engine.py#L47-L79 中的 agent_invoke() 静态方法处理了一个较为棘手的边界情况:LLM 有时会以原始字符串而非 JSON 对象的形式传入工具参数。如果该工具只有一个 LLM 类型的参数,则直接使用该字符串;否则尝试 JSON 解析,并在失败时进行优雅降级。

工具调用的返回结果是 ToolInvokeMessage 生成器,可以 yield 文本、图片、文件、链接或 JSON。ToolFileMessageTransformer 负责将二进制文件输出转换为带签名 URL 的上传文件。

插件守护进程与反向调用

插件守护进程是 Dify 安全运行不受信任代码的解决方案。它是一个独立的 Go 服务,在隔离进程中执行插件,并通过双向通道与主 Dify API 进行通信。

flowchart TD
    subgraph Dify API Process
        API[Flask API Server]
        InnerAPI[Inner API Blueprint<br/>/inner/api]
        PluginImpl[core/plugin/impl/]
    end

    subgraph Plugin Daemon Process
        Daemon[dify-plugin-daemon<br/>Go service]
        PluginRuntime[Plugin Runtime<br/>isolated process per plugin]
    end

    API -->|Forward call| PluginImpl
    PluginImpl -->|HTTP| Daemon
    Daemon --> PluginRuntime
    PluginRuntime -->|Backwards invocation| InnerAPI
    InnerAPI --> API

    style PluginRuntime fill:#f9f,stroke:#333

该架构包含两个通信方向:

正向调用 — 当 Dify 需要使用插件能力(模型推理、工具执行、数据源访问)时,core/plugin/impl/ 中的实现类通过 HTTP 调用插件守护进程,由守护进程将请求路由到对应的插件运行时。

反向调用 — 当插件需要回调 Dify(调用模型、访问存储、加密数据或执行节点)时,它调用守护进程,守护进程再将请求转发至 Dify 的 Inner API(/inner/api)。core/plugin/backwards_invocation/ 中的反向调用处理器涵盖六个类别:

模块 用途
model.py 插件调用 LLM 模型
tool.py 插件调用其他工具
node.py 插件执行工作流节点
app.py 插件与应用状态交互
encrypt.py 插件加密/解密凭据
base.py 共享调用基础设施

PluginModelAssembly 根据 tenant_id 懒加载构建完整的模型调用栈(runtime → factory → provider manager → model manager),为插件提供的模型访问提供清晰的入口点。

docker-compose.yaml#L998-L1068 中的插件守护进程 Docker Compose 配置清晰展示了安全边界:它通过 DIFY_INNER_API_URLDIFY_INNER_API_KEY 与 API 通信,通过签名验证(FORCE_VERIFYING_SIGNATURE)运行插件,并支持配置超时时间和缓冲区大小。

提示: 插件开发通过 PLUGIN_REMOTE_INSTALL_HOSTPLUGIN_REMOTE_INSTALL_PORT 配置项进行,这两个配置会暴露一个调试端点。开发期间,插件连接到该端点即可实现热重载测试,无需重新构建守护进程。

MCP 集成与多租户

Dify 在 core/mcp/ 下实现了 MCP(Model Context Protocol)集成,用于连接支持 MCP 协议的外部工具服务器。core/tools/mcp_tool/ 中的 MCPToolProviderControllerMCPTool 类将 MCP 服务器连接封装为 Dify 工具,使其可在工作流和 Agent 配置中直接使用。

多租户设计贯穿整个模型和工具系统。每项凭据、配置和配额都以租户为作用域进行隔离。账户模型中的 TenantAccountRole 枚举定义了 RBAC 权限:

  • Owner — 对工作区设置拥有完全控制权
  • Admin — 管理模型、工具和数据集
  • Editor — 创建和修改应用及工作流
  • Normal — 使用应用和工作流
  • DatasetOperator — 仅管理数据集

模型凭据采用租户级加密,工具提供商按租户解析,速率限制按租户和应用双维度应用。这确保了 Dify 多工作区架构下的完全隔离。

系列总结

在这五篇文章中,我们完整走过了 Dify 的全貌:从 Docker Compose 拓扑结构,到 Flask 启动序列、请求执行管道、工作流图引擎、RAG 管道,最终来到支撑整个系统运转的模型与工具抽象层。这是一个体量庞大的代码库,但其中的设计模式高度一致:

  • 工厂模式 用于扩展性(节点工厂、向量数据库工厂、索引处理器工厂)
  • 分层模式 用于横切关注点(配额、可观测性、持久化)
  • 队列模式 用于解耦(runner 与 pipeline 之间基于 Redis 的 pub/sub,以及 Celery 异步任务)
  • 适配器模式 用于集成(32 种向量数据库、100 余家模型提供商、5 种工具类型)

理解这些模式,而非死记个别文件的细节,才是在 Dify 这个拥有 6000 余个文件的代码库中自信导航的真正关键。