false, 'provider' => self::PROVIDER_NONE, 'api_key' => '', 'api_base' => 'https://api.openai.com/v1', 'model' => 'gpt-3.5-turbo', 'max_tokens' => 1000, 'rate_limit' => 10, // 每分钟最大调用次数 ]; $saved = get_option('aksrt_ai_settings', []); if (!is_array($saved)) $saved = []; return array_merge($defaults, $saved); } /** * 更新 AI 配置 */ public function update_config(array $config): bool { $current = $this->get_config(); $merged = array_merge($current, $config); // 如果 api_key 为掩码,保留原值 if (isset($config['api_key']) && $config['api_key'] === '***') { $merged['api_key'] = $current['api_key']; } return update_option('aksrt_ai_settings', $merged); } /** * 检查 AI 是否可用 */ public function is_available(): bool { $config = $this->get_config(); return !empty($config['enabled']) && $config['provider'] !== self::PROVIDER_NONE && !empty($config['api_key']); } /** * 获取 AI 状态 */ public function get_status(): array { $config = $this->get_config(); return [ 'enabled' => !empty($config['enabled']), 'provider' => $config['provider'], 'configured' => !empty($config['api_key']), 'model' => $config['model'], 'api_key' => !empty($config['api_key']) ? '***' : '', 'rate_limit' => $config['rate_limit'], 'available' => $this->is_available(), ]; } /** * 调用 AI 生成 * * @param string $prompt 提示词 * @param array $options 选项 * @return array{success: bool, content?: string, message?: string} */ public function generate(string $prompt, array $options = []): array { if (!$this->is_available()) { return ['success' => false, 'message' => 'AI 服务未启用或未配置']; } // 速率限制检查 if (!$this->check_rate_limit()) { return ['success' => false, 'message' => '请求过于频繁,请稍后再试']; } $config = $this->get_config(); $max_tokens = (int) ($options['max_tokens'] ?? $config['max_tokens']); $temperature = (float) ($options['temperature'] ?? 0.7); $response = wp_remote_post($config['api_base'] . '/chat/completions', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $config['api_key'], ], 'body' => wp_json_encode([ 'model' => $config['model'], 'messages' => [ ['role' => 'system', 'content' => '你是一个内容助手,提供建议性内容。输出仅作为参考建议,不会自动应用到线上内容。'], ['role' => 'user', 'content' => $prompt], ], 'max_tokens' => $max_tokens, 'temperature' => $temperature, ]), 'timeout' => 30, ]); // 记录调用 $this->record_call(); if (is_wp_error($response)) { return ['success' => false, 'message' => 'API 请求失败: ' . $response->get_error_message()]; } $body = json_decode(wp_remote_retrieve_body($response), true); $status_code = wp_remote_retrieve_response_code($response); if ($status_code !== 200) { $error_msg = $body['error']['message'] ?? '未知错误'; return ['success' => false, 'message' => 'API 错误 (' . $status_code . '): ' . $error_msg]; } $message = $body['choices'][0]['message'] ?? []; $content = $message['content'] ?? ''; // 兼容推理模型(如 mimo-v2.5-pro):content 为空时取 reasoning_content if (empty($content) && !empty($message['reasoning_content'])) { $content = $message['reasoning_content']; } if (empty($content)) { return ['success' => false, 'message' => 'AI 返回内容为空(可能 max_tokens 不足)']; } return [ 'success' => true, 'content' => trim($content), 'reasoning' => $message['reasoning_content'] ?? '', 'usage' => $body['usage'] ?? [], 'finish_reason' => $body['choices'][0]['finish_reason'] ?? '', ]; } /** * 速率限制检查 */ private function check_rate_limit(): bool { $config = $this->get_config(); $limit = (int) $config['rate_limit']; if ($limit <= 0) return true; $key = 'aksrt_ai_rate_' . get_current_user_id(); $count = (int) get_transient($key); return $count < $limit; } /** * 记录调用 */ private function record_call(): void { $key = 'aksrt_ai_rate_' . get_current_user_id(); $count = (int) get_transient($key); set_transient($key, $count + 1, MINUTE_IN_SECONDS); } }