Laravel 优雅的对接 DeepSeek API,以及踩过的那些坑

前言

随着AI的发展,任何应用如果不对接AI就好像跟不上潮流。Laravel作为 WEB 开发中最为优雅的存在,也应该在这场技术革命中占据自己的一席之地。本文将分享如何在Laravel项目中优雅地集成 DeepSeek API(支持符合OpenAi API规范的所有AI服务商如:硅基流动),并记录下我在这个过程中踩过的坑,希望能为同样想要拥抱AI的Laravel开发者们提供一些参考。

一、技术依赖

在开始之前,首先你需要创建一个 Laravel 应用。如果你还没有创建过 Laravel 项目,可以参考 Laravel 官方文档 中的安装指南,使用 Composer 快速创建一个全新的 Laravel 项目。例如,运行以下命令:

composer create-project laravel/laravel laravel-deepseek

接下来,由于 DeepSeek API 兼容 OpenAI API,我们可以直接使用社区中成熟的第三方扩展包 openai-php/laravel 来简化 API 调用。这个扩展包提供了对 OpenAI API 的封装,并且与 Laravel 框架深度集成,能够帮助我们快速实现与 DeepSeek 的对接。

你可以通过 Composer 安装这个扩展包:

composer require openai-php/laravel

安装完成后,运行以下 Artisan 命令来发布扩展包的配置文件:

php artisan openai:install

这会在 config 目录下生成一个 openai.php 配置文件,你可以在其中配置 DeepSeek API 的密钥,当然你也可以通过 .env 文件来配置,将 DeepSeek 提供的 API Key 填入.envOPENAI_API_KEY 字段中:

OPENAI_API_KEY=
OPENAI_ORGANIZATION=

如果你不知道 DeepSeek API_KEY 如何申请,请参考DeepSeek 文档。

踩坑点一

截至:2025年3月13日,openai-php/laravel 目前暂不支持使用 base_uri 来配置第三方URL,我们可以通过修改依赖文件的方式来配置DeepSeek API 地址,首先我们在 config/openai.php 文件中添加配置:

'base_uri' => env('OPENAI_BASE_URI', 'https://api.openai.com/v1'),

.env 环境中添加配置:

OPENAI_BASE_URI=https://api.deepseek.com

之后我们找到vendor/openai-php/laravel/src/ServiceProvider.php 文件,修改以下代码:

public function register(): void
{
    $this->app->singleton(ClientContract::class, static function (): Client {
    $apiKey = config('openai.api_key');
    $organization = config('openai.organization');
    // 添加 baseUri 配置
    $baseUri = config('openai.base_uri');
    if (! is_string($apiKey) || ($organization !== null && ! is_string($organization))) {
        throw ApiKeyIsMissing::create();
    }

    return OpenAI::factory()
        ->withApiKey($apiKey)
        ->withOrganization($organization)
        // 关联 baseUri
        ->withBaseUri($baseUri)
        ->withHttpClient(new \GuzzleHttp\Client(['timeout' => config('openai.request_timeout', 30), 'verify' => false,]))
        ->make();
    });

    $this->app->alias(ClientContract::class, 'openai');
    $this->app->alias(ClientContract::class, Client::class);
}

以上内容可能会随着 openai-php/laravel 的更新来陆续支持其它 URL,具体可以参考 issues: Can we customize OPENAI_BASE_URL, just like we defined OPENAI_API_KEY?,你也可以通过这种方式来适配其它大模型。

了通过这种方式,我们可以快速搭建起 Laravel 与 DeepSeek API 的桥梁,为后续的功能开发奠定基础。

二、接口实现

在 Laravel 中,与 DeepSeek API 的对接可以通过多种方式实现,具体取决于你的应用场景和需求。Laravel 提供了强大的 Streamed Responses ,来实现流式响应(例如实时获取 AI 生成的文本)这种场景。

以下是一个简单的示例,展示如何使用 Streamed Responses 来实时获取 DeepSeek API 的响应并返回给客户端:

    public function test(Request $request): StreamedResponse
    {
        $message = $request->getContent();
        return response()->eventStream(function () use ($message) {
            yield $message;
            $msgData = json_decode($message, true);
            $stream = OpenAI::chat()->createStreamed([
                'model' => 'deepseek-chat',
                'messages' => [
                    ['role' => 'user', 'content' => $msgData['message']],
                ]
            ]);
            foreach ($stream as $response) {
                yield $response->choices[0];
            }
        }, endStreamWith: new StreamedEvent(event: 'update', data: 'end'));
    }

在这个示例中,我们通过 $message = $request->getContent() 方法来接受前端提供的 JSON 信息,并通过json_decode($message, true); 来解析请求,通过 response()->eventStream() 方法创建了一个流式响应。每次从 DeepSeek API 接收到数据块时,都会通过 yield 将其推送到客户端。

踩坑点二

由于DeepSeek API 响应的内容不包含 created 字段,所以在 openai-php 解析的时候,会出现 Undefined array key created 错误,我们需要将 created 字段设置为可选字段,找到文件vendor\openai-php\client\src\Responses\Models\RetrieveResponse.php, 修改以下代码。

public static function from(array $attributes, MetaInformation $meta): self
    {
        return new self(
            $attributes['id'],
            $attributes['object'],
            // 将created 修改为可选字段
            $attributes['created'] ?? 1,
            $attributes['owned_by'],
            $meta,
        );
    }

以上内容可能会随着 openai-php/client 的更新而修复,具体可以参考 Pull requests: Fix undefined array key “created”,你也可以通过这种方式来适配其它大模型。

前端实现

在前端与 Laravel 后端对接时,我们可以通过 fetch API 向 DeepSeek 的接口发送请求,并处理返回的流式数据。以下是一个完整的前端请求示例:

1. 发送请求

使用 fetch 发送 POST 请求时,可以在 headers 中配置请求头,并在 body 中传递所需的数据。以下是一个示例:

const response = await fetch('/your_api_route', {
    method: 'POST',
    headers: {
        'Accept': '*/*',
        'content-type': 'text/plain;charset=UTF-8', // 使用 JSON 格式传递数据
    },
    body: JSON.stringify({ message: '你的消息内容' }), // 将消息内容序列化为 JSON
});

在这个示例中:

  • method: 'POST' 指定请求方法为 POST。
  • headers 中设置了 AcceptContent-Type,确保服务器能够正确解析请求。
  • body 中通过 JSON.stringify 将消息内容序列化为 JSON 格式。

2. 处理流式响应

DeepSeek 的流式接口返回的是一个标准的ReadableStream 对象。我们可以通过 for await...of 语法对数据流进行异步迭代,逐块处理返回的数据。以下是一个完整的示例:

let totalSize = 0; // 用于记录接收到的数据总大小

try {
    // 异步迭代 response.body
    for await (const chunk of response.body) {
        // 处理每个数据块
        const textChunk = new TextDecoder().decode(chunk); // 将二进制数据解码为字符串
        console.log('Received chunk:', textChunk);

        // 更新总大小
        totalSize += chunk.length;
    }

    // 输出接收到的数据总大小
    console.log('Total data size received:', totalSize, 'bytes');
} catch (error) {
    console.error('Error while reading the stream:', error);
}

在这个示例中:

  • response.body 是一个 ReadableStream 对象,表示服务器返回的流式数据。
  • 使用 for await…of 逐块读取数据流,并通过 TextDecoder 将二进制数据解码为字符串。
  • 每次接收到数据块时,更新 totalSize 以记录总数据大小。
  • 使用 try…catch 捕获可能的错误,确保程序的健壮性。

3. 实时更新 UI

如果你需要将流式数据实时展示在页面上,可以将每个数据块追加到 DOM 元素中。例如:

const outputElement = document.getElementById('output'); // 获取用于展示结果的 DOM 元素

for await (const chunk of response.body) {
    const textChunk = new TextDecoder().decode(chunk); // 解码数据块
    outputElement.textContent += textChunk; // 将数据追加到页面中
}

通过以上方式,你可以轻松实现前端与 DeepSeek API 的流式交互,并将数据实时展示给用户。

完整演示

在实际业务场景中,我们通常需要存储和记录会话内容,并利用大模型的缓存机制来实现对话上下文的管理。XinAdmin 作为一个全栈开发框架,不仅集成了 DeepSeek API,还完整实现了与大模型的对话功能。你可以轻松地将 AI 大模型能力应用到你的应用中,同时实现精细的权限控制。

以下是一个XinAdmin应用的截图示例:
Laravel 优雅的对接 DeepSeek API,以及踩过的哪些坑

欢迎体验

我们致力于为开发者提供简单、高效的工具,帮助你将 AI 能力快速融入实际业务中。无论是对话系统、内容生成,还是其他 AI 应用场景,XinAdmin 都能为你提供强大的支持。

欢迎大家前往 GitHub 和官方网站体验,并提出宝贵的意见和建议!如果你有任何问题,也欢迎随时联系我。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 4

没有推理内容字段吧 reasoning_content

6天前 评论
xineny (楼主) 6天前

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