こんにちは!Fusicの善住です。
今回はAIエージェントをLaravelで実装できるフレームワークの一つであるLar Agentを触ってみました。
似たようなLaravelでのAIエージェントフレームワークにLaravelPrismがありますが、おおまかな機能は同じであるもののLarAgentにしかできないこともありそうです。
前提
OpenAIやAnthropicのAPIkeyを持っている(僕はOpenAIのkeyを使いました)
インストールとか初期設定
インストール
composer require maestroerror/laragent
configファイル生成
php artisan vendor:publish --tag="laragent-config"
↑このコマンドでconfigが生成されるのですが、config内でのデフォルトの環境変数名がOPENAI_API_KEY
なので.envなどにAPIkeyを保存している場合は変数名が同じかどうか一応確認しておきましょう
エージェントの生成
php artisan make:agent {エージェントの名前※お好きな名前を命名してください}
↑このコマンドでapp/AiAgents/配下にエージェント定義ファイルが生成されます。このファイルにエージェントの使用モデルや使うツールの情報などを書きます。
実際に使ってみた
今回僕は「友達の誕生日プレゼントをweb検索して提案するエージェント」を作成してみました。Toolとしてweb検索APIのTavilySearchを使用しました。以下がエージェント定義ファイルと実際に呼び出しているコードです。
app/AiAgents/SuggestAgent.php
<?php
declare(strict_types=1);
namespace App\AiAgents;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use LarAgent\Agent;
use LarAgent\Attributes\Tool;
use Throwable;
class SuggestAgent extends Agent
{
protected $model = 'gpt-4o-mini-2024-07-18';
protected $history = 'in_memory';
protected $provider = 'default';
protected $storeMeta = true;
protected $tools = [];
public function instructions(): string
{
return <<<EOT
#依頼
以下に提示する複数のURLと、それぞれの「関連度(目的との合致度)(1に近ければ近いほど関連度が高い)」を参考にして、相手が最も喜ぶ誕生日プレゼントを3案提案してください。URLの関連度は提案の優先度や重み付けに反映してください。
#形式
提案は3案提示する
各提案に以下の要素を含めること
•「理由」:なぜそのプレゼントが相手に最適か
•「おすすめポイント」:楽しみ方、魅力、使い方など
•「参考にしたURLと関連度」:具体的にどのURLを使い、どの程度重視したか記載する
#ルール
提示された複数のURLのみを情報源とする(外部知識や推測は含めない)
各URLの関連度(1に近ければ近いほど関連度が高い)に従って情報を重み付けし、最終提案に反映する
曖昧な表現を避け、具体的に説明する
#評価・改善
URLの情報が最大限活用され、関連度が適切に反映されているか?
提案が具体的で、相手の好みや状況に合った内容になっているか?
EOT;
}
public function prompt(String $message): string
{
return $message;
}
#[Tool('Tavily検索')]
public function searchTvly(String $tavilyQuery): string
{
$results = "";
try {
$response = Http::withToken(env('TVLY_API_KEY'))
->post('https://api.tavily.com/search', [
// 各optionについて詳しくは公式ドキュメントを参照してください
// https://docs.tavily.com/documentation/api-reference/endpoint/search
// 検索クエリ
"query" => $tavilyQuery,
// 一般的な検索か時事ニュースなどの検索かを選択
"topic" => "general",
// 検索クエリを関連性が高くなるようにカスタマイズするか
"search_depth" => "advanced",
// 検索結果を最大何件取得するか
"max_results" => 5,
// レスポンスに生のhtmlテキストを含めるか
"include_raw_content" => true,
]);
// 200番以外が返ってきた時は例外を投げる
$response->throwUnlessStatus(200);
$response = $response->json()['results'];
foreach ($response as $result) {
// raw_contentがnullの場合はスキップ
if ($result['raw_content']) {
$results .= $result['url'] . $result['score'];
}
}
} catch (Throwable $e) {
Log::error($e->getMessage());
}
return $results;
}
}
実際に使っているところ※$friend
には誕生日を贈りたい友達の情報(性別、好み、贈りたいプレゼントの予算感)が格納されています
$agent = SuggestAgent::for('user-123');
$results = $agent->respond(
<<<EOT
#相手の情報
性別: {$friend->getAttribute('viewGender')}
好み: {$friend->favorites->pluck('favorite')->implode(', ')}
予算: {$friend->budget}円
EOT
);
出力結果
以下に、相手が最も喜ぶ誕生日プレゼントを3案提案します。各提案にはその理由、おすすめポイント、および参考にしたURLを記載しています。
### 提案1: 高級クラフトビールセット
- **理由**: 相手の好みであるお酒にピッタリで、特別な誕生日を大人の贅沢な味わいで祝うことができます。クラフトビールは様々な味が楽しめ、飲み比べで新しい発見がある楽しさもあります。
- **おすすめポイント**: セットによって異なる種類や味わいが楽しめるので、飲む人にとっても新しい体験となり、友人とシェアしたり、特別な瞬間を共有するのにも最適です。
- **参考にしたURLと関連度**: [Giftpedia - お酒誕生日プレゼント](https://giftpedia.jp/2767440.8708723)(関連度0.87)
### 提案2: 人気のゲームソフト(PS4 or Switch用)
- **理由**: ゲームが好きな相手にとって、市場で話題のゲームソフトは大きな魅力です。ゲームは楽しむだけでなく、ストーリーやキャラクターとの出会いを通じて一種の体験ができます。
- **おすすめポイント**: 友人やオンラインで他のプレイヤーと楽しむこともでき、相手の嗜好に合ったジャンルを選べば、何時間でも楽しむことができます。
- **参考にしたURLと関連度**: [Giftpedia - ゲーム誕生日プレゼント](https://giftpedia.jp/2771440.6628188)(関連度0.66)
### 提案3: スマホ用の便利なガジェット(モバイルバッテリーなど)
- **理由**: ガジェット好きな相手には、実用性の高いアイテムが最適です。特に、モバイルバッテリーはスマホを頻繁に使用する現代の必需品で、友人や外出時にも役立ちます。
- **おすすめポイント**: コンパクトで軽量なデザインのものや、多機能タイプなど、使い勝手が広がる選択肢がたくさんあるので、相手のライフスタイルに合わせて選ぶ楽しさもあります。
- **参考にしたURLと関連度**: [Lifestyle - ガジェット誕生日プレゼント](https://gift.biglobe.ne.jp/goods_appliances_gadget/budget_3000)(関連度0.81)
以上の提案は、相手の趣味や好みを考慮し、関連度の高い情報を基にしたものです。相手に喜ばれるプレゼント選びに役立ててください。
消費トークン量取得
LarAgentと冒頭述べたPrismとの違いの一つ (Prismも使用トークン量を取得できるようでした)に「(使用モデルが対応しているかどうかによるが)入出力に使用したAPIトークンを取得できる」点があります。アプリのコードを少し書き換えて...
$response = $agent
->returnMessage()
->respond(
<<<EOT
#相手の情報
性別: {$friend->getAttribute('viewGender')}
好み: {$friend->favorites->pluck('favorite')->implode(', ')}
予算: {$friend->budget}円
EOT
);
$usage = $response->toArrayWithMeta()['metadata']['usage']['total_tokens'];
$usageに格納された値:1657
この機能を使ってトークン制限をかける...なんてことも出来ます!
また、公式からエンジンフックを使って回答ログを残すみたいなユースケースも紹介されていました
一通り触ってみた感想ですが、愚直にLLMのAPIにcURLを叩かなくても良いことに加えてフックやツールなどを定義しやすいところがかなり推しポイントでした!
まだまだ開発途中なのでこれからの開発にも期待ですね〜