1. はじめに
最近、ChatGPTが流行ですよね。
非エンジニアの方でもChatGPTを使っていることを耳にします。
また、ChatGPTだけではなくOpenAIではAPIも公開しています。
APIではGPT4を含む数種類のAIモデルを使用できるということで、
早速試してみようと思い、Webアプリをサクッと作ってみました!
今回作成したアプリはLaravel(breeze)を使っています!
アプリの機能としては下記の簡単なものになります。
- 画面からテキストを入力
- 入力されたテキストをもとにOpenAIのAPIを呼び出す
- 返ってきた結果を画面に出力
2. 対象読者
- OpenAI のAPI を使いたい方
- Laravel を触ったことがある方
- Docker で環境を作ったことがある方
Laravel や Docker の基本的な内容に関しては省略します。
3. 目次
4. 環境
- ホストPC: CentOS7.9
- 仮想環境: Docker v23.0.2
- サーバサイド: Laravel v9.19
- フロント: Laravel(blade) v9.19
- DB: MySQL v8.0
下記のdocker-compose.yml及びDockerfileで構築しています。
ローカル環境でのみ動作させる想定のため、セキュリティ対策はしておりません。
RDBMS(MySQL)はメモリを食うので、ほかの代替手段(SQLite)を使っていただいてもいいと思います。
今回作成するアプリではログイン認証でのDBを使っています。
# docker-compose.yml
version: '3.9'
services:
app:
build:
context: <Dockerfileのディレクトリパス>
container_name: breeze_app
volumes:
- <Laravelプロジェクトのパス>:/var/www/html
ports:
- "8000:80"
environment:
- TZ=Asia/Tokyo
depends_on:
- db
db:
image: mysql:8.0
container_name: breeze_db
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_DATABASE: laravel_breeze
MYSQL_ROOT_PASSWORD: <パスワード>
MYSQL_USER: laravel_breeze
MYSQL_PASSWORD: <パスワード>
TZ: Asia/Tokyo
volumes:
- db_data:/var/lib/mysql
expose:
- 3306
volumes:
db_data:
# Dockerfile
FROM php:8.0-fpm
RUN apt-get update && apt-get upgrade -y && apt-get install -y unzip libzip-dev
RUN docker-php-ext-install pdo_mysql zip
RUN docker-php-ext-enable pdo_mysql zip
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-enable pdo_mysql
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
RUN apt-get install -y nodejs
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
RUN echo "Asia/Tokyo" > /etc/timezone
RUN dpkg-reconfigure -f noninteractive tzdata
RUN apt-get install -y locales
RUN sed -i -e 's/# ja_JP.UTF-8 UTF-8/ja_JP.UTF-8 UTF-8/' /etc/locale.gen
RUN locale-gen
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
EXPOSE 80
5. 構築
前提
- OpenAIにてアカウントが作成済み
- OpenAIにてSECRET KEYが発行済み
環境構築
上記のdocker-compose.yml及びDockerfileを作成後、下記を実行します。
$ docker-compose up -d --build
ビルド終了後、下記のコマンドでコンテナが起動していることを確認します。
$ docker ps
Laravelプロジェクトの作成
今回はComposerのcreate-project
というコマンドを実行してプロジェクトを作成します。
$ docker exec -it breeze_app bash
# コンテナの中に入る
$ composer global require laravel/installer
$ composer create-project --prefer-dist laravel/laravel .
$ chmod -R a+w storage bootstrap/cache public
breezeのインストール
上記のコンテナに入った状態で下記を実行します。
$ composer require laravel/breeze --dev
$ php artisan breeze:install
(Breezeのインストール方法が選択できるので「blade」を選択する)
プロジェクトのセットアップ
LaravelからDBにアクセスできるように.env
の下記項目を編集します。
# 編集
DB_HOST=db
DB_DATABASE=laravel_breeze
DB_USERNAME=laravel_breeze
DB_PASSWORD=<パスワード>
# 追記
OPEN_AI_API_KEY=<OpenAIにて発行したSECRET KEY>
その後、下記のコマンドをコンテナに入った状態で実行します。
$ php artisan migrate
$ npm install
サーバ起動
下記のコマンドを実行し、ブラウザからhttp://<サーバのホスト>:8000
にアクセスするとLaravelの画面が表示されます。
Breezeではログイン機能は用意されています。詳しくはスターターキット 9.x Laravelを参照してください。
$ php artisan serve --host 0.0.0.0 --port 80
6. 実装
画面の実装
画面側のコードにおいて、新規作成及び変更するファイルは下記になります。
コードのアーキテクトは考えずに作成していますのでご了承ください。
resources/
├── views/
├── layouts/
│ └── navigation.blade.php (変更)
├── open_ai/ (以下すべて新規作成)
├── partials/
│ └── chat-gpt-form.blade.php
└── gpt.blade.php
- navigation.blade.php
<!-- 既存コード -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
<!-- 追記 -->
<x-nav-link :href="route('open_ai.gpt')" :active="request()->routeIs('gpt')">
{{ __('GPT') }}
</x-nav-link>
</div>
<!-- 既存コード -->
- chat-gpt-form.blade.php
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('ChatGPTの入力フォーム') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("質問事項を入力して送信ボタンを押してください。") }}
</p>
</header>
<form id="gptForm" method="post" action="{{ route('open_ai.send') }}" class="mt-6 space-y-6">
@csrf
<div>
<x-input-label for="question" :value="__('質問入力欄')" />
<textarea class="mt-1 block w-full" rows="5" name="question" required>{{old('question', '')}}</textarea>
<x-input-error class="mt-2" :messages="$errors->get('question')" />
</div>
<div>
<x-input-label for="answer" :value="__('回答出力欄')" />
@if(session('answer'))
<textarea id="answerTextArea" class="mt-1 block w-full" rows="5" name="answer" oninput="autoResizeTextarea()" disabled>{{ session('answer') }}</textarea>
@else
<textarea id="answerTextArea" class="mt-1 block w-full" rows="5" name="answer" disabled></textarea>
@endIf
<x-input-error class="mt-2" :messages="$errors->get('answer')" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('送信') }}</x-primary-button>
@if(session('answer'))
<p
x-data="{ show: true }"
x-show="show"
class="text-sm text-gray-600 dark:text-gray-400"
>{{ __('回答欄に出力しました。') }}</p>
@endif
</div>
</form>
</section>
<script>
function autoResizeTextarea() {
const textarea = document.getElementById('answerTextArea');
textarea.style.height = 'auto';
textarea.style.height = (textarea.scrollHeight) + 'px';
}
document.addEventListener('DOMContentLoaded', () => {
autoResizeTextarea();
});
</script>
- gpt.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('GPTお試し') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w-xl">
@include('open_ai.partials.chat-gpt-form')
</div>
</div>
</div>
</div>
</x-app-layout>
APIの実装
API側のコードにおいて、新規作成及び変更するファイルは下記になります。
コードのアーキテクトは考えずに作成していますのでご了承ください。
app/
├── Http/
├── Controllers/
└── GptController.php (新規作成)
config/
└── services.php (変更)
routes/
└── web.php (変更)
- web.php
// 下記を追記
Route::middleware('auth')->group(function () {
Route::get('/gpt', [GptController::class, 'index'])->name('open_ai.gpt');
Route::post('/gpt', [GptController::class, 'send'])->name('open_ai.send');
});
- services.php
// 下記を追記
'open_ai' => [
'api_key' => env('OPEN_AI_API_KEY', ''),
],
- GptController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Http;
use Illuminate\View\View;
class GptController extends Controller
{
/**
* ページの表示
*
* @param Request $request
* @return View
*/
public function index(Request $request): View
{
return view('open_ai.gpt', [
'user' => $request->user(),
]);
}
/**
* chatGPTのAPI実行
*
* @param Request $request
* @return View
*/
public function send(Request $request): RedirectResponse
{
$url = "https://api.openai.com/v1/chat/completions";
$apiKey = config('services.open_ai.api_key');
$headers = array(
"Content-Type" => "application/json",
"Authorization" => "Bearer $apiKey"
);
$data = array(
"model" => "gpt-3.5-turbo",
"messages" => [
[
"role" => "user",
"content" => $request->question,
]
]
);
$response = Http::withHeaders($headers)->timeout(60)->post($url, $data);
if ($response->json('error')) {
info('エラーが発生');
}
return Redirect::route('open_ai.gpt')
->with('answer', $response->json('choices')[0]['message']['content'];)
->withInput();
}
}
以上にて、実装は完了です!
ブラウザでログイン後、GPTページにて動作確認することができると思います。
7. さいごに
いかがでしたか?
私は今回のアプリを作るためにChatGPTを何度も使いました。
今回は最低限の機能しかないため使いにくい点はたくさんあると思います。
例えば、モデルの選択、リクエストパラメータの調整、ローディング画面など
これをベースに色々と開発してみてください!
また、docker環境を作成しましたが、
LaravelのSailを使えば簡単にDocker環境が構築できることを後日知りました。。
OpenAI のAPIは従量課金制ですが、アカウントを作成すると\$18までは無料で使えます。(2023/03時点)
これまで何度も使っていますが、まだ\$1も使えていません。
\$18の枠内でたくさん使えると思えますので、どんどん遊んでみましょう!