Laravel AI SDK 1.4: як агенти обробляють збої інструментів

Laravel AI SDK v1.4.0, про який на Laravel News пише Paul Redmond, змінює поведінку агентів при збоях інструментів, роботу Agent::fake() та помилки OpenAI 429. Під час оновлення важливо перевірити обробку винятків, тести approval flow та фронтендні стріми.

Виняток інструмента більше не перериває запуск агента

У попередніх версіях виняток усередині інструмента завершував запуск і спричиняв спрацювання AgentFailed. У v1.4.0 SDK перехоплює помилку та віддає її моделі як невдалий результат інструмента, тому агент може пояснити збій користувачу або викликати інший інструмент.

Модель отримує текст на кшталт:

The tool call failed: Order 1234 can no longer be refunded.

Запуск продовжується, але:

  • виняток усе одно потрапляє у ваш обробник через report();
  • ToolFailed як і раніше, спрацьовує;
  • у стрімах надходить подія ToolResult з successful = false.

Важливе обмеження: відновлюється лише виняток самого інструмента. Якщо помилку викидає слухач події, запуск позначається як невдалий і завершується.

Якщо ваш код покладався на те, що помилка інструмента завершує запуск, автори пропонують повторно викидати її зі слухача ToolFailed:

use Illuminate\Support\Facades\Event;
use Laravel\Ai\Events\ToolFailed;
Event::listen(ToolFailed::class, fn (ToolFailed $event) => throw $event->exception);

Agent::fake та approval flow

Human-in-the-loop approval призупиняє запуск до схвалення або відхилення інструмента. До v1.4.0 під Agent::fake() схвалений інструмент взагалі не запускався, тому тести не покривали повний сценарій.

Тепер фейковий агент поводиться як реальний провайдер: схвалений інструмент виконується, його результат зберігається в toolResults, відхилений отримує результат denied. Приклад з матеріалу:

SupportAgent::fake([
    new ToolCall('call-1', 'SendEmail', ['to' => '[email protected]']),
    'The email is on its way.',
]);

$paused = (new SupportAgent)->forUser($user)->prompt('Email John the release notes');

$response = (new SupportAgent)
    ->continue($paused->conversationId, as: $user)
    ->prompt(Decisions::from(['call-1' => Decision::approve()]));
$response->toolResults[0]->result; // "Email sent to [email protected]."
$response->text; // "The email is on its way."

Наслідки для оновлення:

  • тести, які відновлюють призупинений запуск під Agent::fake(), тепер повинні враховувати реальне виконання схваленого інструмента;
  • відхилення без причини завершує turn, тому для очікуваної відповіді від агента в тестах варто передавати reason, наприклад Decision::reject('Not now.').

OpenAI 429 та стрім помилок

OpenAI використовує 429 і для обмежень rate limit, і для помилок білінгу (insufficient_quota, credit_balance_exhausted). Раніше всі 429 перетворювалися на RateLimitedException і могли повторно виконуватися.

У v1.4.0:

  • 429 з білінговими кодами повертає InsufficientCreditsException і не повторюється;
  • інші 429 і надалі викидають RateLimitedException.

Механізм failover не змінюється, але код, який перехоплював RateLimitedException для помилок квоти або балансу, тепер має перехоплювати InsufficientCreditsException.

У стрімах усі помилки провайдерів тепер передаються з єдиним іменем події error. Конкретний код лишається в payload.type, а збої джобів, як і раніше, йдуть як stream_failed. Фронтендам потрібно перейти з прослуховування подій за кодами (403, PERMISSION_DENIED) на єдину подію error.

Raw HTTP response для додаткових полів

Класифікація, ембедінги, транскрипції, зображення та reranking отримали властивість raw з необробленою HTTP-відповіддю провайдера, як у текстових відповідях. Це дозволяє читати поля, що не мапляться в об'єкти SDK, наприклад usage.cost від OpenRouter:

$response = Classification::of($ticket)
    ->questions($questions)
    ->classify(provider: 'openrouter');
$cost = $response->raw->json('usage.cost');

raw стає null після серіалізації, а для ембедінгів — ще й коли всі вектори отримані з кешу.

Окремо автори зазначають, що генерація заголовка розмови тепер також відбувається як агентський запуск і породжує стандартні agent events. Якщо такі події потрібно відрізняти від звичайних turn, варто орієнтуватися на parentInvocationId.


Джерело: Paul Redmond – Laravel News

Пошук

Закрити