config = config('ai'); $this->currentService = $this->config['default']; } /** * 聊天对话 */ public function chat(array $messages, array $options = []): array { $cacheKey = $this->getCacheKey('chat', $messages, $options); // 检查缓存 if ($this->config['cache']['enabled']) { $cached = Cache::get($cacheKey); if ($cached) { return $cached; } } $result = $this->callService('chat', $messages, $options); // 缓存结果 if ($this->config['cache']['enabled'] && $result['success']) { Cache::put($cacheKey, $result, $this->config['cache']['ttl']); } return $result; } /** * 调用AI服务 */ protected function callService(string $action, array $data, array $options = []): array { $serviceConfig = $this->getServiceConfig(); // 记录请求日志 if ($this->config['logging']['enabled'] && $this->config['logging']['log_requests']) { Log::info('AI服务请求', [ 'service' => $this->currentService, 'action' => $action, 'data' => $this->filterSensitiveData($data), ]); } try { $response = $this->makeRequest($serviceConfig, $data, $options); // 记录响应日志 if ($this->config['logging']['enabled'] && $this->config['logging']['log_responses']) { Log::info('AI服务响应', [ 'service' => $this->currentService, 'action' => $action, 'response' => $this->filterSensitiveData($response), ]); } return $response; } catch (\Exception $e) { // 记录错误日志 if ($this->config['logging']['enabled'] && $this->config['logging']['log_errors']) { Log::error('AI服务错误', [ 'service' => $this->currentService, 'action' => $action, 'error' => $e->getMessage(), ]); } // 尝试降级服务 if ($this->config['fallback']['enabled']) { return $this->fallbackService($action, $data, $options); } return [ 'success' => false, 'error' => 'AI服务暂时不可用: ' . $e->getMessage(), 'service' => $this->currentService, ]; } } /** * 发送HTTP请求 */ protected function makeRequest(array $serviceConfig, array $data, array $options): array { $url = $serviceConfig['api_url']; $timeout = $serviceConfig['timeout'] ?? 30; $headers = $this->buildHeaders($serviceConfig); $body = $this->buildRequestBody($serviceConfig, $data, $options); $response = Http::withHeaders($headers) ->timeout($timeout) ->post($url, $body); if ($response->successful()) { return $this->parseResponse($serviceConfig, $response->json()); } else { throw new \Exception('API请求失败: ' . $response->status() . ' - ' . $response->body()); } } /** * 构建请求头 */ protected function buildHeaders(array $serviceConfig): array { $headers = [ 'Content-Type' => 'application/json', ]; switch ($this->currentService) { case 'openai': $headers['Authorization'] = 'Bearer ' . $serviceConfig['api_key']; break; case 'azure_openai': $headers['api-key'] = $serviceConfig['api_key']; break; case 'anthropic': $headers['x-api-key'] = $serviceConfig['api_key']; $headers['anthropic-version'] = '2023-06-01'; break; case 'aliyun_qwen': $headers['Authorization'] = 'Bearer ' . $serviceConfig['api_key']; break; } return $headers; } /** * 构建请求体 */ protected function buildRequestBody(array $serviceConfig, array $data, array $options): array { $body = []; switch ($this->currentService) { case 'openai': case 'local': $body = [ 'model' => $serviceConfig['model'], 'messages' => $data['messages'] ?? [], 'max_tokens' => $options['max_tokens'] ?? $serviceConfig['max_tokens'], 'temperature' => $options['temperature'] ?? $serviceConfig['temperature'], 'stream' => $options['stream'] ?? false, ]; break; case 'azure_openai': $body = [ 'messages' => $data['messages'] ?? [], 'max_tokens' => $options['max_tokens'] ?? $serviceConfig['max_tokens'], 'temperature' => $options['temperature'] ?? $serviceConfig['temperature'], 'stream' => $options['stream'] ?? false, ]; break; case 'anthropic': $body = [ 'model' => $serviceConfig['model'], 'messages' => $data['messages'] ?? [], 'max_tokens' => $options['max_tokens'] ?? $serviceConfig['max_tokens'], 'temperature' => $options['temperature'] ?? $serviceConfig['temperature'], 'stream' => $options['stream'] ?? false, ]; break; case 'aliyun_qwen': $body = [ 'model' => $serviceConfig['model'], 'messages' => $data['messages'] ?? [], 'max_tokens' => $options['max_tokens'] ?? $serviceConfig['max_tokens'], 'temperature' => $options['temperature'] ?? $serviceConfig['temperature'], 'stream' => $options['stream'] ?? false, ]; break; } return $body; } /** * 解析响应 */ protected function parseResponse(array $serviceConfig, array $response): array { switch ($this->currentService) { case 'openai': case 'azure_openai': case 'local': $content = $response['choices'][0]['message']['content'] ?? ''; $tokens = $response['usage']['total_tokens'] ?? 0; break; case 'anthropic': $content = $response['content'][0]['text'] ?? ''; $tokens = $response['usage']['input_tokens'] + $response['usage']['output_tokens'] ?? 0; break; case 'aliyun_qwen': $content = $response['choices'][0]['message']['content'] ?? ''; $tokens = $response['usage']['total_tokens'] ?? 0; break; default: $content = ''; $tokens = 0; } return [ 'success' => true, 'content' => $content, 'tokens' => $tokens, 'raw_response' => $response, ]; } /** * 降级服务 */ protected function fallbackService(string $action, array $data, array $options): array { $services = $this->config['fallback']['service_order']; $currentIndex = array_search($this->currentService, $services); if ($currentIndex === false || $currentIndex >= count($services) - 1) { return [ 'success' => false, 'error' => '所有AI服务都不可用', ]; } // 切换到下一个服务 $nextService = $services[$currentIndex + 1]; $this->currentService = $nextService; Log::warning('AI服务降级', [ 'from' => $services[$currentIndex], 'to' => $nextService, 'action' => $action, ]); // 重试请求 return $this->callService($action, $data, $options); } /** * 获取服务配置 */ protected function getServiceConfig(): array { $serviceConfig = $this->config['services'][$this->currentService] ?? []; if (empty($serviceConfig)) { throw new \Exception("AI服务配置不存在: {$this->currentService}"); } return $serviceConfig; } /** * 过滤敏感数据 */ protected function filterSensitiveData(array $data): array { if (!$this->config['security']['filter_sensitive_data']) { return $data; } $filtered = $data; $sensitiveKeywords = $this->config['security']['sensitive_keywords']; array_walk_recursive($filtered, function (&$value, $key) use ($sensitiveKeywords) { foreach ($sensitiveKeywords as $keyword) { if (stripos($key, $keyword) !== false || stripos($value, $keyword) !== false) { $value = '***FILTERED***'; break; } } }); return $filtered; } /** * 生成缓存键 */ protected function getCacheKey(string $action, array $data, array $options): string { $prefix = $this->config['cache']['prefix']; $hash = md5(serialize([$action, $data, $options, $this->currentService])); return $prefix . $hash; } /** * 获取当前服务 */ public function getCurrentService(): string { return $this->currentService; } /** * 设置服务 */ public function setService(string $service): void { if (isset($this->config['services'][$service])) { $this->currentService = $service; } else { throw new \Exception("不支持的AI服务: {$service}"); } } /** * 获取可用服务列表 */ public function getAvailableServices(): array { return array_keys($this->config['services']); } /** * 测试服务连接 */ public function testConnection(): array { try { $testMessages = [ ['role' => 'system', 'content' => '你是一个测试助手'], ['role' => 'user', 'content' => '请回复"OK"'], ]; $result = $this->chat($testMessages, [ 'max_tokens' => 10, 'temperature' => 0.1, ]); return [ 'success' => $result['success'], 'service' => $this->currentService, 'response_time' => '正常', 'message' => $result['success'] ? '连接正常' : ($result['error'] ?? '连接失败'), ]; } catch (\Exception $e) { return [ 'success' => false, 'service' => $this->currentService, 'error' => $e->getMessage(), 'message' => '连接失败', ]; } } /** * 获取服务状态 */ public function getServiceStatus(): array { $services = []; foreach ($this->config['services'] as $name => $config) { $originalService = $this->currentService; $this->currentService = $name; try { $testResult = $this->testConnection(); $services[$name] = [ 'status' => $testResult['success'] ? 'online' : 'offline', 'message' => $testResult['message'], ]; } catch (\Exception $e) { $services[$name] = [ 'status' => 'error', 'message' => $e->getMessage(), ]; } $this->currentService = $originalService; } return $services; } }