Відповідь моделі може генеруватися десятки секунд, а з інструментами - ще довше. Синхронний prompt() у контролері тримає PHP-воркер увесь цей час і впирається в тайм-аути проксі та PHP.
Три підходи:
1. Стримінг (SSE) - користувач бачить текст по мірі генерації:
Route::post('/assistant', function (Request $request) {
return SupportAssistant::make(user: $request->user())
->stream($request->validated('message'))
->then(function (StreamedAgentResponse $response) {
// повна відповідь: зберегти, порахувати токени
});
});
StreamableAgentResponse повертається з маршруту як потокова відповідь. Перший токен приходить за секунду-дві, але воркер зайнятий до кінця генерації.
2. Черга - запит повертається одразу, генерація йде у воркері:
ReviewClassifier::make()
->queue($review->body)
->then(fn (AgentResponse $response) => $review->update([...]))
->catch(fn (Throwable $e) => report($e));
return back()->with('status', 'Аналіз запущено');
Підходить для фонових задач: класифікація, резюме, обробка документів. Результат - у базу, а інтерфейс опитує статус чи отримує подію.
3. Трансляція подій - генерація у черзі, а потік подій іде в браузер через WebSocket (Reverb):
SupportAssistant::make()->broadcastOnQueue($message, new PrivateChannel("chat.{$chat->id}"));
Поєднує обидва: воркер вебсервера звільняється одразу, а користувач бачить текст у реальному часі.
Як обрати:
| Сценарій | Підхід |
|---|---|
| чат, де користувач чекає відповідь | стримінг або черга + трансляція |
| фонова обробка без очікування | черга |
| багато одночасних користувачів | черга + трансляція (не займати вебворкери) |
Інфраструктурні деталі:
- тайм-аути:
#[Timeout]агента,timeoutворкера черги йretry_afterпідключення мають бути узгоджені, інакше задачу візьме другий воркер, і модель відповість двічі (і двічі буде оплачено); - буферизація: Nginx, Cloudflare й інші проксі можуть буферизувати SSE - потрібні
X-Accel-Buffering: noі відповідні налаштування; - окрема черга для LLM-задач з власним лімітом воркерів - щоб повільні виклики моделі не затримували листи й сповіщення;
- повтори: при збоях провайдера краще резервний провайдер (
provider: [...]), ніж повтор задачі цілком; - Octane / FrankenPHP - стримінг утримує воркер так само; рахуйте кількість воркерів під одночасні потоки.