---
title: "Реліз Laravel AI SDK 0.11: Спостережуваність Виконання Агентів і Розширені Можливості Відмовостійкості"
url: https://laravelukraine.com/blog/reliz-laravel-ai-sdk-011-sposterezuvanist-vikonannia-agentiv-i-rozsireni-mozlivosti-vidmovostiikosti
date: 2026-08-22
source: https://laravel-news.com/laravel-ai-sdk-0-11?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+laravelnews
---

# Реліз Laravel AI SDK 0.11: Спостережуваність Виконання Агентів і Розширені Можливості Відмовостійкості

Laravel AI v0.11.0 надає виконанню агента єдиний ідентифікатор кореляції та набір подій життєвого циклу, які спрацьовують навколо кожного круготрипу провайдера та кожного виклику інструменту. До цього виконання, що потребувало п'яти круготрипів для вирішення своїх викликів інструментів, виглядало ідентично тому, що завершилося за один виклик, а виконання, яке зазнало невдачі в шлюзі, не звітувало про це взагалі. Реліз вийшов 19 серпня 2026 року з 36 об'єднаними pull request та дванадцятьма новими контриб'юторами.

## Трасування Повного Виконання Агента

Сім pull request від [@pushpak1300](https://github.com/pushpak1300) переробили спосіб звітування виконання агента про себе.

`streamPrompt()` вже генерував ідентифікатор виконання на рівні run, але `prompt()` цього не робив, тому синхронне middleware бачило `$prompt->invocationId === null`. Натомість streaming middleware бачило реальне значення, а відмовостійкість на трьох провайдерах виробляла три непов'язані ID для одного виконання. `prompt()` тепер генерує ID заздалегідь, а провайдер повторно використовує те, що надав викликач ([
#871](https://github.com/laravel/ai/pull/871)).

`RunContext` тепер несе ідентичність виконання та диспетчеризує події безпосередньо, замінюючи пару callback-функцій, які кожен провайдер реєстрував у циклі генерації. Стара схема відстежувала поточний ID виклику інструменту в єдиній змінній властивості, що ламалося при вкладенні: агент, викликаний як інструмент, перезаписував ID до того, як зовнішня подія `ToolInvoked` спрацьовувала, тому зовнішня подія несла ID внутрішнього виклику. ID тепер генерується всередині `executeTool()`, і інструменти можуть прочитати його за допомогою `Request::toolInvocationId()` ([
#872](https://github.com/laravel/ai/pull/872)).

`StartingStep`, `StepCompleted` та `StepFailed` спрацьовують навколо кожного круготрипу провайдера як на синхронному, так і на streaming шляху ([
#873](https://github.com/laravel/ai/pull/873)). `StartingStep` несе повідомлення та вирішені опції, з якими надсилається крок, `StepCompleted` несе повну відповідь кроку, а кожна завершальна подія несе час виконання кроку в мілісекундах, що відповідає `QueryExecuted::$time`. `StartingStep` також несе всю історію повідомлень виконання, тому `ShouldQueue` listener серіалізує її разом з будь-якими вкладеннями.

`executeTool()` раніше не мав `catch`, тому інструмент, обробник якого кидав виняток, поширювався прямо з циклу генерації без запису про те, який інструмент його спричинив. `ToolFailed` тепер повідомляє про це, несучи той самий ID виклику інструменту, що й `InvokingTool`, який його відкрив, і виняток все ще перекидається ([
#874](https://github.com/laravel/ai/pull/874)). На рівні run `AgentFailed` повідомляє про термінальну невдачу один раз на виконання, і лише після того, як виконання завершилося, що означає після того, як відмовостійкість вичерпала ланцюг провайдерів ([
#876](https://github.com/laravel/ai/pull/876)).

Виклик інструменту тепер публікує свої ID виконання run та виклику інструменту на час свого виконання, і будь-який агент, викликаний під час його роботи, підхоплює їх як `parentInvocationId` та `parentToolInvocationId` ([
#875](https://github.com/laravel/ai/pull/875)). Це зв'язує суб-агента з виконанням, яке делегувало йому завдання, і охоплює написаний вручну інструмент, який викликає агента, а не лише `AgentTool`. Зв'язок не перетинає межу черги, тому prompt, відправлений з `promptOnQueue()` зсередини інструменту, все одно починає своє власне виконання без батьківського.

## Розміщений Пошук Інструментів

Кожен інструмент, який агент надає, раніше відправлявся провайдеру при кожному запиті. З великим каталогом це коштує токенів і дає моделі довге меню для вибору. [@behzadsp](https://github.com/behzadsp) додав обгортку `ToolSearch` у [
#697](https://github.com/laravel/ai/pull/697), яка відкладає інструменти всередині неї, щоб OpenAI та Anthropic завантажували їх на вимогу через власний розміщений пошук:

```php
public function tools(): iterable
{
    return [
        new WeatherTool,
        new ToolSearch(tools: [new SearchInvoices, new RefundOrder]),
    ];
}
```

Самі інструменти не потребують змін: немає інтерфейсу для реалізації, немає опцій провайдера. Обгортка відображається на `{"type":"tool_search"}` на OpenAI та на типи `tool_search_tool_*` на Anthropic, а стратегія пошуку Anthropic є аргументом конструктора, валідованим проти `regex` та `bm25`:

```php
new ToolSearch(tools: [new SearchInvoices], strategy: 'bm25')
```

Провайдери, які не підтримують розміщений пошук, кидають чіткий виняток до відправлення запиту, а не відкидають інструменти обгортки, і перевірка виконується навіть коли обгортка порожня, щоб неправильні конфігурації виявлялися під час розробки. Лише одна обгортка може бути зареєстрована на запит, а розміщений пошук OpenAI вимагає збережених відповідей, тому `ToolSearch`, використаний з `store=false`, кидає виняток. Той самий pull request додає відтворення виводу без стану для OpenAI, захоплюючи повний упорядкований вивід відповіді та відтворюючи його дослівно при продовженні, коли `store=false`.

## Більше Сценаріїв Відмовостійкості

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

Коли провайдер був недоступний через те, що хост був недоступний, з'єднання було відхилено або локальний екземпляр Ollama не працював, HTTP-клієнт Laravel кидав `ConnectionException`. Це окрема гілка ієрархії винятків клієнта від `RequestException`, тому вона повністю проскочила обробник відмовостійкості. Тепер вона перекидається як `ProviderConnectionException`, що реалізує `FailoverableException`, у спільній ознаці, яку використовує кожен шлюз ([@JVillator0](https://github.com/JVillator0), [
#781](https://github.com/laravel/ai/pull/781)).

Набір статусів, що розглядаються як перевантажений провайдер, виріс з лише `503` до `502`, `503`, `504`, `520`, `522` та `524`, додавши таймаути шлюзу та помилки Cloudflare, які провайдери за Cloudflare повертають під час інцидентів. Чистий `500` навмисно залишений поза списком, оскільки це може бути детермінована помилка, яку відмовостійкість замаскує ([@sulimanbenhalim](https://github.com/sulimanbenhalim), [
#810](https://github.com/laravel/ai/pull/810)). Anthropic отримав таке саме оброблення для своїх тимчасових помилок шлюзу та Cloudflare ([
#884](https://github.com/laravel/ai/pull/884)).

Anthropic також відхиляє запити з HTTP 400, коли організація досягає свого налаштованого ліміту витрат, а формулювання повідомлення не відповідало жодним існуючим шаблонам кредитів. Отже, необроблений `RequestException` ніколи не досягав циклу відмовостійкості. [@oddvalue](https://github.com/oddvalue) повідомив приблизно про 990 необроблених винятків за місяць саме з цієї причини, з другим ключем Anthropic та провайдером OpenAI, які простоювали в тому самому ланцюгу. Додавання `usage limit` до списку шаблонів виправляє це ([
#864](https://github.com/laravel/ai/pull/864)).

## Підтримка Провайдерів

- xAI отримав веб-пошук ([
#857](https://github.com/laravel/ai/pull/857)) та пошук файлів ([@timmcleod](https://github.com/timmcleod), [
#894](https://github.com/laravel/ai/pull/894))
- Транскрипція з'явилася для OpenAI-сумісного провайдера ([
#858](https://github.com/laravel/ai/pull/858)) та для Groq ([
#868](https://github.com/laravel/ai/pull/868))
- OpenRouter підтримує серверний інструмент веб-завантаження ([@CamilleScholtz](https://github.com/CamilleScholtz), [
#889](https://github.com/laravel/ai/pull/889))
- Цитати веб-завантаження Anthropic тепер з'являються на `$response->meta->citations`, які раніше поверталися порожніми, оскільки читалися лише блоки `web_search_tool_result`. Тільки для не-streaming відповідей ([
#892](https://github.com/laravel/ai/pull/892))
- Текстова модель за замовчуванням Gemini перемістилася на `gemini-3.7-flash` ([
#887](https://github.com/laravel/ai/pull/887))

## Помічники Для Тестування

`assertPromptedTimes()` приєднується до фейкових тверджень у трейті `Promptable`, працюючи так само, як `Bus::assertDispatchedTimes()` та `Notification::assertSentTimes()` ([@F1nnG](https://github.com/F1nnG), [
#891](https://github.com/laravel/ai/pull/891)):

```php
SalesCoach::assertPromptedTimes(3);
```

Фейкова генерація транскрипцій, зображень, аудіо та embeddings у черзі тепер запускає callback `then(...)`, щоб тест міг стверджувати про те, що зробив callback, замість зупинки на диспетчеризації ([@gdebrauwer](https://github.com/gdebrauwer), [
#797](https://github.com/laravel/ai/pull/797)).

## Інші Виправлення

- Потокове використання підсумовується через кроки виклику інструментів у кожному шлюзі провайдера ([
#698](https://github.com/laravel/ai/pull/698)), шлюз OpenAI більше не втрачає токени запису кешу з використання ([
#846](https://github.com/laravel/ai/pull/846)), а токени кеш-попадань DeepSeek виключаються зі звітованої кількості токенів промпту ([
#896](https://github.com/laravel/ai/pull/896))
- Частково осиротілі виклики інструментів фільтруються при відтворенні історії розмови ([
#758](https://github.com/laravel/ai/pull/758)), а невідомі локальні виклики інструментів виправляються, а не призводять до збою виконання ([
#859](https://github.com/laravel/ai/pull/859))
- Відповіді Mistral, що повертають контент як список блоків, обробляються ([
#866](https://github.com/laravel/ai/pull/866)), причини зупинки Anthropic `refusal` та `model_context_window_exceeded` відображаються ([
#881](https://github.com/laravel/ai/pull/881)), markdown огорожі коду видаляються зі структурованого виводу OpenAI-сумісного провайдера ([
#879](https://github.com/laravel/ai/pull/879)), а стан текстової частини скидається після `TextEnd` у потоках Responses API ([
#854](https://github.com/laravel/ai/pull/854))
- Вкладення документів S3 реконструюються у `File::fromArray` ([
#878](https://github.com/laravel/ai/pull/878)), джерела текстових документів надсилаються як `text/plain` на Anthropic ([
#886](https://github.com/laravel/ai/pull/886)), а запит MCP залишається прив'язаним під час перетворення результатів генератора ([
#888](https://github.com/laravel/ai/pull/888))

## Примітки До Оновлення

Помилка провайдера, повідомлена всередині тіла потоку, HTTP 200, чий SSE payload несе об'єкт `error`, тепер кидає `StreamErrorException` замість завершення кроку з `break`. Споживачі раніше отримували частковий `->text`, без причини завершення, без термінального `StreamEnd` та без сигналу, що виконання зазнало невдачі. Виняток несе власну подію помилки провайдера на `->error`, і він навмисно не є відмовостійким, оскільки ці надходять як 200 ([
#870](https://github.com/laravel/ai/pull/870)).

Дві події отримали обов'язкові аргументи конструктора: `AgentFailedOver` приймає `string $invocationId` як свій останній аргумент ([
#871](https://github.com/laravel/ai/pull/871)), а `ToolInvoked` приймає `float $time` ([
#874](https://github.com/laravel/ai/pull/874)). Listeners не постраждали; оновлення потрібне лише коду, що конструює ці події вручну.

`AgentFailedOver` також більше не спрацьовує для фінального провайдера в ланцюгу. Ця спроба не має на що відступити, тому вона повідомляє про невдачу виконання через `AgentFailed` натомість ([
#876](https://github.com/laravel/ai/pull/876)).

Додатки, що використовують текстову модель Gemini за замовчуванням, перейдуть на `gemini-3.7-flash` ([
#887](https://github.com/laravel/ai/pull/887)). Встановіть модель явно, якщо потрібна стара.

Оновіться за допомогою `composer update laravel/ai`, або встановіть пакет вперше з `composer require laravel/ai` та дотримуйтесь [офіційної документації](https://laravel.com/docs/ai) для конфігурації.
