使用 laradock 运行,本地运行正常,线上部署异常

线上环境

php8.2+laravel11

相关代码

配置相关

<?php
namespace App\Services\AI;

use App\Contracts\AI\ProviderInterface;
use Illuminate\Support\Manager;

/**
 * AI 管理器类
  */
class AIManager extends Manager
{
    /**
 * 获取默认驱动
  *
 * @return string 默认驱动名称
  */
  public function getDefaultDriver(): string
  {
        return $this->config->get('ai.default', 'openai');
    }

    /**
 * 创建驱动
  *
 * @param string $driver 驱动名称
  * @return ProviderInterface
 */  protected function createDriver( $driver): ProviderInterface
    {
        $driverClass = match ($driver) {
            'doubao' => DouBao\DoubaoAIProvider::class,
            'openai' => OpenAI\OpenAIProvider::class,
            'aliyun' => ALiYun\ALiYunAIProvider::class,
            'deepSeek' => DeepSeek\DeepSeekProvider::class,
            default => throw new \InvalidArgumentException("未知的驱动类型: $driver"),
        };

        return $this->container->make($driverClass, [
            'config' => $this->config->get("ai.providers.$driver", []),
        ]);
    }
}
<?php
namespace App\Services\AI;

use App\Contracts\AI\ServiceInterface;
use Exception;
use Illuminate\Support\Facades\Validator;

/**
 * 抽象服务类
 */
abstract class AbstractService implements ServiceInterface
{
    protected readonly array $config; // 配置信息
    protected readonly string $version; // 服务版本

    /**
     * 构造函数
     *
     * @param array $config 服务配置
     * @param string $version 服务版本
     */
    public function __construct(array $config, string $version)
    {
        $this->config = $config;
        $this->version = $version;
    }

    /**
     * 定义参数的验证规则
     *
     * @return array 验证规则数组
     */
    abstract protected function rules(): array;

    /**
     * 验证输入参数
     *
     * @param array $params 参数数组
     * @return bool 验证是否通过
     * @throws Exception 如果验证失败
     */
    public function validate(array $params): bool
    {
        $validator = Validator::make($params, $this->rules());
        if ($validator->fails()) {
            throw new \InvalidArgumentException('错误的数据格式: ' . implode(', ', $validator->errors()->all()));
        }
        return true;
    }

    /**
     * 获取配置信息,带默认值
     *
     * @param string $key 配置键
     * @param mixed $default 默认值
     * @return mixed 配置值
     */
    protected function getConfig(string $key, mixed $default = null): mixed
    {
        return data_get($this->config, $key, $default);
    }
}

服务相关

<?php
namespace App\Services\AI\DouBao;

use App\Contracts\AI\ProviderInterface;
use App\Contracts\AI\ServiceInterface;
use Illuminate\Contracts\Container\Container;
use InvalidArgumentException;
/**
 * DouBao AI 提供者
 */
readonly class DouBaoAIProvider implements ProviderInterface
{
    private Container $container; // 服务容器
    private array $config; // 配置信息

    /**
     * 构造函数
     *
     * @param Container $container 服务容器
     * @param array $config 提供者配置信息
     */
    public function __construct(Container $container, array $config)
    {
        $this->container = $container;
        $this->config = $config;
    }

    /**
     * 检查支持的服务类型和版本
     *
     * @param string $type 服务类型
     * @param string $version 服务版本
     * @return bool 是否支持该服务
     */
    public function supports(string $type, string $version): bool
    {
        return isset($this->config['versions'][$type]) &&
            in_array($version, $this->config['versions'][$type], true);
    }

    /**
     * 获取指定服务
     *
     * @param string $type 服务类型
     * @param string $version 服务版本
     * @return ServiceInterface 指定服务实例
     * @throws InvalidArgumentException|\Illuminate\Contracts\Container\BindingResolutionException 如果不支持指定类型或版本
     */
    public function getService(string $type, string $version = ''): ServiceInterface
    {
        $serviceClass = "App\\Services\\AI\\DouBao\\" . ucfirst($type) . "Service";

        if (!class_exists($serviceClass)) {
            throw new InvalidArgumentException("服务类 '$serviceClass' 未找到。");
        }

        return $this->container->make($serviceClass, [
            'config' => $this->config,
            'version' => $version,
        ]);
    }
}
<?php
namespace App\Services\AI\DouBao;

use App\Services\AI\AbstractService;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Client\Response;
/**
 * 文本服务
 */
class TextService extends AbstractService
{
    /**
     * 定义参数的验证规则
     *
     * @return array 验证规则数组
     */
    protected function rules(): array
    {
        return [
            'prompt' => 'required|array',
            'prompt.*.role' => 'required|string',
            'prompt.*.content' => 'required|string',
            'max_tokens' => ['sometimes', 'integer', 'min:1', 'max:4000'],
            'temperature' => ['sometimes', 'numeric', 'between:0,2'],
        ];
    }

    /**
     * 执行服务
     *
     * @param array $params 参数数组
     * @return array 执行结果
     *@throws Exception 如果参数验证失败或请求失败
     */
    public function execute(array $params): array
    {
        $this->validate($params);

        $apiKey = $this->getConfig('access_key');
        $url = $this->getConfig('api_url') . $this->getConfig('text.url');

        $body = [
            'model' => $this->getConfig('text.model'),
            'messages' => $params['prompt'],
            'max_tokens' => $params['max_tokens'] ?? 150,
            'temperature' => $params['temperature'] ?? 0.7,
        ];

        try {
            $response = Http::withHeaders([
                'Authorization' => 'Bearer ' . $apiKey,
                'Content-Type' => 'application/json',
            ])->post($url, $body);

            return $this->handleResponse($response);
        } catch (\Throwable $e) {
            Log::error('DouBao 文本服务错误', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);
            throw new Exception('调用 DouBao 文本服务失败: ' . $e->getMessage());
        }
    }

    /**
     * 处理 API 响应
     *
     * @param Response $response API 响应
     * @return array 处理后的结果
     * @throws Exception 如果响应格式无效
     */
    protected function handleResponse(Response $response): array
    {
        $responseBody = json_decode($response->getBody()->getContents(), true);

        if (isset($responseBody['choices'][0]['message']['content'])) {
            return [
                'role' => 'assistant',
                'content' => $responseBody['choices'][0]['message']['content'],
            ];
        } else {
            Log::error('DouBao API 响应格式错误', ['response' => $responseBody]);
            throw new Exception('无效的响应格式。');
        }
    }
}

报错

  try {
            $result = Ai::driver(driver:'doubao')
                ->getService(type:'text')
                ->execute([
                    'prompt' => $data
                ]);
            return response()->json($result);
        } catch (\Exception $e) {
            return response()->json(['error' => '请求失败,错误信息:' . $e->getMessage()], 500);
        }

本地

使用 laradock 运行,本地运行正常,线上部署异常
线上
使用 laradock 运行,本地运行正常,线上部署异常

这是爪子问题嘛,这文件怎么可能不存在

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 4

file

大小写

1个月前 评论
Shine-x (楼主) 1个月前

估计是大小写问题 DouBaoAIProvider 对 DoubaoAIProvider 错

1个月前 评论

class找不到的,一律默认文件名有问题~

1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!