endpoint = $endpoint ?? config('agent.provider.endpoint', ''); $this->timeoutSeconds = (int) config('agent.provider.timeout_seconds', 30); $this->connectTimeoutSeconds = (int) config('agent.provider.connect_timeout_seconds', 5); $this->retryTimes = (int) config('agent.provider.retry_times', 1); $this->retryBackoffMs = (int) config('agent.provider.retry_backoff_ms', 500); } /** * @param array $context * @param array $options */ public function generate(array $context, array $options = []): string { if (empty($this->endpoint)) { // placeholder to avoid accidental outbound calls when未配置 return (new DummyAgentProvider())->generate($context, $options); } $payload = [ 'context' => $context, 'options' => $options, ]; $attempts = $this->retryTimes + 1; $lastException = null; $lastResponseBody = null; $lastStatus = null; for ($attempt = 1; $attempt <= $attempts; $attempt++) { try { $response = Http::connectTimeout($this->connectTimeoutSeconds) ->timeout($this->timeoutSeconds) ->post($this->endpoint, $payload); $lastStatus = $response->status(); $lastResponseBody = $response->body(); if ($response->successful()) { $data = $response->json(); return is_string($data) ? $data : ($data['content'] ?? ''); } $retryable = $lastStatus === 429 || $lastStatus >= 500; if ($retryable && $attempt < $attempts) { usleep($this->retryBackoffMs * 1000); continue; } throw new ProviderException( 'HTTP_ERROR', 'Agent provider failed', $retryable, $lastStatus, $lastResponseBody ); } catch (ConnectionException $exception) { $lastException = $exception; if ($attempt < $attempts) { usleep($this->retryBackoffMs * 1000); continue; } } catch (\Throwable $exception) { $lastException = $exception; break; } } $rawMessage = $lastException ? $lastException->getMessage() : $lastResponseBody; if ($lastException instanceof ConnectionException) { throw new ProviderException( 'CONNECTION_FAILED', 'Agent provider connection failed', true, $lastStatus, $rawMessage ); } throw new ProviderException( 'UNKNOWN_ERROR', 'Agent provider error', false, $lastStatus, $rawMessage ); } }