最後に:実際に Laravel で AIエージェントを構築するためのソースツリーとコード例
ここまで述べてきたように、AIエージェントの本質は 「API × 状態管理 × ループ」 にあります。では実際に、これを Laravel でどう構築するのか?
以下では、AIエージェントを Laravel 上で実装する際の 最低限のソースツリー と コード例 を紹介します。
/app
/Agents
/LP
LPAgent.php
LPWorkflow.php
LPStateMachine.php
/Services
/AI
OpenAIService.php
/LP
LPAssembler.php
/Models
Agent.php
/Http/Controllers
LPAgentController.php
/database
/migrations
create_agents_table.php
/routes
api.php
この構成は、以下の思想でできています:
- LPAgent → どのステップを実行するか決める司令塔
- LPWorkflow → 実際の処理(GPTコール)が書かれる場所
- LPStateMachine → 「次のアクション」を決める脳みそ
- Agentモデル → state/memory を JSON で保持
- OpenAIService → GPT への API 呼び出し統合
- LPAssembler → HTML合成など結果物の組み立て
- Controller → APIエンドポイント
- Migration → エージェント状態のDB保存
どんなエージェントでも、このテンプレートで複製できます。
Agentモデル & Migration
/database/migrations/create_agents_table.php
Schema::create('agents', function (Blueprint $table) {
$table->id();
$table->string('type'); // 'lp', 'job', 'subsidy' など
$table->json('state')->nullable(); // next_action / progress など
$table->json('memory')->nullable(); // GPTの中間生成物
$table->timestamps();
});
/app/Models/Agent.php
class Agent extends Model
{
protected $fillable = ['type', 'state', 'memory'];
protected $casts = [
'state' => 'array',
'memory' => 'array',
];
}
これは AIエージェントの頭脳の保存場所 です。
LPエージェントの実装(司令塔)
/app/Agents/LP/LPAgent.php
namespace App\Agents\LP;
use App\Models\Agent;
class LPAgent
{
public function runStep(Agent $agent)
{
$state = $agent->state;
// 今やるべきアクション
$nextAction = $state['decision']['next_action'] ?? 'start_analysis';
$workflow = app(LPWorkflow::class);
if (!method_exists($workflow, $nextAction)) {
throw new \RuntimeException("Unknown action: {$nextAction}");
}
return $workflow->{$nextAction}($agent);
}
}
これが エージェントのメインループ です。今のステップを読み取り、そのステップのメソッドを実行し、結果を state/memory に保存して次へ進みます。
状態管理(ステートマシン)
/app/Agents/LP/LPStateMachine.php
namespace App\Agents\LP;
use App\Models\Agent;
class LPStateMachine
{
public function updateState(Agent $agent, string $next)
{
$state = $agent->state;
// 次のステップに移動
$state['decision']['next_action'] = $next;
$state['current']['status'] = $next;
// クレジット消費管理(GPT呼び出し回数)
$state['credit']['used'] += 1;
$state['credit']['remaining'] =
$state['credit']['limit'] - $state['credit']['used'];
$agent->state = $state;
$agent->save();
return $agent;
}
}
AI開発なのに「ステートマシン」が重要なのは、AIが暴走せず、確実に目的を達成するために必要な構造だからです。
LPエージェントの処理(ワークフロー)
/app/Agents/LP/LPWorkflow.php
namespace App\Agents\LP;
use App\Models\Agent;
use App\Services\AI\OpenAIService;
use App\Services\LP\LPAssembler;
class LPWorkflow
{
public function __construct(
protected OpenAIService $ai,
protected LPAssembler $assemble
) {}
/** STEP1: ヒアリング分析 */
public function start_analysis(Agent $agent)
{
$input = $agent->memory['raw_input_text'];
$json = $this->ai->gptJson(<<<PROMPT 以下の事業説明から - 事業目的 - USP - ターゲットの悩み を抽出しJSONで返してください。 {$input} PROMPT);
$memory=$agent->memory;
$memory['business_summary'] = $json;
$agent->memory = $memory;
return app(LPStateMachine::class)
->updateState($agent, 'generate_aidma_copy');
}
/** STEP2: AIDMAコピー生成 */
public function generate_aidma_copy(Agent $agent)
{
$summary = json_encode($agent->memory['business_summary']);
$json = $this->ai->gptJson(<<<PROMPT 以下の要約から AIDMAのA/I/D/M/A2を3パターンずつ生成しJSONで返してください。 {$summary} PROMPT);
$mem=$agent->memory;
$mem['aidma_copy'] = $json;
$agent->memory = $mem;
return app(LPStateMachine::class)
->updateState($agent, 'generate_color_theme');
}
/** STEP3: カラーテーマ */
public function generate_color_theme(Agent $agent)
{
$json = $this->ai->gptJson(<<<PROMPT 以下の事業概要に合うLPのカラーテーマを main/sub/accent/emphasis/cta の5色で
3パターン生成しJSONで返してください。 {$agent->memory['business_summary']}
PROMPT);
$mem = $agent->memory;
$mem['color_theme'] = $json;
$agent->memory = $mem;
return app(LPStateMachine::class)
->updateState($agent, 'generate_html_blocks');
}
/** STEP4: HTMLブロック生成 */
public function generate_html_blocks(Agent $agent)
{
$json = $this->ai->gptJson(<<<PROMPT 以下のAIDMAコピーとカラーテーマから、 Bootstrap5で使えるHTMLセクションを A/I/D/M/A2
各3パターン生成してください。 AIDMA: {$agent->memory['aidma_copy']}
Theme:
{$agent->memory['color_theme']}
PROMPT);
$mem = $agent->memory;
$mem['html_blocks'] = $json;
$agent->memory = $mem;
return app(LPStateMachine::class)
->updateState($agent, 'assemble_lp');
}
/** STEP5: LP完成品の組み立て */
public function assemble_lp(Agent $agent)
{
$lp = $this->assemble->build($agent->memory);
$agent->memory['lp_variants'] = $lp;
$agent->save();
return app(LPStateMachine::class)
->updateState($agent, 'completed');
}
}
GPT を呼び出すサービス
/app/Services/AI/OpenAIService.php
namespace App\Services\AI;
use Illuminate\Support\Facades\Http;
class OpenAIService
{
public function gptJson(string $prompt): array
{
$res = Http::withToken(env('OPENAI_API_KEY'))
->post('https://api.openai.com/v1/chat/completions', [
'model' => 'gpt-4o-mini',
'response_format' => ['type' => 'json_object'],
'messages' => [
['role' => 'user', 'content' => $prompt]
]
]);
$content = $res->json()['choices'][0]['message']['content'] ?? '{}';
return json_decode($content, true);
}
}
LPを組み立てるサービス
/app/Services/LP/LPAssembler.php
namespace App\Services\LP;
class LPAssembler
{
public function build(array $memory): array
{
$blocks = $memory['html_blocks'];
return [
'variant_1' => $blocks['A'][0] . $blocks['I'][0] . $blocks['D'][0] . $blocks['M'][0] . $blocks['A2'][0],
'variant_2' => $blocks['A'][1] . $blocks['I'][1] . $blocks['D'][1] . $blocks['M'][1] . $blocks['A2'][1],
'variant_3' => $blocks['A'][2] . $blocks['I'][2] . $blocks['D'][2] . $blocks['M'][2] . $blocks['A2'][2],
];
}
}
APIエンドポイント
/app/Http/Controllers/LPAgentController.php
class LPAgentController extends Controller
{
public function start(Request $req)
{
$agent = Agent::create([
'type' => 'lp',
'state' => [
'decision' => ['next_action' => 'start_analysis'],
'credit' => ['used' => 0, 'limit' => 50, 'remaining' => 50],
],
'memory' => [
'raw_input_text' => $req->input('text'),
],
]);
return response()->json(['agent_id' => $agent->id]);
}
public function run($id)
{
$agent = Agent::findOrFail($id);
app(\App\Agents\LP\LPAgent::class)->runStep($agent);
return $agent->fresh();
}
public function status($id)
{
return Agent::findOrFail($id);
}
}
/routes/api.php
Route::post('/lp/start', [LPAgentController::class, 'start']);
Route::post('/lp/run/{id}', [LPAgentController::class, 'run']);
Route::get('/lp/status/{id}', [LPAgentController::class, 'status']);
まとめ:この構造は「どんなエージェントでも」量産できる。LPエージェント以外にもそのまま流用できます。JobAgentや補助金エージェントなど、ワークフロー部分だけ差し替えれば済みます。
これをAIエージェントの雛形にすることで最小単位のエージェントを作ることができます。ご参考までに