Перейти к содержимому
Экосистема AI Vibe

Разборы

RBAC для AI-агентов в Laravel: три слоя, которые LLM не обходит

Редакция 6 сентября 2026 г.

RBAC для AI-агентов в Laravel: три слоя, которые LLM не обходит

Промпт, проверка в PHP-хендлере, фильтр в SQL — три уровня, и только два нижних держат границу. Для соло-бэкенда с агентом в очереди это значит явно передавать пользователя в каждый вызов инструмента, а не полагаться на системный промпт — разбор в материале.

Почему агенты не попадают в ваш web middleware

AI-агенты не открывают браузер, не жмут кнопки и не проходят через стандартный HTTP-поток с сессией и middleware. Они ходят в API, вызывают function calling, крутятся в background queues и иногда в CLI.

Отсюда типичная утечка: инструмент get_open_tickets делает Ticket::where('status', 'open')->get() без scope — и агент видит все открытые тикеты, хотя в контроллере тот же запрос ограничен auth()->id(). Web-RBAC отработал бы на человека; агент получил god-mode, потому что путь другой.

Агентский runtime — Prism, Echo или свой agent loop — по сути внешний оркестратор без HTTP-сессии. Те же риски detached context, что у связки Cursor и MCP: инструменты есть, а «кто сейчас пользователь» в контексте не зафиксирован.

LLM — прокси, не субъект прав

В материале не вводится отдельная сущность «агент-пользователь» в RBAC. Модель — LLM как прокси человека (proxy for the user). Права наследуются от acting user — того, кто запустил агента.

Механизм передачи идентичности по цепочке:

  • DTO AgentContext с полем actingUser инжектится в каждый tool handler;
  • в queue job RunAgentLoop вызывается Auth::setUser($actingUser), чтобы Gates и Policies видели того же пользователя, что и веб-контроллер;
  • audit пишет causer_id человека плюс agent_session_id.

Worker под generic system user либо блокирует всё, либо обходит ограничения — отдельный service account для агента не предлагается.

// Упрощённая идея из статьи: acting user в контексте job
Auth::setUser($agentContext->actingUser);
Gate::authorize('close', $ticket);

Серверный периметр: Gates, scopes и динамический registry

Безопасность сидит в application-level и database-level, не в промпте.

Слой Что делает в статье
Gates / Policies Gate::authorize() до мутации, например close на тикете
Eloquent scopes accessibleBy, Global Scopes — фильтр на SQL, не «попроси модель отфильтровать»
AgentToolRegistry Список tools собирается под permission set пользователя
laravel-permission-manager ABAC, иерархии ролей, multi-tenancy, allow/deny overrides

Пример ABAC: Regional Manager может одобрить refund до $5 000 только в своём регионе; у Director — отдельное правило про conflict of interest на direct reports.

Отдельного Laravel middleware «для агентов» в посте не описано — акцент на application-level (Gates) и database-level (scopes). Spatie Permission упоминается как возможная основа для scope accessibleBy.

При AuthorizationException orchestration возвращает error string в LLM, и пользователь видит что-то вроде «обратитесь к старшему коллеге» — эскалация через агента, а не тихий отказ.

Почему prompt-level security не работает

Фраза «Only show John his own data» в system prompt — это доверие вероятностному текстовому генератору границу безопасности. Prompt-Level в архитектурной таблице помечен как «Never for security» — годится для тона и подсказок UI, не для доступа к данным.

Провальный сценарий: system prompt объявляет ассистента «read-only analytics», но в registry лежит update_customer_status. LLM всё равно вызовет mutation tool.

Жёсткий периметр по тексту:

  1. Gate::authorize() внутри tool;
  2. SQL scopes и metadata-фильтры в vector DB;
  3. write-tools не попадают в registry без can('update', …) / can('delete', …).

Для non-repudiation предлагается ActivityLog: causer_id (human), agent_id / agentSessionId, tool_name, parameters, llm_reasoning — с redact PII в параметрах.

В Horizon и multi-thread workers риск «user bleeding» при Auth::setUser: нужен reset auth или scoped context между job'ами.

Для Qdrant и Pinecone — синхрон metadata (например department_id) и фильтр при query. Иначе confidential docs попадают в context window, даже если SQL в другом tool был чистым.

По бюджету токенов: не отдавать LLM полный dataset «отфильтруй сам» — filter at SQL; кеш resolved permission tree на старт agent loop (ReAct multi-step).

Два подхода к registry: не сглаживать противоречие

В статье два тезиса из разных секций — оба валидны, но для разных целей.

Секция 3 — удобство отладки: не скрывай tool от LLM, пусть Gate блокирует вызов. Агент видит полный каталог, ошибка авторизации объяснима.

Секция 6 — жёсткий периметр и экономия токенов: не регистрируй write-tools без прав. Меньше шума в system prompt, точнее tool selection.

Для соло-разработчика с растущим числом agent tools разумная схема: mutation tools — только при явном can, read-tools — шире, но всё равно с Gate внутри handler'а. Определения инструментов здесь — аналог skills/tools в agent loop; граница безопасности в PHP, не в rules IDE.

Что вынести в свой стек

Архитектурная таблица сравнивает четыре слоя: Prompt-Level, Application-Level (Gates/Policies), Database-Level (Global Scopes / Postgres RLS), API Proxy/Gateway. Для Laravel monolith рекомендация — Application-Level + permission-manager; для multi-tenant SaaS с тяжёлыми vector queries — Database-Level / vector metadata filters.

Практический чеклист после чтения:

  • В каждом tool handler есть acting user из AgentContext, не system worker.
  • Мутации проходят Gate::authorize() до изменения данных.
  • Выборки ограничены scopes, не инструкцией модели.
  • Registry и audit согласованы: кто вызвал tool, с какими параметрами, от имени кого.

Конкретные версии Laravel и PHP в исходном посте не указаны; latency-бенчмарки permission-manager тоже не приведены — только качественные предупреждения.

Источники