🤖

【Laravel 13】Laravel AI SDK を試してみた

に公開

こんにちは!tatata-keshiです😃

私事ですが、先日5月26日〜27日に開催された Laravel Live Japan へ参加してきました!

https://laravellive.jp/ja

1日目のKeynote セッションでは、Laravel製作者のTaylor Otwell氏が登壇し、最新のLaravelエコシステムである Laravel Boost、Laravel AI SDK、Laravel Cloudについてそれぞれ紹介していました。
(この記事では Laravel AI SDK について書いていますが、個人的には Laravel Cloud の東京リージョン解禁も熱いトピックでした)

今回は、Keynoteで紹介されていた Laravel AI SDK を用いて、簡単なアプリケーションを実装してみました。

Laravel AI SDK とは

Laravel AI SDK は、LaravelアプリケーションとOpenAIやAnthropic、GeminiなどのAIプロバイダがやり取りするための統一されたインターフェースを提供するファーストパーティのパッケージです。

https://laravel.com/docs/13.x/ai-sdk

Laravel 13でAI SDKが正式安定版となりLaravelのエコシステムに組み込まれたことによって、LaravelアプリケーションにおけるAI機能開発のハードルが劇的に下がったといえます。

Laravel AI SDK を用いてアプリを作る

それでは早速アプリケーションを実装していきます。
今回は「AIが料理レシピをプロンプトから提案し、それを保存できる」機能を持った簡単なアプリを作ります。

処理の流れ

なお、以降の手順はLaravelのプロジェクトを作成している前提で進めていきます。

1. Laravel AI SDKのインストール

はじめにLaravel AI SDKパッケージをcomposer経由でインストールします。

composer require laravel/ai

インストールが完了したら、AI SDKの設定ファイルを作成する必要があるので以下のコマンドを実行します。

php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"

この時AI SDKが会話を保存するテーブルのmigrationファイルが作成されるので、migrationも実行しておきましょう。

php artisan migrate

2. Gemini API Keyの作成

次に使用するAIエージェントのAPIキーを設定します。今回は無料でAPIキーを発行できるGeminiを使用します。

GeminiのAPIキーは、Google AI Studio の「APIキーを作成」から作成できます。

Google AI Studioの画面

APIキーを発行したら、環境変数に追加します。

.env
GEMINI_API_KEY="$(YOUR_API_KEY)"

3. DB設計

次にDB設計を考えます。今回は最低限の機能で実装するため、レシピを保存するための recipes テーブルを作成します。

4. Agentの作成

続いてAgentを作成します。Agentは簡潔に説明するとAIの役割や振る舞いを1つのクラスにまとめたものであり、Laravel AI SDKの核とも言える機能です。

Agentはmake:agentコマンドで作成できます。

php artisan make:agent RecipeAgent

今回はinstructions()schema()を使ったシンプルなAgentを作成します。

RecipeAgent.php
<?php

namespace App\Ai\Agents;

use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Attributes\Provider;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;
use Stringable;

#[Provider(Lab::Gemini)]
class RecipeAgent implements Agent, HasStructuredOutput
{
    use Promptable;

    /**
     * Get the instructions that the agent should follow.
     */
    public function instructions(): Stringable|string
    {
        return <<<'PROMPT'
        あなたは熟練の料理研究家です。ユーザーから渡されるリクエストに対して、家庭で再現できる料理レシピを1つだけ提案してください。

        厳守事項:
        - 出力は必ず日本語にすること。
        - 分量は g / ml / 個 / 大さじ / 小さじ など、家庭で使う単位で具体的に書くこと。
        - 手順は調理順に並べ、1ステップごとに1文で簡潔に書くこと。
        - ユーザーが指定した条件(食材・人数・調理時間など)があれば必ず尊重すること。
        - 指定された JSON スキーマに厳密に従って構造化出力を返すこと。スキーマ外の説明文や前置きを返してはならない。
        PROMPT;
    }

    /**
     * Get the agent's structured output schema definition.
     *
     * @return array<string, mixed>
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'title' => $schema->string()->required(),
            'description' => $schema->string()->required(),
            'ingredients' => $schema->array()->items(
                $schema->object(fn ($schema) => [
                    'name' => $schema->string()->required(),
                    'quantity' => $schema->string()->required(),
                ])
            )->required(),
            'steps' => $schema->array()->items($schema->string())->required(),
        ];
    }
}

instructions()には共通の指示を書きます。GemやGPTsに記述する指示と同じようなものです。
今回の場合はレシピの出力とのフォーマットを指定しています。

    /**
     * Get the instructions that the agent should follow.
     */
    public function instructions(): Stringable|string
    {
        return <<<'PROMPT'
        あなたは熟練の料理研究家です。ユーザーから渡されるリクエストに対して、家庭で再現できる料理レシピを1つだけ提案してください。

        厳守事項:
        - 出力は必ず日本語にすること。
        - 分量は g / ml / 個 / 大さじ / 小さじ など、家庭で使う単位で具体的に書くこと。
        - 手順は調理順に並べ、1ステップごとに1文で簡潔に書くこと。
        - ユーザーが指定した条件(食材・人数・調理時間など)があれば必ず尊重すること。
        - 指定された JSON スキーマに厳密に従って構造化出力を返すこと。スキーマ外の説明文や前置きを返してはならない。
        PROMPT;
    }

AIの出力結果を配列としてControllerに渡しデータを取り出せるようにしたい、という場合はHasStructuredOutputインターフェースとschema()を使用します。

schema()を使用することで、AIの出力をJSON Schema形式で指定できます。
構造化出力によってJSONパース処理や型チェックの処理を別途実装する必要がなくなり、AIの出力を他の処理でそのまま利用しやすくなるため開発体験の向上にもつながります。

class RecipeAgent implements Agent, HasStructuredOutput 
    /**
     * Get the agent's structured output schema definition.
     *
     * @return array<string, mixed>
     */
    public function schema(JsonSchema $schema): array
    {
        return [
            'title' => $schema->string()->required(),
            'description' => $schema->string()->required(),
            'ingredients' => $schema->array()->items(
                $schema->object(fn ($schema) => [
                    'name' => $schema->string()->required(),
                    'quantity' => $schema->string()->required(),
                ])
            )->required(),
            'steps' => $schema->array()->items($schema->string())->required(),
        ];
    }

今回はGeminiのAPIキーを使用するため、#[Provider] アトリビュートで使用するAIプロバイダーを指定しています。

#[Provider(Lab::Gemini)]
class RecipeAgent implements Agent, HasStructuredOutput

5. 一連の処理を実装

ここまできたら、残りはこれまでのLaravelアプリケーションと同じです。

プロンプトからレシピを生成し、フロントエンドに出力結果を返すgenerateメソッドを作成します。

RecipeController
    public function generate(GenerateRecipeRequest $request): JsonResponse
    {
        $prompt = $request->validated('prompt');

        try {
            $response = (new RecipeAgent)->prompt($prompt);
        } catch (Throwable $e) {
            report($e);

            return response()->json(
                ['message' => 'レシピの生成に失敗しました。時間をおいて再度お試しください。'],
                Response::HTTP_BAD_GATEWAY,
            );
        }

        return response()->json([
            'recipe' => [
                'title' => $response['title'],
                'description' => $response['description'],
                'ingredients' => $response['ingredients'],
                'steps' => $response['steps'],
                'source_prompt' => $prompt,
            ],
        ]);
    }

Agentを呼び出すときに、->prompt($prompt)というように書くことで、AIに任意のプロンプトを引数として渡すことができます。

$response = (new RecipeAgent)->prompt($prompt);

(※残りのControllerの処理や、フロントエンド側の実装は分量が多いので省略します)

6. デモ

実際に動かしてみると以下のように動作します。

入力画面でプロンプトを入れて送信します
プロンプト入力

「レシピを生成」を押すとAIがレシピを出力します
レシピを生成

生成したレシピは保存して後から確認できます
レシピ一覧

まとめ

今回は最低限の機能実装でしたが、Laravel AI SDK には他にも機能がかなり用意されているので、引き続き色々試していきたいと思いました。

ソーシャルデータバンク テックブログ

Discussion