Middle: питання на співбесіді з теми «AI SDK, MCP і Boost»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
3 питання
Проблема: розбирати вільний текст моделі регулярними виразами - крихко. Модель може додати пояснення, змінити формат чи забути поле.
Структурований вивід: агент описує JSON-схему відповіді, а провайдер змушує модель повернути дані саме в такій формі.
php artisan make:agent ReviewClassifier --structured
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasStructuredOutput;
use Laravel\Ai\Promptable;
class ReviewClassifier implements Agent, HasStructuredOutput
{
use Promptable;
public function instructions(): string
{
return 'Класифікуй відгук покупця. Не вигадуй фактів, яких немає у тексті.';
}
public function schema(JsonSchema $schema): array
{
return [
'sentiment' => $schema->string()->enum(['positive', 'neutral', 'negative'])->required(),
'topics' => $schema->array()->items($schema->string())->required(),
'needs_reply' => $schema->boolean()->required(),
'score' => $schema->integer()->min(1)->max(5)->required(),
];
}
}
Відповідь поводиться як масив:
$result = (new ReviewClassifier)->prompt($review->body);
$review->update([
'sentiment' => $result['sentiment'],
'needs_reply' => $result['needs_reply'],
]);
Що дає схема:
enumобмежує значення - у базу не потрапить"Positive!"замістьpositive;- типи й межі (
integer,min,max) - менше перевірок у коді; - вкладені об'єкти й масиви об'єктів - для витягання списків (позиції з рахунку, контакти з листа).
Чого схема не гарантує:
- правильності змісту: модель поверне валідний JSON з неправильною класифікацією чи вигаданим значенням. Схема перевіряє форму, а не правду;
- однакової підтримки в усіх провайдерів - строгий режим відрізняється, і частина обмежень схеми може ігноруватися.
Тому дані від моделі - це вхідні дані користувача:
- валідувати перед збереженням (
Validator::make($result->toArray(), [...])чи enum PHP зtryFrom); - не використовувати як SQL, шлях до файлу чи HTML без екранування;
- для критичних рішень (повернення коштів, блокування) - людина в процесі.
Практичні поради:
- описи полів (
->description('...')) у схемі допомагають моделі так само, як інструкції; - низька температура (
#[Temperature(0)]) для класифікації й витягання даних - стабільніші результати; - у тестах
ReviewClassifier::fake([['sentiment' => 'negative', ...]])повертає структуровану відповідь без звернення до API.
Інструмент - функція застосунку, яку модель може попросити викликати: знайти замовлення, перевірити залишок, створити заявку. Модель не виконує код сама - вона повертає «виклич інструмент X з аргументами Y», SDK виконує handle і передає результат назад моделі.
php artisan make:tool FindOrder
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
class FindOrder implements Tool
{
public function __construct(private User $user) {}
public function description(): string
{
return 'Знаходить замовлення поточного користувача за номером і повертає статус доставки.';
}
public function schema(JsonSchema $schema): array
{
return ['number' => $schema->integer()->required()];
}
public function handle(Request $request): string
{
$order = $this->user->orders()->where('number', $request['number'])->first();
return $order ? "Статус: {$order->status->label()}" : 'Замовлення не знайдено.';
}
}
// в агенті
public function tools(): iterable
{
return [new FindOrder($this->user)];
}
Головне правило безпеки: модель - недовірений учасник. Її аргументи можуть бути сформовані під впливом тексту від зловмисника (prompt injection у листі, відгуку, документі). Тому:
- авторизація всередині інструмента: шукати замовлення через
$this->user->orders(), а неOrder::find($number)- інакше модель «знайде» чуже замовлення; - мінімальний набір інструментів: агенту підтримки не потрібен інструмент видалення;
- валідація аргументів - схема описує форму, але межі й права перевіряє код;
- обмеження кроків:
#[MaxSteps(5)]- щоб модель не зациклилася у викликах і не спалила бюджет.
Схвалення людиною для незворотних дій: інструмент реалізує Approvable з трейтом InteractsWithApprovals - тоді виконання призупиняється, доки людина не підтвердить дію (повернення коштів, видалення, відправка листа). Для цього агент має зберігати розмову (RemembersConversations), щоб продовжити її після схвалення.
Опис - це інструкція для моделі: від description() і описів полів залежить, чи правильно модель обере інструмент. Пишіть, коли його використовувати і що він повертає.
Що повертати: короткий текст чи JSON лише з потрібними полями. Повертати модель цілком ($order->toJson()) - це зайві токени й ризик віддати моделі внутрішні дані (хеші, нотатки менеджерів), які потім потраплять у відповідь користувачу.
Реальні запити до LLM у тестах - повільні, платні, недетерміновані й потребують ключа API в CI. AI SDK має вбудовані підробки, як Mail::fake() чи Http::fake().
Підробка агента:
use App\Ai\Agents\SupportAssistant;
use Laravel\Ai\Prompts\AgentPrompt;
SupportAssistant::fake(); // фіксована відповідь на будь-який промпт
SupportAssistant::fake(['Перша відповідь', 'Друга відповідь']); // по черзі
SupportAssistant::fake(fn (AgentPrompt $prompt) => str_contains($prompt->prompt, 'повернення')
? 'Повернення можливе протягом 14 днів.'
: 'Не можу допомогти з цим питанням.');
Структурований вивід - масиви як відповіді:
ReviewClassifier::fake([
['sentiment' => 'negative', 'topics' => ['delivery'], 'needs_reply' => true, 'score' => 2],
]);
Твердження:
SupportAssistant::assertPrompted(fn (AgentPrompt $prompt) => str_contains($prompt->prompt, '№1042'));
SupportAssistant::assertNotPrompted('...');
SupportAssistant::assertNeverPrompted();
// якщо агента запускали через ->queue()
ReviewClassifier::assertQueued(fn (AgentPrompt $prompt) => ...);
Інші можливості SDK мають свої підробки: Embeddings::fake(), Image::fake(), Audio::fake(), Transcription::fake(), Files::fake(), Stores::fake().
Що варто тестувати:
| Що | Як |
|---|---|
| що код реагує на відповідь моделі (зберігає, відповідає, ескалює) | fake з різними відповідями, зокрема з неочікуваними |
| що в промпт потрапляють потрібні дані й не потрапляють зайві (персональні дані) | assertPrompted з перевіркою тексту |
| інструменти агента | окремо, як звичайні класи: виклик handle з різними аргументами й користувачами |
| обробка збоїв провайдера | fake із замиканням, що кидає виняток |
Що підробки не перевіряють: якість відповідей самої моделі. Для цього потрібні оцінювальні набори (evals) - окремий набір запитів з очікуваними властивостями відповіді, який запускається проти реальної моделі вручну чи за розкладом, а не в кожному прогоні тестів.
Захист від випадкових реальних запитів: у phpunit.xml задати порожні чи фіктивні ключі провайдерів - тест, який забув підробку, впаде на автентифікації, а не витратить гроші.