相关文章推荐
飘逸的山羊  ·  mysql中不用group by ...·  2 年前    · 
大气的书包  ·  动态设置约束性小部件·  2 年前    · 
不羁的闹钟  ·  JavaScript 学习-38.HTML ...·  2 年前    · 
知识渊博的匕首  ·  Django ...·  2 年前    · 
想旅行的双杠  ·  POCO::Net ...·  2 年前    · 
<form class="p-4 flex space-x-4 justify-center items-center" action="/chat" method="post">
        @csrf
        <input id="message" placeholder="输入你的问题..." type="text" name="message" autocomplete="off" class="border rounded-md  p-2 flex-1" required />
        <button class="flex items-center justify-center px-4 py-2 bg-green-500 hover:bg-green-600 text-white rounded-md text-sm md:text-base" type="submit">
            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                <path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" />
        </button>
        <button class="flex items-center justify-center px-4 py-2 bg-gray-400 hover:bg-gray-500 text-white rounded-md text-sm md:text-base" onclick="window.location.href='/reset'">
            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
                <path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
        </button>
    </form>

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

实现会话核心功能

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

use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use GeekrOpenAI\Laravel\Facades\OpenAI; class ChatController extends Controller * Handle the incoming prompt. public function chat(Request $request): RedirectResponse // 系统消息 $messages = $request->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('/');

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

完成前端展示

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

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

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

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

@foreach($messages as $message)
    <div class="flex rounded-lg p-4 @if ($message['role'] === 'assistant') bg-green-200 flex-reverse @else bg-blue-200 @endif ">
        <div class="ml-4">
            <div class="text-lg">
                @if ($message['role'] === 'assistant')
                    <a href="#" class="font-medium text-gray-900">GeekChat</a>
                @else
                    <a href="#" class="font-medium text-gray-900">你</a>
                @endif
            <div class="mt-1">
                <p class="text-gray-600">
                    {!! \Illuminate\Mail\Markdown::parse($message['content']) !!}
@endforeach

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

基于 Docker 开发部署

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

composer require laravel/sail --dev

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

./vendor/bin/sail up -d

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

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

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

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

use GuzzleHttp\Client as GuzzleClient; use OpenAI\Client; use OpenAI\Transporters\HttpTransporter; use OpenAI\ValueObjects\ApiKey; use OpenAI\ValueObjects\Transporter\BaseUri; use OpenAI\ValueObjects\Transporter\Headers; class OpenAI * Creates a new Open AI Client with the given API token. public static function client(string $apiKey, $baseUri, string $organization = null): Client $apiKey = ApiKey::from($apiKey); $baseUri = BaseUri::from($baseUri); $headers = Headers::withAuthorization($apiKey); if ($organization !== null) { $headers = $headers->withOrganization($organization); $client = new GuzzleClient(); $transporter = new HttpTransporter($client, $baseUri, $headers); return new Client($transporter);

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

太烂了有点差凑活看还不错太棒了 (8 人打了分, 平均分: 5.00)
Loading...

Published in AI and ChatGPT