Files
dify-docs/plugin-dev-zh/0222-creating-new-model-provider-extra.mdx
2025-07-16 16:42:34 +08:00

286 lines
15 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
dimensions:
type:
primary: implementation
detail: standard
level: intermediate
standard_title: Creating New Model Provider Extra
language: zh
title: 实现标准的模型集成
description: 本文档面向需要编写Python代码以添加或增强Dify模型支持的开发者详细指导如何创建目录结构、编写模型配置、实现模型调用逻辑、调试和发布插件的完整流程包含了核心方法实现和错误处理的细节。
---
本文档是为需要通过编写 Python 代码来为 Dify 添加或增强模型支持的开发者准备的标准指南。当你需要添加的模型涉及新的 API 调用逻辑、特殊参数处理或 Dify 需要显式支持的新功能(如 Vision, Tool Calling就需要遵循本指南的步骤。
**阅读本文前,建议你:**
* 具备 Python 编程基础和面向对象编程的基本理解。
* 熟悉你想要集成的模型供应商提供的 API 文档和认证方式。
* 已安装并配置好 Dify 插件开发工具包 (参考 [初始化开发工具](../initialize-development-tools.md))。
* (可选)阅读 [模型插件介绍](link-to-conceptual-intro) 文档,了解模型插件的基本概念和架构。
本指南将引导你完成创建目录结构、编写模型配置 (YAML)、实现模型调用逻辑 (Python) 以及调试和发布插件的全过程。
---
## 第 1 步: 创建目录结构
一个组织良好的目录结构是开发可维护插件的基础。你需要为你的模型供应商插件创建特定的目录和文件。
1. **定位或创建供应商目录:** 在你的插件项目(通常是 `dify-official-plugins` 的本地克隆)的 `models/` 目录下,找到或创建以模型供应商命名的文件夹(例如 `models/my_new_provider`)。
2. **创建 `models` 子目录:** 在供应商目录下,创建 `models` 子目录。
3. **按模型类型创建子目录:** 在 `models/models/` 目录下,为你需要支持的**每种模型类型**创建一个子目录。常见的类型包括:
* `llm`: 文本生成模型
* `text_embedding`: 文本 Embedding 模型
* `rerank`: Rerank 模型
* `speech2text`: 语音转文字模型
* `tts`: 文字转语音模型
* `moderation`: 内容审查模型
4. **准备实现文件:**
* 在每个模型类型目录下(例如 `models/models/llm/`),你需要创建一个 Python 文件来实现该类型模型的调用逻辑(例如 `llm.py`)。
* 同样在该目录下,你需要为该类型下的每个具体模型创建一个 YAML 配置文件(例如 `my-model-v1.yaml`)。
* (可选)可以创建一个 `_position.yaml` 文件来控制该类型下模型在 Dify UI 中的显示顺序。
**示例结构 (假设供应商 `my_provider` 支持 LLM 和 Embedding):**
```bash
models/my_provider/
├── models # 模型实现和配置目录
│ ├── llm # LLM 类型
│ │ ├── _position.yaml (可选, 控制排序)
│ │ ├── my-llm-model-v1.yaml
│ │ ├── my-llm-model-v2.yaml
│ │ └── llm.py # LLM 实现逻辑
│ └── text_embedding # Embedding 类型
│ ├── _position.yaml (可选, 控制排序)
│ ├── my-embedding-model.yaml
│ └── text_embedding.py # Embedding 实现逻辑
├── provider # 供应商级别代码目录
│ └── my_provider.py (用于凭证验证等, 参考“创建模型供应商”文档)
└── manifest.yaml # 插件清单文件
```
---
## 第 2 步: 定义模型配置 (YAML)
对于每个具体模型,你需要创建一个 YAML 文件来描述其属性、参数和功能,以便 Dify 能够正确地理解和使用它。
1. **创建 YAML 文件:** 在对应的模型类型目录下(例如 `models/models/llm/`),为你要添加的模型创建一个 YAML 文件,文件名通常与模型 ID 保持一致或具有描述性(例如 `my-llm-model-v1.yaml`)。
2. **编写配置内容:** 遵循 [AIModelEntity Schema Definition](../../../schema-definition/model/model-designing-rules.md#aimodelentity) 规范编写内容。关键字段包括:
* `model`: (必需) 模型的官方 API 标识符。
* `label`: (必需) 在 Dify UI 中显示的名称 (支持多语言)。
* `model_type`: (必需) 必须与所在目录类型一致 (如 `llm`)。
* `features`: (可选) 声明模型支持的特殊功能 (如 `vision`, `tool-call`, `stream-tool-call` 等)。
* `model_properties`: (必需) 定义模型固有属性,如 `mode` (`chat` 或 `completion`), `context_size`。
* `parameter_rules`: (必需) 定义用户可调参数及其规则 (名称 `name`, 类型 `type`, 是否必须 `required`, 默认值 `default`, 范围 `min`/`max`, 选项 `options` 等)。可以使用 `use_template` 引用预定义模板简化常见参数(如 `temperature`, `max_tokens`)的配置。
* `pricing`: (可选) 定义模型的计费信息。
**示例 (`claude-3-5-sonnet-20240620.yaml`):**
```yaml
model: claude-3-5-sonnet-20240620
label:
en_US: claude-3-5-sonnet-20240620
model_type: llm
features:
- agent-thought
- vision
- tool-call
- stream-tool-call
- document
model_properties:
mode: chat
context_size: 200000
parameter_rules:
- name: temperature
use_template: temperature
- name: top_p
use_template: top_p
- name: max_tokens
use_template: max_tokens
required: true
default: 8192
min: 1
max: 8192 # 注意 Dify 层面可能有限制
pricing:
input: '3.00'
output: '15.00'
unit: '0.000001' # 每百万 token
currency: USD
```
---
## 第 3 步: 编写模型调用代码 (Python)
这是实现模型功能的和核心步骤。你需要在对应模型类型的 Python 文件中(例如 `llm.py`)编写代码来处理 API 调用、参数转换和结果返回。
1. **创建/编辑 Python 文件:** 在模型类型目录下(例如 `models/models/llm/`)创建或打开相应的 Python 文件(例如 `llm.py`)。
2. **定义实现类:**
* 定义一个类,例如 `MyProviderLargeLanguageModel`。
* 该类必须继承自 Dify 插件 SDK 中对应的**模型类型基类**。例如,对于 LLM需要继承 `dify_plugin.provider_kits.llm.LargeLanguageModel`。
```python
import logging
from typing import Union, Generator, Optional, List
from dify_plugin.provider_kits.llm import LargeLanguageModel # 导入基类
from dify_plugin.provider_kits.llm import LLMResult, LLMResultChunk, LLMUsage # 导入结果和用量类
from dify_plugin.provider_kits.llm import PromptMessage, PromptMessageTool # 导入消息和工具类
from dify_plugin.errors.provider_error import InvokeError, InvokeAuthorizationError # 导入错误类
# 假设你有一个 vendor_sdk 用于调用 API
# import vendor_sdk
logger = logging.getLogger(__name__)
class MyProviderLargeLanguageModel(LargeLanguageModel):
# ... 实现方法 ...
```
3. **实现关键方法:** (具体需要实现的方法取决于继承的基类,以下以 LLM 为例)
* `_invoke(...)`: **核心调用方法**。
* **签名:** `def _invoke(self, model: str, credentials: dict, prompt_messages: List[PromptMessage], model_parameters: dict, tools: Optional[List[PromptMessageTool]] = None, stop: Optional[List[str]] = None, stream: bool = True, user: Optional[str] = None) -> Union[LLMResult, Generator[LLMResultChunk, None, None]]:`
* **职责:**
* 使用 `credentials` 和 `model_parameters` 准备 API 请求。
* 将 Dify 的 `prompt_messages` 格式转换为供应商 API 所需的格式。
* 处理 `tools` 参数以支持 Function Calling / Tool Use (如果模型支持)。
* 根据 `stream` 参数决定是进行流式调用还是同步调用。
* **流式返回:** 如果 `stream=True`,此方法必须返回一个生成器 (`Generator`),通过 `yield` 逐块返回 `LLMResultChunk` 对象。每个 chunk 包含部分结果(文本、工具调用块等)和可选的用量信息。
* **同步返回:** 如果 `stream=False`,此方法必须返回一个完整的 `LLMResult` 对象,包含最终的文本结果、完整的工具调用列表以及总的用量信息 (`LLMUsage`)。
* **实现模式:** 强烈建议将同步和流式逻辑拆分到内部帮助方法中。
```python
def _invoke(self, ..., stream: bool = True, ...) -> Union[LLMResult, Generator[LLMResultChunk, None, None]]:
# 准备 API 请求参数 (认证、模型参数转换、消息格式转换等)
api_params = self._prepare_api_params(credentials, model_parameters, prompt_messages, tools, stop)
try:
if stream:
return self._invoke_stream(model, api_params, user)
else:
return self._invoke_sync(model, api_params, user)
except vendor_sdk.APIError as e:
# 处理 API 错误, 映射到 Dify 错误 (参考 _invoke_error_mapping)
# ... raise mapped_error ...
pass # Replace with actual error handling
except Exception as e:
logger.exception("Unknown error during model invocation")
raise e # Or raise a generic InvokeError
def _invoke_stream(self, model: str, api_params: dict, user: Optional[str]) -> Generator[LLMResultChunk, None, None]:
# 调用 vendor_sdk 的流式接口
# for api_chunk in vendor_sdk.create_stream(...):
# # 将 api_chunk 转换为 LLMResultChunk
# dify_chunk = self._convert_api_chunk_to_llm_result_chunk(api_chunk)
# yield dify_chunk
pass # Replace with actual implementation
def _invoke_sync(self, model: str, api_params: dict, user: Optional[str]) -> LLMResult:
# 调用 vendor_sdk 的同步接口
# api_response = vendor_sdk.create_sync(...)
# 将 api_response 转换为 LLMResult (包括 message.content, tools, usage)
# dify_result = self._convert_api_response_to_llm_result(api_response)
# return dify_result
pass # Replace with actual implementation
```
* `validate_credentials(self, model: str, credentials: dict) -> None`: (必需) 用于在用户添加或修改凭证时验证其有效性。通常通过调用一个简单的、低成本的 API 端点(如列出可用模型、检查余额等)来实现。如果验证失败,应抛出 `CredentialsValidateFailedError` 或其子类。
* `get_num_tokens(self, model: str, credentials: dict, prompt_messages: List[PromptMessage], tools: Optional[List[PromptMessageTool]] = None) -> int`: (可选但推荐) 用于预估给定输入的 Token 数量。如果无法准确计算或 API 不支持,可以返回 0。
* `@property _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]`: (必需) 定义一个**错误映射**字典。键是 Dify 的标准 `InvokeError` 子类,值是供应商 SDK 可能抛出的需要被映射到该标准错误的异常类型列表。这对于 Dify 统一处理不同供应商的错误至关重要。
```python
@property
def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]:
# 示例映射
mapping = {
InvokeAuthorizationError: [
vendor_sdk.AuthenticationError,
vendor_sdk.PermissionDeniedError,
],
InvokeRateLimitError: [
vendor_sdk.RateLimitError,
],
# ... 其他映射 ...
}
# 可以在这里加入基类的默认映射 (如果基类提供的话)
# base_mapping = super()._invoke_error_mapping
# mapping.update(base_mapping) # 注意合并策略
return mapping
```
---
## 第 4 步: 调试插件
在将插件贡献给社区之前充分的测试和调试是必不可少的。Dify 提供了远程调试功能,让你可以在本地修改代码并实时在 Dify 实例中测试效果。
1. **获取调试信息:**
* 在你的 Dify 实例中,进入 "插件管理" 页面 (可能需要管理员权限)。
* 点击页面右上角的 "调试插件",获取你的 `调试 Key` 和 `远程服务器地址` (例如 `http://<your-dify-domain>:5003`)。
2. **配置本地环境:**
* 在你的本地插件项目**根目录**下,找到或创建 `.env` 文件 (可从 `.env.example` 复制)。
* 编辑 `.env` 文件,填入调试信息:
```dotenv
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=<your-dify-domain-or-ip> # Dify 服务器地址
REMOTE_INSTALL_PORT=5003 # 调试端口
REMOTE_INSTALL_KEY=****-****-****-****-**** # 你的调试 Key
```
3. **启动本地插件服务:**
* 在插件项目根目录下,确保你的 Python 环境已激活(如果使用虚拟环境)。
* 运行主程序:
```bash
python -m main
```
* 观察终端输出,如果连接成功,通常会有相应的日志提示。
4. **在 Dify 中测试:**
* 刷新 Dify 的 "插件" 或 "模型供应商" 页面,你应该能看到你的本地插件实例,可能带有 "调试中" 标识。
* 前往 "设置" -> "模型供应商",找到你的插件,配置有效的 API 凭证。
* 在 Dify 应用中选择并使用你的模型进行测试。你在本地对 Python 代码的修改(保存后通常会自动重新加载服务)会直接影响 Dify 中的调用行为。使用 Dify 的调试预览功能可以帮助你检查输入输出和错误信息。
---
## 第 5 步: 打包与发布
当你完成了开发和调试,并对插件的功能满意后,就可以将其打包并贡献给 Dify 社区了。
1. **打包插件:**
* 停止本地调试服务 (`Ctrl+C`)。
* 在插件项目**根目录**下运行打包命令:
```bash
# 将 <provider_name> 替换为你的供应商目录名
dify plugin package models/<provider_name>
```
* 这将在项目根目录下生成一个 `<provider_name>.difypkg` 文件。
2. **提交 Pull Request:**
* 确保你的代码风格良好,并遵循 Dify 的[插件发布规范](https://docs.dify.ai/zh-hans/plugins/publish-plugins/publish-to-dify-marketplace)。
* 将你的本地 Git 提交推送到你 Fork 的 `dify-official-plugins` 仓库。
* 在 GitHub 上向 `langgenius/dify-official-plugins` 主仓库发起 Pull Request。在 PR 描述中清晰说明你所做的更改、添加的模型或功能,以及任何必要的测试说明。
* 等待 Dify 团队审核。审核通过并合并后,你的贡献将包含在官方插件中,并在 [Dify Marketplace](https://marketplace.dify.ai/) 上可用。
---
## 探索更多
* [模型 Schema 定义](/plugin-dev-zh/0412-model-schema) (模型 YAML 规范)
* [插件 Manifest 结构](/plugin-dev-zh/0411-general-specifications) (`manifest.yaml` 规范)
* [Dify Plugin SDK 参考](https://github.com/langgenius/dify-plugin-sdks) (查找基类、数据结构和错误类型)
* [Dify 官方插件仓库](https://github.com/langgenius/dify-official-plugins) (查看现有插件的实现)
{/*
Contributing Section
DO NOT edit this section!
It will be automatically generated by the script.
*/}
---
[编辑此页面](https://github.com/langgenius/dify-docs/edit/main/plugin-dev-zh/0222-creating-new-model-provider-extra.mdx) | [提交问题](https://github.com/langgenius/dify-docs/issues/new?template=docs.yml)