ChatGPT と Laravel の API を連携させる(Actions in GPTs)

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、話題に事欠かないChatGPTですが、この間の「CEO、退陣すんのんかい or いや、せんのんかい(吉本新喜劇風)」な出来事の影響で公開が遅れていたGPT Storeが公開されるというニュースが出てきました。

どうやらこれは、

独自のGPTをつくって公開し、そこから収益を上げられるようにしよう

という目的もあるようで、もしそうならきっと専門的なデータをもっている人は自動的なコンサルティングサービスもできるわけで、使い方次第ではすごいことになるのかもと感じています。

そこで❗

今回はこのニュースを受けてLaravelで作ったAPIChatGPTから直接アクセスできるように独自の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で試してみる

では、OpenAPIyamlを出力する前に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が変わってくれるようなイメージです。

もしそういったシステムをご準備されたい場合はいつでもお気軽にご相談ください。お待ちしております。😄✨

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はLaravelで作ったAPIGPTsを紐付けてユーザーの検索ができるようにしてみました。

おそらくより精度が高い結果が取得できるようにするには、プロンプトをより改良しないといけないんでしょうが、ITリテラシーが比較的低い方でも、文字が入力できればより有益な情報をとりだすことができるようになります。(つまりスマホが使えればOKってことですね)

今回はシンプルなユーザー検索でしたが、もっと他にも活用法があると思います。

ぜひ皆さんも楽しんでやってみてくださいね。

ではでは〜❗

「つんくさんが出した本
読んでます👍」

このエントリーをはてなブックマークに追加       follow us in feedly