前言

WordPress 6.9 引入了 PHP AI Client SDK,为 WordPress 生态提供了统一的 AI 服务接入标准。本文分析我最近实现的 AI Provider for DeepSeek 插件,它是该 SDK 的 DeepSeek 适配实现,同时支持作为 Composer 包和 WordPress 插件两种使用方式。

插件架构概览

插件的目录结构遵循 PSR-4 规范:

wp-ai-provider-for-deepseek/
├── plugin.php                          # WordPress 插件入口
├── readme.txt                        # WordPress 插件描述
├── assets/images/deepseek.svg        # 供应商 Logo
└── src/
    ├── autoload.php                 # PSR-4 自动加载器
    ├── Provider/
    │   ├── DeepSeekProvider.php            # 主 Provider 类
    │   └── DeepSeekProviderAvailability.php # API Key 可用性检查器
    ├── Models/
    │   └── DeepSeekTextGenerationModel.php # 文本生成模型实现
    └── Metadata/
        └── DeepSeekModelMetadataDirectory.php # 模型元数据目录

核心实现分析

1. 插件入口与 Hook 注册(plugin.php)

插件通过三个关键 Hook 完成初始化:

// 注册 AI Provider(优先级 5,确保在 AI Client 之后加载)
add_action('init', __NAMESPACE__ . '\\register_provider', 5);

// 输出 Provider 数据到前端(确保 admin 页面可用)
add_action('admin_enqueue_scripts', __NAMESPACE__ . '\\ensure_provider_data_output', 5);

// 注册 Connector(WordPress 连接器系统)
add_action('wp_connectors_init', __NAMESPACE__ . '\\register_connector', 10);

// 自动审批 Connector 使用权限(优先级 20,确保 Approvals_Store 可用)
add_action('init', __NAMESPACE__ . '\\auto_approve_connector', 20);

设计亮点:

  • register_provider() 先检查 AiClient 类是否存在,避免依赖未安装时 fatal error
  • 使用 $registry->hasProvider() 防止重复注册
  • ensure_provider_data_output() 处理边缘情况:当 AI Client 的脚本未加载时,手动输出 window.aiProviderData

2. Provider 主类(DeepSeekProvider.php)

核心类继承自 AbstractApiProvider,实现四个工厂方法:

class DeepSeekProvider extends AbstractApiProvider
{
    // API 基础 URL
    protected static function baseUrl(): string
    {
        return 'https://api.deepseek.com';
    }

    // 创建模型实例(根据能力分发到具体模型类)
    protected static function createModel(...): ModelInterface
    {
        foreach ($capabilities as $capability) {
            if ($capability->isTextGeneration()) {
                return new DeepSeekTextGenerationModel(...);
            }
        }
        throw new \RuntimeException('Unsupported model capabilities');
    }

    // 创建 Provider 元数据(支持版本化特性)
    protected static function createProviderMetadata(): ProviderMetadata
    {
        $args = [
            'deepseek',
            'DeepSeek',
            ProviderTypeEnum::cloud(),
            'https://platform.deepseek.com/api_keys',
            RequestAuthenticationMethod::apiKey()
        ];

        // 1.2.0+ 支持描述
        if (version_compare(AiClient::VERSION, '1.2.0', '>=')) {
            $args[] = __('Text generation with DeepSeek models.', 'ai-provider-for-deepseek');
        }

        // 1.3.0+ 支持 Logo
        if (version_compare(AiClient::VERSION, '1.3.0', '>=')) {
            $args[] = dirname(__DIR__, 2) . '/assets/images/deepseek.svg';
        }

        return new ProviderMetadata(...$args);
    }

    // 自定义可用性检查器(用真实 API 请求验证 Key)
    protected static function createProviderAvailability(): ProviderAvailabilityInterface
    {
        return new DeepSeekProviderAvailability(static::class);
    }

    // 模型元数据目录(动态从 API 发现可用模型)
    protected static function createModelMetadataDirectory(): ModelMetadataDirectoryInterface
    {
        return new DeepSeekModelMetadataDirectory();
    }
}

版本兼容处理是这段代码的精华:通过 version_compare 逐步添加新特性支持,确保插件在旧版 PHP AI Client 上也能正常运行。

3. API Key 验证与缓存(DeepSeekProviderAvailability.php)

DeepSeek 不支持标准的”列出模型”端点,因此插件采用真实请求验证法:发送一个 max_tokens=1 的最小化请求,根据 HTTP 状态码判断 Key 是否有效。

public function isConfigured(): bool
{
    // 1. 检查缓存(5 分钟过期)
    $cached = get_transient(self::CACHE_KEY);
    if ($cached !== false) {
        return (bool) $cached;
    }

    // 2. 获取 API Key(优先 WordPress 选项,fallback 到环境变量)
    $apiKey = $this->getApiKey();
    if (empty($apiKey)) {
        $this->cacheResult(false);
        return false;
    }

    // 3. 发送最小化请求
    $response = wp_remote_post($url, [
        'headers' => [
            'Content-Type'  => 'application/json',
            'Authorization' => 'Bearer ' . $apiKey,
        ],
        'body' => json_encode([
            'model'    => 'deepseek-v4-flash',
            'messages' => [['role' => 'user', 'content' => 'test']],
            'max_tokens' => 1,
        ]),
        'timeout' => 30,
    ]);

    // 4. 根据状态码判断(200/400 = Key 有效,401/403 = Key 无效)
    $responseCode = wp_remote_retrieve_response_code($response);
    $isConfigured = !in_array($responseCode, [401, 403], true);

    // 5. 缓存结果
    $this->cacheResult($isConfigured);
    return $isConfigured;
}

缓存策略有效减少了重复验证请求:使用 WordPress Transient API,5 分钟过期。注意错误时不缓存,允许下次重试。

4. 模型元数据动态发现(DeepSeekModelMetadataDirectory.php)

插件通过调用 DeepSeek 的 /models 端点动态获取可用模型列表,而非硬编码:

protected function parseResponseToModelMetadataList(Response $response): array
{
    $responseData = $response->getData();
    
    // 定义 DeepSeek 模型支持的能力和选项
    $textGenerationCapabilities = [
        CapabilityEnum::textGeneration(),
        CapabilityEnum::chatHistory(),
    ];
    
    $textGenerationOptions = [
        new SupportedOption(OptionEnum::systemInstruction()),
        new SupportedOption(OptionEnum::candidateCount()),
        new SupportedOption(OptionEnum::maxTokens()),
        new SupportedOption(OptionEnum::temperature()),
        new SupportedOption(OptionEnum::topP()),
        new SupportedOption(OptionEnum::customOptions()),
        new SupportedOption(OptionEnum::inputModalities()),  // 修复:添加必需的能力声明
        new SupportedOption(OptionEnum::outputModalities()),
    ];

    // 将 API 响应映射为 ModelMetadata 对象
    $models = array_map(function ($modelData) use ($textGenerationCapabilities, $textGenerationOptions) {
        return new ModelMetadata(
            $modelData['id'],    // model ID(如 deepseek-v4-flash)
            $modelData['id'],    // model name
            $textGenerationCapabilities,
            $textGenerationOptions
        );
    }, $responseData['data']);

    // 排序:deepseek-v4-flash 优先,其次是 deepseek-v4-pro,然后字母序
    usort($models, [$this, 'modelSortCallback']);
    
    return $models;
}

注意inputModalitiesoutputModalities 的添加——这是修复 ModelRequirements::fromPromptData()generate_text() 调用的关键,缺少这两个选项会导致运行时错误。

5. 文本生成模型实现(DeepSeekTextGenerationModel.php)

DeepSeek API 兼容 OpenAI 的 Chat Completions 格式,因此直接继承 AbstractOpenAiCompatibleTextGenerationModel

class DeepSeekTextGenerationModel extends AbstractOpenAiCompatibleTextGenerationModel
{
    protected function createRequest(
        HttpMethodEnum $method,
        string $path,
        array $headers = [],
        $data = null
    ): Request {
        // 覆盖父类:使用 DeepSeek Provider 的 URL 而非 OpenAI 的
        return new Request(
            $method,
            DeepSeekProvider::url($path),  // => https://api.deepseek.com/chat/completions
            $headers,
            $data,
            $this->getRequestOptions()
        );
    }
}

这个实现非常简洁,因为大部分工作(请求格式化、响应解析、流式输出等)都由父类 AbstractOpenAiCompatibleTextGenerationModel 完成。只需要覆盖 createRequest() 将请求路由到 DeepSeek 的 API 地址即可。

Connector 审批系统

WordPress 的 AI Client 引入了 Connector 审批机制:每个插件使用 Connector(如 DeepSeek)前必须获得审批。本插件在 init hook(优先级 20)自动审批自己:

function auto_approve_connector(): void
{
    if (!class_exists('\WordPress\AI\Connector_Approval\Approvals_Store')) {
        return;  // 旧版 AI Client 不支持审批系统
    }

    $store = new \WordPress\AI\Connector_Approval\Approvals_Store();
    $plugin_basename = plugin_basename(__FILE__);
    $connector_id = 'deepseek';

    if ($store->is_approved($plugin_basename, $connector_id)) {
        return;  // 已审批,跳过
    }

    $store->set_approval($plugin_basename, $connector_id, true);
}

这避免了用户手动在设置页面审批插件,提升了用户体验。

使用方式

作为 WordPress 插件

// 1. 安装并激活插件(依赖 PHP AI Client 插件)
// 2. 配置 API Key
putenv('DEEPSEEK_API_KEY=your-api-key');

// 3. 使用
$result = AiClient::prompt('解释量子计算')
    ->usingProvider('deepseek')
    ->generateTextResult();

echo $result->toText();

作为 Composer 包

composer require wordpress/ai-provider-for-deepseek

<?php
use WordPress\AiClient\AiClient;
use WordPress\DeepSeekAiProvider\Provider\DeepSeekProvider;

// 注册 Provider
$registry = AiClient::defaultRegistry();
$registry->registerProvider(DeepSeekProvider::class);

// 设置 API Key
putenv('DEEPSEEK_API_KEY=your-api-key');

// 生成文本
$result = AiClient::prompt('Explain quantum computing')
    ->usingProvider('deepseek')
    ->generateTextResult();

echo $result->toText();

技术亮点总结

特性 实现方式 价值
版本兼容 version_compare 渐进式添加特性 支持旧版 AI Client,无 breaking change
API Key 验证 最小化真实请求 + Transient 缓存 准确验证 + 减少重复请求
动态模型发现 调用 /models API + 元数据映射 自动支持新模型,无需更新代码
Connector 审批 init hook 自动审批 零配置用户体验
OpenAI 兼容 继承 AbstractOpenAiCompatible* 复用大量现有逻辑,代码量极小
双模式运行 Composer 包 + WordPress 插件 最大灵活性,可在任何 PHP 项目中使用

结论

这个插件的实现展示了如何为一个新的 AI 服务商编写符合 WordPress PHP AI Client 标准的 Provider。核心要点:

  1. 继承正确的抽象类:文本生成继承 AbstractOpenAiCompatibleTextGenerationModel
  2. 实现四个工厂方法baseUrl()createModel()createProviderMetadata()createModelMetadataDirectory()
  3. 自定义可用性检查:当标准方法(列出模型)不可用时,用真实请求验证
  4. 版本兼容处理:使用 version_compare 渐进式添加新特性支持
  5. WordPress 集成:正确注册 Hook、Connector、审批

完整代码已开源在 GitHub,欢迎试用和贡献。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注