
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、話題に事欠かないChatGPT
ですが、この間の「CEO、退陣すんのんかい or いや、せんのんかい(吉本新喜劇風)」な出来事の影響で公開が遅れていたGPT Store
が公開されるというニュースが出てきました。
どうやらこれは、
独自のGPTをつくって公開し、そこから収益を上げられるようにしよう
という目的もあるようで、もしそうならきっと専門的なデータをもっている人は自動的なコンサルティングサービスもできるわけで、使い方次第ではすごいことになるのかもと感じています。
そこで
今回はこのニュースを受けてLaravel
で作ったAPI
にChatGPT
から直接アクセスできるように独自のGPT
を作ってみることにしました。
ぜひ何かの参考になりましたら嬉しいです。
「正月休みが楽しすぎて
現在空虚感…(ヒュー…)」
開発環境: Laravel 10.x、ChatGPT
目次 [非表示]
前提として
Laravel Breeze
などのログイン機能がインストールされて有効になっているが前提です。
※ ただし、テストデータは後で紹介するので不要です。
では今回も楽しんでやっていきましょう
参考記事: ログイン機能の準備をする
Laravel Sanctum をインストールする
まずは、API
キーを使ってLaravel
のデータにアクセスできるようにしないといけません。
もちろん我らがLaravel
ですから、この機能はインストールするだけで簡単に使うことができるようになっています。
では、そのパッケージ「Laravel Sanctum」をインストールしましょう。
以下のコマンドを実行してください。
composer require laravel/sanctum
すると、パッケージがインストールされるので、続けてパッケージの中から必要なファイルをLaravel
側へパブリッシュ(コピー)します。
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
これで、コンフィグとマイグレーションがコピーされました。
マイグレーションは「データベース・テーブルの設計図」ですので、さらに以下のコマンドでテーブルを作成します。
php artisan migrate
すると、以下のようなテーブル「personal_access_tokens」が作成されることになります。
Enum をつくる
今回性別のデータを用意するので、以下のようにEnum
(列挙型のデータ)をつくっておきましょう。(Enum
はいろんなところで便利です)
app/Enums/Gender.php
<?php
namespace App\Enums;
enum Gender: string
{
case Male = '男性';
case Female = '女性';
case Other = 'その他';
case None = '該当なし';
public static function values()
{
return array_column(self::cases(), 'value');
}
}
なお、Gender::values();
とすると、「男性」「女性」「その他」「該当なし」を配列で受け取ることができます。
テストデータをつくる
今回はユーザー検索するエンドポイント(URL
)をつくって、それをChatGPT
から呼び出すようにしますが、現状では情報が少すぎるので「住所」や「性別」、「電話番号」もテーブルに追加しておきましょう。
では、以下のコマンドを実行してください。
php artisan make:migration add_to_additional_columns_to_users
すると、マイグレーション・ファイルが作成されるので中身を以下のようにしてください。
database/migrations/2024_01_09_193911_add_to_additional_columns_to_users.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('address')->after('password');
$table->string('phone')->after('address');
$table->string('gender')->after('phone');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('address');
$table->dropColumn('phone');
$table->dropColumn('gender');
});
}
};
次に、追加されたカラムにもデータが入るようにUserFactory
を変更します。
database/factories/UserFactory.php
<?php
namespace Database\Factories;
use App\Enums\Gender;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
// 省略
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
// ここから追加しました
'address' => fake()->address(),
'phone' => fake()->phoneNumber(),
'gender' => Arr::random(Gender::values())
];
}
// 省略
なお、テストデータは日本向けになるように忘れずコンフィグ設定も変更しておきましょう。
config/app.php
// 省略
'faker_locale' => 'ja_JP',
// 省略
後は、DatabaseSeeder
の初期状態でコメントアウトされているコードを有効にして件数を50
へ変更し、さらに最初の1人だけはAPI
トークンが自動的に作成されるようにしましょう。
database/seeders/DatabaseSeeder.php
// 省略
public function run(): void
{
\App\Models\User::factory(50)->create(); //
テストデータを 50件追加
$token = \App\Models\User::find(1)->createToken('for_chatgpt'); //
API トークン作成
dump($token->plainTextToken); //
API トークンが表示されます
// \App\Models\User::factory()->create([
// 'name' => 'Test User',
// 'email' => 'test@example.com',
// ]);
}
// 省略
これでテストデータの準備は完了しました。
以下のコマンドで実際にデータを作ってみましょう。
php artisan migrate:fresh --seed
すると、API
トークンがコマンドラインの中に表示されることになります(注意: データベースの中身とは違っていますので、必ずここで取得してください)
そして、ユーザーテーブルはこうなりました。
OpenAPI(Swagger)に対応させる
ChatGPT
で独自のGPTs
をつくるためには「こういう仕様のAPIにアクセスしてね」と教えて上げないといけません。
もちろん手作業でやってもいいのですが、GPTs
の設定にはこの仕様(Schema
)を登録できるので、Swagger-PHP
をインストールしてAPIの仕様を自動的にアプトプットできるようにしておきましょう。
では以下のコマンドでパッケージをインストールしてください。
composer require zircote/swagger-php
パッケージがインストールできたら、API
の全体設定をセットしておきます。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use OpenApi\Attributes as OA;
#[OA\OpenApi(
info: new OA\Info(
version: '1.0.0',
description: 'APIのドキュメント',
title: 'API by Laravel Sanctum',
),
servers: [
new OA\Server(
url: 'https://example.com',
),
],
)]
class AppServiceProvider extends ServiceProvider
{
// 省略
}
ご注意: なお、「https://example.com」の部分はご自身のサーバー環境に置き換えてください。
これで以下のコマンドを実行すると「openapi.yaml」が作成され、この中にAPI
の仕様をまとめてくれるというわけです。(こういう仕組みを考える人って尊敬しますね)
./vendor/bin/openapi app -o openapi.yaml
エンドポイントをつくる
では、ユーザーを検索できるエンドポイントをつくっていきます。
以下のコマンドを実行してください。
php atisan make:controller Api\\UserController
すると、コントローラーが作成されるので、中身を以下のようにします。
app/Http/Controllers/Api/UserController.php
<?php
namespace App\Http\Controllers\Api;
use App\Enums\Gender;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use OpenApi\Attributes as OA;
class UserController extends Controller
{
#[OA\Get(
path: '/api/user',
description: 'ユーザーの一覧を取得する',
tags: ['User'],
parameters: [
new OA\Parameter(name: 'name', description: '検索する名前', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'email', description: '検索するメールアドレス', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'address', description: '検索する住所', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'phone', description: '検索する電話番号', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'gender', description: '検索する性別', in: 'query', required: false, schema: new OA\Schema(type: 'string', enum: Gender::class)),
],
responses: [
new OA\Response(response: 200, description: 'AOK'),
new OA\Response(response: 400, description: 'Bad Request'),
new OA\Response(response: 401, description: 'Not allowed'),
]
)]
public function index(Request $request)
{
return User::query()
->when($request->name, function ($query, $name) {
$query->where('name', 'LIKE', '%' . $name . '%');
})
->when($request->email, function ($query, $email) {
$query->where('email', 'LIKE', '%' . $email . '%');
})
->when($request->address, function ($query, $address) {
$query->where('address', 'LIKE', '%' . $address . '%');
})
->when($request->phone, function ($query, $phone) {
$query->where('phone', 'LIKE', '%' . $phone . '%');
})
->when($request->gender, function($query, $gender) {
$valid_genders = Gender::values();
if(! in_array($gender, $valid_genders, true)) {
abort(400, 'Bad Request');
}
$query->whereIn('gender', $valid_genders);
})
->get();
}
}
もしかするとちょっと見慣れないコードが入っていると思ったかもしれませんが、これがAttributes
と呼ばれる、シンプルに言うと「追加情報」のようなブロックで、OpenAPI
はここを見てAPI
の仕様をまとめてくれることになります。
つまり、ここでは「どんなURLで、どんな検索キーワードが入ってきて、レスポンスはこうです」みたいなことが書かれているわけです。
Postmanで試してみる
では、OpenAPI
でyaml
を出力する前にAPI
自体がうまく動いているかPostman
でチェックしておきましょう。
Postman
は特にAPI
をつくる場合にはとても便利なツールで無料プランもあります。
では、まずは以下のコマンドでもう一度データベースを初期化します。
php artisan migrate:fresh --seed
すると、API
トークンが表示されるのでこれをコピーしておいてください。
では、Postman
を開いてタブを追加し、以下のようにURLを入力し、「Authorization > Bearer Token」を選択します。
すると、トークンをいれる入力ボックスが表示されるので、ここへ先ほどのAPIトークンをセットしてください。
これで準備は完了です!
Send
ボタンを押してみましょう。
もし以下の部分にテストデータが表示されていれば成功です。
API ドキュメントを生成する
では、ひとつ寄り道しましたがこの状態でAPI
のドキュメント(仕様)をアウトプットしてみましょう。
以下のコマンドを実行してください。
./vendor/bin/openapi app -o openapi.yaml
すると、以下のようなファイルが作成されます。
openapi.yaml
openapi: 3.0.0
info:
title: 'API by Laravel Sanctum'
description: APIのドキュメント
version: 1.0.0
servers:
-
url: 'https://example.com'
paths:
/api/user:
get:
tags:
- User
description: ユーザーの一覧を取得する
operationId: ef3192335324c07faff67807a2e8a2f8
parameters:
-
name: name
in: query
description: 検索する名前
required: false
schema:
type: string
-
name: email
in: query
description: 検索するメールアドレス
required: false
schema:
type: string
-
name: address
in: query
description: 検索する住所
required: false
schema:
type: string
-
name: phone
in: query
description: 検索する電話番号
required: false
schema:
type: string
-
name: gender
in: query
description: 検索する性別
required: false
schema:
type: string
enum:
- 男性
- 女性
- その他
- 該当なし
responses:
'200':
description: AOK
'400':
description: 'Bad Request'
'401':
description: 'Not allowed'
GPTs をつくる
では、やっと本題にたどり着きました。
ここまででつくったAPI
にアクセスできる独自のChatGPT
(=My Gpts
)をつくっていきましょう。
まずChatGPT
にログインしてページ左側メニューの「Explore」ボタンをクリックします。
ページ移動するので、「Create a GPT」をクリック。
さらにページ移動した先にある「Configure」タブをクリック。
すると、コンフィグのフォームが表示されるので、わかりやすいように名前を入力します。
そして、ページ下部にある「Create new action」をクリック。
認証のモーダルが開くので、Noneの部分をクリックし「API Key」を選択します。
フォームが変化するので、テストデータを作ったときに取得したAPI
キー(API
トークン)を入力、さらに「Bearer」を選択後、「Save」ボタンをクリックしてください。
続いて、「Schema」に先ほどコマンドで生成したopenapi.yaml
の中身をコピペします。
すると、すぐ下にエンドポイントが表示されるので「Test」ボタンをクリックしてうまくいくか確認しておきましょう。
すると、GPT
がページ右側に以下のような表示が出てくるので「Allow」ボタンをクリックしてAPIへのアクセスを許可します。
※ 毎回許可するのがめんどうでしたら、すぐ横にある「Always allow for *****」をクリックしてください。
そして、以下のように返してくれたらアクセスは成功しています。
内容としては「John で検索してもデータがなかったわ…」と言っていますが、これはもちろん「John」に該当するユーザーがないためです。
なので、以下のようにデータベースに存在している人の名前で検索してもらいましょう。
じゃあ、「John」じゃなくて「鈴木」で検索してみて。
すると、以下のテストデータを返してくれました。
【ChatGPTの回答】
検索結果によると、データベースには「鈴木 充」という名前のユーザーが登録されています。このユーザーの詳細は以下の通りです:
名前: 鈴木 充
メールアドレス: zidaka@example.org
住所: 3859110 広島県潟市南区中村町井上10-9-3 ハイツ田辺105号
電話番号: 0743-04-3185
性別: 男性
この情報がお役に立てば幸いです。他にも何かお手伝いできることがあれば、遠慮なくお知らせください。
では、うまくいったのでいつでも使えるように保存しておきましょう。(今回はOnly me
(=自分だけ使える)に設定しています)
保存が完了すると、今つくったGPTのページに移動するのでそのままテストすることができるでしょう。
これで作業は完了です
お疲れ様でした。
テストしてみる
では、実際にテストしてみましょう。
まずは以下のテストユーザーです。
name: 宇野 拓真
email: bhirokawa@example.net
address: 4938120 島根県江古田市中央区渚町井上4-5-1
phone: 080-4550-6410
gender: 女性
先ほどは名前での検索でしたので今回は電話番号でやってみましょう。
【プロンプト】 電話番号が「080-4550-6410」の人のデータを教えて
すると・・・・・・
【ChatGPT の回答】
電話番号「080-4550-6410」の持ち主は宇野 拓真さんです。以下は宇野さんの詳細情報です:
名前: 宇野 拓真
メールアドレス: bhirokawa@example.net
住所: 4938120 島根県江古田市中央区河田町井上4-5-1
性別: 女性
アカウント作成日: 2024年1月10日
ご参考までにどうぞ。
うまくいきました
では、次はひとりだけじゃなく複数のユーザーを見つけてみましょう。
テストデータでは「住所が北海道」になっている人が3人いるようなので、以下のように聞いてみます。
【プロンプト】 住所が北海道の人の名前をすべて教えて
すると、以下のように返してくれました。
【ChatGPT の回答】 北海道に住んでいる人の名前は以下の通りです: 佐藤 和也 杉山 洋介 小泉 直人 これらの方々が北海道にお住まいです。
※ データ的にも正しいです。
これも成功です
では、最後に件数も教えてもらいましょう。
【プロンプト】 電話番号に 080 が入っている人の件数を教えて
すると、こう回答してくれました。
【ChatGPT の回答】 電話番号に「080」が含まれる人の件数は7件です。
はい
すべて成功です
ちなみに: 失敗するケースも…
テストではほぼうまく処理できたのですが、例えば「男性として登録されている人数を教えて」と聞いてみると正解は17件だったのに対し、ChatGPT
での回答は20
件と間違っていました。(もしかすると名前で判別したのでしょうか?)
そこで、「じゃあその名前を全部教えて」と聞いてみるときちんと17件となるなど、「ゆらぎ」のようなものが存在しているようでした。
これから精度があがるかと思いますが、もし業務で利用するとなると使い方は少し気をつけないといけないかもしれません。
企業様へのご提案
今回やったように「すでにデータが存在していれば」それをChatGPT
に紐付けることで、いつでも自社のデータを活用することができます。
例えば、過去にあった営業ノウハウが蓄積されている場合、新人がChatGPT
に「お客様が●●な雰囲気のときはどうすればいいですか?」や「●●な場合の受注確度はどのくらい?」などのように社員教育にもつなげることができるでしょう。
また、過去に発生した重大事故などをデータ化しておけば「●●な条件で実行しようとしているが大丈夫でしょうか?」などのような質問ができるため、事前に事故を防止することにつながるかもしれません。
つまり、これまでは「検索ノウハウ」をもっている人でないと正しいデータを取得しにくかったですが、これをChatGPT
が変わってくれるようなイメージです。
もしそういったシステムをご準備されたい場合はいつでもお気軽にご相談ください。お待ちしております。
おわりに
ということで、今回はLaravel
で作ったAPI
にGPTs
を紐付けてユーザーの検索ができるようにしてみました。
おそらくより精度が高い結果が取得できるようにするには、プロンプトをより改良しないといけないんでしょうが、IT
リテラシーが比較的低い方でも、文字が入力できればより有益な情報をとりだすことができるようになります。(つまりスマホが使えればOKってことですね)
今回はシンプルなユーザー検索でしたが、もっと他にも活用法があると思います。
ぜひ皆さんも楽しんでやってみてくださいね。
ではでは〜
「つんくさんが出した本
読んでます」