Интеграция
Модуль frame/ai встраивает AI-помощника в интерфейс Frame. Пользователь взаимодействует через popover-чат на страницах ресурсов.
Возможности
- Настройка фильтров, сортировок и поиска в каталоге текстом
- Заполнение полей формы при создании и редактировании записей
- Автоматический поиск и привязка связанных записей через tool calling
- Защита персональных данных (PII) и чувствительных полей
- Кастомизация промптов через Blade-шаблоны
Установка
1. Composer
composer require frame/ai
Для локальной разработки:
{
"repositories": [
{
"type": "path",
"url": "./packages/ai",
"options": { "symlink": true }
}
],
"require": {
"frame/ai": "*"
}
}
2. Frontend
В файле инициализации подключите модуль до FramePlugin:
import { registerAiModule } from '@frame/ai'
registerAiModule()
app.use(FramePlugin, { ... })
3. Конфигурация
php artisan vendor:publish --tag=ai-config
4. Переменные окружения
# Провайдер (любой из config/frame-ai.php Laravel AI SDK)
AI_PROVIDER=openrouter
# Модель (зависит от провайдера)
AI_MODEL=stepfun/step-3.5-flash:free
Ключи API настраиваются через Laravel AI SDK. Например, для OpenRouter:
OPENROUTER_API_KEY=sk-or-...
Конфигурация
return [
// Провайдер Laravel AI SDK
'provider' => env('AI_PROVIDER', 'openrouter'),
// Модель для запросов
'model' => env('AI_MODEL', 'stepfun/step-3.5-flash:free'),
// Типы полей, всегда исключаемые из AI
'sensitive_types' => ['password'],
// Имена полей, всегда исключаемые из AI
'sensitive_fields' => [
'password', 'remember_token', 'api_token',
'api_key', 'secret', 'token', 'ssn',
'credit_card', 'card_number', 'cvv',
],
// Типы с PII (исключены по умолчанию, можно разрешить)
'pii_types' => ['email', 'phone'],
// Имена с PII (исключены по умолчанию, можно разрешить)
'pii_fields' => [
'email', 'phone', 'address', 'ip_address',
'date_of_birth', 'passport', 'inn',
],
];
Каталог (Table Assistant)
AI анализирует схему таблицы ресурса и преобразует текстовый запрос в фильтры, сортировки и поисковый запрос.
Примеры запросов:
- «записи с заполненной датой рождения» → фильтр
birthday is not null - «взрослые, отсортировать по имени» → фильтр
audience in ["Adult"]+ сортировкаname asc - «найди Иванова» → поиск «Иванов»
Операторы фильтрации берутся из FilterStrategy каждого поля — AI видит только реально доступные операции для конкретного типа поля.
Кнопка Сбросить в popover очищает все фильтры, сортировки и поиск.
Создание и редактирование (Form Assistant)
AI заполняет поля формы через setValues() — пользователь видит результат и сам решает когда сохранить.
Обычные поля
AI генерирует значения на основе описания:
Запрос: «Создай пользователя Иван Иванов, 1990 г.р., Junior»
Результат: { name: "Иван", surname: "Иванов", birthday: "1990-01-01", audience: "Junior" }
Relation поля
Для relation полей (BelongsTo, HasOne, MorphOne) AI использует tool calling:
- AI видит поле
addressс инструкцией «используйsearch_addressдля поиска» - Вызывает tool с поисковым запросом
- Tool ищет через SearchDriver ресурса (Meilisearch, TNTSearch и т.д.)
- AI получает результаты и подставляет ID найденной записи
- Backend гидрирует ID в полный объект для combobox
Режим редактирования
В режиме редактирования AI меняет только те поля, которые явно указаны в запросе. Остальные поля не затрагиваются.
Запрос: «Поменяй название на Офис Кьюбика»
Результат: { title: "Офис Кьюбика" } ← только title, адрес не тронут
Безопасность
Модуль автоматически исключает чувствительные данные из контекста AI на двух уровнях.
Всегда исключены
Нельзя переопределить. Эти поля никогда не попадают в промпт:
- Типы:
password - Имена:
password,remember_token,api_token,api_key,secret,token,ssn,credit_card,cvv
PII — исключены по умолчанию
Можно разрешить для конкретного ресурса:
- Типы:
email,phone - Имена:
email,phone,address,ip_address,date_of_birth,passport,inn
public static function configureAi(ResourceAgent $agent): void
{
$agent
// Разрешить конкретные PII поля
->allowFields(['email'])
// Или разрешить все PII
->withPii()
// Дополнительно скрыть поля
->sensitive(['salary', 'internal_notes']);
}
Промпты
Промпты — Blade-шаблоны, которые можно переопределить:
php artisan vendor:publish --tag=ai-views
Шаблоны публикуются в resources/views/vendor/frame-ai/prompts/:
| Шаблон | Назначение |
|---|---|
table-assistant.blade.php | Промпт для каталога (фильтры, сортировки, поиск) |
form-assistant.blade.php | Промпт для заполнения формы (create/edit) |
Шаблоны получают реальные данные ресурса: список полей, типы, операторы, описания из configureAi.
UI
Кнопка AI автоматически появляется на страницах ресурсов через App Slots:
| Страница | Слот |
|---|---|
| Каталог | resource-table-actions |
| Просмотр | resource-page-actions |
| Редактирование | resource-edit-actions |
| Создание | resource-create-actions |
Кнопка показывает gradient-иконку (аналоговая гармоника от primary цвета). В тёмной теме — gradient fill фон с белым текстом. При загрузке — spinner.
Popover открывается по клику, не блокирует работу со страницей.
API-эндпоинты
Все эндпоинты доступны под префиксом Frame API (по умолчанию /api/frame):
| Метод | Путь | Описание |
|---|---|---|
| POST | /ai/table-assistant | Генерация фильтров/сортировок из текста |
| POST | /ai/form-assistant | Генерация значений полей формы |
Эндпоинты работают под стандартной авторизацией Frame.
Ограничения
- Бесплатные модели могут быть нестабильны (rate limits, таймауты). Для продакшена рекомендуется платная модель
- Tool calling зависит от поддержки модели — не все бесплатные модели умеют вызывать tools
- Генерация изображений пока не поддерживается (планируется)
- Автоматическое сохранение — AI не сохраняет данные, только заполняет формы. Пользователь всегда принимает финальное решение
- Качество ответов зависит от модели и качества описаний в
configureAi
Переводы
Добавьте переводы в JSON-файл локали, передаваемый в FramePlugin:
{
"AI Assistant": "AI Ассистент",
"How can I help?": "Чем могу помочь?",
"Describe what data you want to see": "Опишите, какие данные хотите увидеть",
"Records for the last 7 days": "Записи за последние 7 дней",
"Fill in user John Doe, born 1990": "Заполни пользователя Иван Иванов, 1990 г.р.",
"Something went wrong. Try again.": "Что-то пошло не так. Попробуйте ещё раз.",
"Done": "Готово",
"Could not fill the form": "Не удалось заполнить форму",
"New chat": "Новый чат",
"Reset": "Сбросить",
"Filters and sorting have been reset": "Фильтры, сортировки и поиск сброшены",
"Thinking...": "Думаю..."
}