Урок 11

Практика: первый AI-агент

Соберём всё вместе: агент поддержки с tools, structured output и стримингом.

Шаг 1: Агент

php artisan make:agent SupportAgent --structured
namespace App\Ai\Agents;

use App\Ai\Tools\LookupOrder;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;

class SupportAgent implements Agent, HasTools, HasStructuredOutput
{
    use Promptable;

    public function instructions(): string
    {
        return 'Ты — агент поддержки. Помогай с заказами. Будь вежлив.';
    }

    public function tools(): array
    {
        return [new LookupOrder];
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'answer' => $schema->string()->required(),
            'category' => $schema->string()->enum(['billing', 'technical', 'general'])->required(),
        ];
    }
}

Шаг 2: Tool

php artisan make:tool LookupOrder
namespace App\Ai\Tools;

use App\Models\Order;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Stringable;

class LookupOrder implements Tool
{
    public function description(): Stringable|string
    {
        return 'Находит заказ по ID.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'order_id' => $schema->integer()->required(),
        ];
    }

    public function handle(array $arguments): string
    {
        $order = Order::find($arguments['order_id']);

        return $order
            ? "Заказ #{$order->id}: {$order->status}"
            : 'Заказ не найден';
    }
}

Шаг 3: Route

use App\Ai\Agents\SupportAgent;

Route::post('/support', function (Request $request) {
    $response = SupportAgent::make(user: $request->user())
        ->prompt($request->input('message'));

    return response()->json([
        'answer' => $response['answer'],
        'category' => $response['category'],
    ]);
});

Шаг 4: Streaming route

Route::get('/support/stream', function (Request $request) {
    return SupportAgent::make(user: $request->user())
        ->stream($request->input('message'))
        ->usingVercelDataProtocol();
});

Шаг 5: Queue для длинных

Route::post('/support/async', function (Request $request) {
    SupportAgent::make(user: $request->user())
        ->queue($request->input('message'))
        ->then(fn ($response) => $request->user()->notify(new SupportDone($response)));

    return response()->json(['status' => 'queued']);
});

Итоги

  • Агент с tools и structured output
  • Tool с валидацией
  • Route для sync, stream, queue
  • Использование в реальном приложении