chatgpt网页版源代码 基于 OpenAI API + Laravel 快速构建网页版 ChatGPT

默认分类1年前 (2023)发布 admin
2,527 0
ChatGPT国内版

在上篇教程中我们提到, 近期发布了全新的 API,并且在该教程中,学院君给大家演示了如何基于这个 API 快速实现命令行版 ,今天,让我们看看如何利用它结合 10 构建 网页版,我们把这个网页克隆版的 命名为 。

如果您想直接跳转到源代码,可以在我的 上找到它。

这个新的 API 模型提示方式和之前有点不同,因为它针对的是“聊天”自动完成,所以我们不仅仅是发送一个简单的字符串作为提示,而是发送一整段聊天对话,然后 AI 模型将自动完成对话功能。

下面是我们今天要完成的 网页克隆版 的最终样子,比较简陋,但该有的核心功能也都具备了:

整体功能并不复杂,这里我们使用网页应用开发神器 框架结合 CSS 快速完成应用的开发。

PS:经过数次迭代,这个项目最新面孔已经是这样的了,成为一个支持文字、语音、翻译、画图的多功能聊天机器人,你可以通过这个链接进行体验 ——

初始化项目

作为起点,我们使用 安装器初始化一个新的 10 应用程序:

laravel new geekchat

不了解 框架的可以看下官方文档。

然后通过 安装 PHP 扩展包,该扩展包可用于在 PHP 项目中调用 API 接口:

composer require geekr/openai-laravel

接下来,我们需要发布上面这个扩展包的配置文件并设置 API 密钥。

要发布配置文件,运行以下命令即可:

php artisan vendor:publish --provider="GeekrOpenAILaravelServiceProvider"

发布完成后,可以在 .env 文件中设置 API 密钥,如下所示:

OPENAI_API_KEY="你的 OpenAI 密钥"

此外,这个扩展包还支持代理配置,已解决国内调用不了 接口的问题:

OPENAI_BASE_URI=open.aiproxy.xyz

构建会话表单

我们想要的是一个聊天样式的 UI,所以需要在默认首页视图文件 .blade.php 中添加一个简单的问题输入字段以及一个“重置会话”按钮,就像我们在 中重置当前会话一样:

这个输入字段需要放在一个表单中,该表单用于将输入的问题提交到 应用程序的 POST 路由 /chat,从而完成与 的会话:

        @csrf
        
        
        
    

接下来,我们将完成后端处理会话与重置会话的核心功能。

实现会话核心功能

chatgpt网页版源代码 基于 OpenAI API + Laravel 快速构建网页版 ChatGPT

我们将新建一个控制器 处理会话相关的业务逻辑:

然后在控制器中定义会话处理方法,核心逻辑其实和上篇命令行版 大同小异,只是换了一种语言实现而已:

session()->get('messages', [
            ['role' => 'system', 'content' => 'You are GeekChat - A ChatGPT clone. Answer as concisely as possible.']
        ]);
        // 用户消息
        $messages[] = ['role' => 'user', 'content' => $request->input('message')];
        $response = OpenAI::chat()->create([
            'model' => 'gpt-3.5-turbo',
            'messages' => $messages
        ]);
        // 响应消息
        $messages[] = ['role' => 'assistant', 'content' => $response->choices[0]->message->content];
        $request->session()->put('messages', $messages);
        return redirect('/');
    }
}

正如我上面提到的,此处的提示有点不同 —— 它是来自用户的消息和从 获得的响应的混合物。新的聊天 API 还允许我们定义“系统”消息,这是某种通用指令,用于告诉聊天模型其一般用途应该是什么。

这里我们通过 $ 数组聚合提示,作为默认值,我们将在其中放置我们的“系统”消息,然后将用户问题消息放进来,就可以使用这个数组并执行 API 请求:

$response = OpenAI::chat()->create([
    'model' => 'gpt-3.5-turbo',
    'messages' => $messages
]);

拿到 聊天响应消息后,我们还要将其添加到我们的 $ 数组中:

$messages[] = ['role' => 'assistant', 'content' => $response->choices[0]->message->content];

请注意,这里我们添加来自 API 响应消息时,使用了 “” 角色将其添加到 $ 数组中,以表明这是来自 API 而不是用户的消息。

这样一来,我们就能够提出涉及先前消息和回复上下文的问题了。

由于这些“消息”需要随时间增长,我们需要将它们存储在某个地方,对于这个简单的克隆版本,我们将把消息存储在会话中。现在我们的 $ 数组包含了我们需要的所有消息,我们可以将其存储回会话中并重定向回去:

$request->session()->put('messages', $messages);
return redirect('/');

就是这样!下一次用户发送消息时,我们将重用会话中的消息并将新消息附加到其中,就像 一样。

最后不要忘了在路由文件 /web.php 中定义路由与控制器方法的映射关系:

Route::post('/chat', ChatController::class . '@chat');

相比之下,重置会话就简单多了,只需要清空会话中的消息数据,然后重定向到首页即可,还是在 中定义重置会话方法:

/**
 * Reset the session.
 */
public function reset(Request $request): RedirectResponse
{
    $request->session()->forget('messages');
    return redirect('/');
}

对应的路由映射关系如下:

Route::get('/reset', ChatController::class . '@reset');

完成前端展示

现在,我们在会话中有了所有的消息(包含来自 响应和用户的消息),我们需要做的就是将它们传递给视图并向用户显示它们。这一切都是在首页路由中完成的。

为了不显示内部的“系统”消息,我们可以在将消息传递给视图之前从消息数组中删除它:

Route::get('/', function () {
    $messages = collect(session('messages', []))->reject(fn ($message) => $message['role'] === 'system');
    return view('welcome', [
        'messages' => $messages
    ]);
});

在视图中,我现在只是循环遍历消息,根据其来自用户还是来自 给它们不同的背景颜色,然后使用 解析器解析消息内容并渲染:

@foreach($messages as $message)
    
@if ($message['role'] === 'assistant') GeekChat @else @endif

{!! IlluminateMailMarkdown::parse($message['content']) !!}

@endforeach

chatgpt网页版源代码 基于 OpenAI API + Laravel 快速构建网页版 ChatGPT

这就是所有需要做的事情,得益于新的 API,你可以轻松构建自己的 克隆版。

基于 开发部署

如果你对 部署,还可以使用 自带的 Sail 扩展包通过 在本地部署启动应用,开始之前需要先安装 Sail 扩展包:

composer require laravel/sail --dev

由于这个项目比较简单,没有数据库、缓存、消息队列,所以我们直接通过 Sail 启动应用就好了(确保此时 已经启动并运行):

./vendor/bin/sail up -d

如果启动过程中出现类似下面这样的错误:

#0 274.4 tee: /etc/apt/keyrings/ppa_ondrej_php.gpg: No such file or directory

多半是网络原因导致的,可以参照这篇教程设置 软件源的国内镜像,或者参考这个项目的 代码仓库,我把很多不需要数据库、前端依赖都删除掉了,同时移除了本地对 PHP//Sail 的依赖,保留最小可用资源,直接通过 – up -d 启动即可。

启动成功后,就可以通过 在浏览器中访问 网页版了:

如果是部署到生产环境,可以使用 php-fpm 作为 http 服务器,也可以使用 扩展包提供的高性能 http 服务器选项 —— 或者 ,我这个 是基于 + 部署的。

最后,我们用以下对话结束今天的教程:

如果你没有 账号,想要快速体验,可以访问这个演示版:,演示版背后的源码就是今天这篇教程演示的。

附录:国内无法调用 接口的解决办法设置 HTTP 代理

不少同学反映国内无法直接通过代码调用 接口,我在写这个示例项目的时候也遇到这个问题,解决办法也不难,就是在发起 HTTP 请求的时候在请求头中添加代理设置:

'proxy' => 'http://127.0.0.1:10809',
'verify' => false,

如果是通过 curl 发起的请求也是参照这个思路。Go HTTP 代理设置参考这个代码配置。

之前使用的 `-php/` 这个扩展包不支持对代理进行设置,也不支持对 和 进行扩展(都是通过 final 修饰):

因此我重新开发了一个扩展包,也就是今天项目中使用的 geekr/-,主要就是在原来的基础上支持配置代理(以域名代理的方式实现,不是这种配置本地代理,本地代理只能本地使用)。

此外,你还可以使用另一个 PHP 扩展包替代 —— /open-ai,该扩展包支持你对代理进行设置:

通过中间层代理

不过如果你没有本地代理或者不想每个项目配置,还可以使用 解决 和 的 API 无法访问的问题,其实就是把一个国内可访问的域名指向 ,再将 作为代理,转发给 接口进行交互,最后把响应数据返回给客户端:

参照这个思路,使用 AWS 或者其他云服务厂商的 API 网关+ 函数(云函数)也可以实现类似的功能。不想折腾的同学可以使用极客书房提供的腾讯云代理,只需要在发起请求时将 的 API 域名 替换成 ..xyz 即可:

代码里也是一样,以我开发的 geekr/- 为例,它首先从配置文件读取 ,然后在发起请求的时候,以自定义的 $ 为准发起请求,这样就可以通过代理的方式发起对 的接口请求了:

withOrganization($organization);
        }
        $client = new GuzzleClient();
        $transporter = new HttpTransporter($client, $baseUri, $headers);
        return new Client($transporter);
    }
}

这个代理的源码我也提交到 仓库里了,其实就是做一层转发而已:GO–PROXY,觉得有帮助就给个 star 吧。

© 版权声明
广告也精彩

相关文章

暂无评论

暂无评论...