363 lines
30 KiB
Markdown
363 lines
30 KiB
Markdown
# Роли агента (Agent Roles) — проектный план
|
|
|
|
> Статус: проработанная фича, **не реализована**. Контекст: gitmost — форк Docmost.
|
|
> Идея: дать возможность создавать переиспользуемые **роли агента** (например
|
|
> «Корректор» или «Факт-чекер, который ходит в веб и проверяет факты») и
|
|
> заводить чат, привязанный к выбранной роли. Роль задаёт поведение агента
|
|
> (системный промпт) и, опционально, модель.
|
|
>
|
|
> Зафиксированные решения по объёму (см. раздел «Развилки»):
|
|
> - **Владение** — только **админские, общие на воркспейс** роли (как провайдер и
|
|
> внешние MCP-серверы сегодня). Личных ролей в v1 нет.
|
|
> - **Гейтинг инструментов** — **нет**. Роль меняет только инструкции и (опц.) модель;
|
|
> набор инструментов всегда полный (тот же, что у обычного чата). Ограничение
|
|
> возможностей по ролям отложено (см. «Возможные расширения»).
|
|
> - **Артефакт этого шага** — только дизайн-документ; код не пишется.
|
|
|
|
## Зачем это (и почему ложится в текущую архитектуру)
|
|
|
|
Сегодня у встроенного AI-агента нет понятия персоны/роли на уровне чата: вся
|
|
настройка поведения — один системный промпт **на весь воркспейс**. Пользователь
|
|
хочет заводить разные чаты под разные задачи (вычитка орфографии, проверка фактов
|
|
по вебу и т. д.), каждый — со своей инструкцией и, возможно, своей моделью.
|
|
|
|
Три факта из текущего кода определяют дизайн (всё сверено по исходникам):
|
|
|
|
1. **Системный промпт — только на уровне воркспейса.** Собирается в
|
|
[ai-chat.prompt.ts](../apps/server/src/core/ai-chat/ai-chat.prompt.ts),
|
|
функция `buildSystemPrompt()`, по слоям: *базовая персона*
|
|
(`workspace.settings.ai.provider.systemPrompt` либо `DEFAULT_PROMPT`) →
|
|
*контекст* (имя воркспейса, открытая страница) → несъёмный `SAFETY_FRAMEWORK`.
|
|
Персоны на чат сейчас нет — её надо добавить как ещё один слой.
|
|
|
|
2. **Инструменты — всегда все включены.** В
|
|
[ai-chat.service.ts](../apps/server/src/core/ai-chat/ai-chat.service.ts):
|
|
`const tools = { ...external.tools, ...docmostTools }`. ~40 Docmost-инструментов
|
|
строит `AiChatToolsService.forUser()`
|
|
([tools/ai-chat-tools.service.ts](../apps/server/src/core/ai-chat/tools/ai-chat-tools.service.ts)),
|
|
внешние MCP-инструменты подмешивает `mcpClients.toolsFor(workspaceId)`
|
|
([external-mcp/mcp-clients.service.ts](../apps/server/src/core/ai-chat/external-mcp/mcp-clients.service.ts)).
|
|
Механизма включать подмножество инструментов нет — есть только CASL-проверка в
|
|
момент вызова (через персональный loopback-токен). **По зафиксированному
|
|
решению этот механизм мы и не вводим** — роль не трогает набор инструментов.
|
|
|
|
3. **Веб-доступ уже решён внешними MCP.** Внешние MCP-серверы
|
|
(`ai_mcp_servers`, напр. Tavily) с SSRF-защитой
|
|
([external-mcp/ssrf-guard.ts](../apps/server/src/core/ai-chat/external-mcp/ssrf-guard.ts))
|
|
и шифрованием заголовков — это и есть «факт-чекер ходит в гугл». Поскольку
|
|
гейтинга нет, веб-инструменты **уже доступны каждому чату**, если админ
|
|
подключил соответствующий MCP-сервер. Роль «Факт-чекер» работает чисто за счёт
|
|
инструкции «проверяй факты по веб-источникам и цитируй ссылки» — она направляет
|
|
модель пользоваться уже доступными инструментами, а не добавляет их.
|
|
|
|
4. **Чат создаётся неявно** при первом сообщении: клиент
|
|
([chat-thread.tsx](../apps/client/src/features/ai-chat/components/chat-thread.tsx))
|
|
шлёт POST `/api/ai-chat/stream` с `chatId: null`, сервер
|
|
([ai-chat.controller.ts](../apps/server/src/core/ai-chat/ai-chat.controller.ts))
|
|
создаёт строку `ai_chats`. Привязать чат к роли можно одним новым полем `role_id`,
|
|
которое клиент передаёт один раз при первом сообщении.
|
|
|
|
**Вывод:** роль — это тонкий слой поверх существующего пайплайна. Нужны:
|
|
новая таблица ролей + админский CRUD, поле `ai_chats.role_id`, новый слой в
|
|
`buildSystemPrompt()`, опциональный override модели в `getChatModel()`, пикер роли
|
|
и управление ролями в UI. Граница безопасности (CASL через loopback-токен)
|
|
**не меняется** — роль её не ослабляет и не усиливает (см. «Безопасность»).
|
|
|
|
## Модель
|
|
|
|
**Роль (Agent Role)** — именованный, общий на воркспейс пресет, который связывает:
|
|
|
|
| Часть | Что задаёт | Пример «Корректор» | Пример «Факт-чекер» |
|
|
| --- | --- | --- | --- |
|
|
| **instructions** | фрагмент системного промпта (персона/поведение) | «Исправляй только орфографию, пунктуацию и грамматику. Никогда не меняй смысл, факты, тон и структуру текста. Используй точечную правку текста» | «Проверяй фактические утверждения страницы по авторитетным веб-источникам. Цитируй ссылки. Помечай сомнительные места комментарием. Не редактируй текст страницы без явной просьбы» |
|
|
| **model (опц.)** | модель ≠ дефолт воркспейса | дешёвая модель | сильная модель |
|
|
| **presentation** | имя, emoji, описание | 🔤 «Корректор» | 🔎 «Факт-чекер» |
|
|
|
|
Чего роль в v1 **не** задаёт (по зафиксированным решениям): набор инструментов,
|
|
выбор конкретных внешних MCP-серверов, владельца (роли только общие/админские),
|
|
снапшот конфигурации на чат.
|
|
|
|
**Привязка чата к роли** — нулевое поле `ai_chats.role_id`. Чат «помнит», с какой
|
|
ролью создан; роль применяется на каждом ходу. Чат без роли (`role_id IS NULL`) —
|
|
обычный универсальный ассистент (текущее поведение).
|
|
|
|
## Модель данных (миграции)
|
|
|
|
Соглашение: `apps/server/src/database/migrations/YYYYMMDDThhmmss-description.ts`.
|
|
Только **добавляем** таблицы/столбцы (никогда не трогаем данные Docmost). Timestamp
|
|
новой миграции должен сортироваться **после** последней применённой; на момент
|
|
написания последняя — `20260618T160000-ai-stt-credentials.ts`, значит брать
|
|
`20260619T...`. После миграции — `pnpm --filter server migration:codegen` для
|
|
регенерации [db.d.ts](../apps/server/src/database/types/db.d.ts). Образец стиля —
|
|
[20260617T130000-ai-mcp-servers.ts](../apps/server/src/database/migrations/20260617T130000-ai-mcp-servers.ts).
|
|
|
|
**Миграция — таблица ролей + привязка чата:**
|
|
```sql
|
|
CREATE TABLE ai_agent_roles (
|
|
id uuid PRIMARY KEY DEFAULT gen_uuid_v7(),
|
|
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
creator_id uuid REFERENCES users(id) ON DELETE SET NULL, -- кто создал (аудит)
|
|
name varchar NOT NULL, -- "Корректор"
|
|
emoji varchar, -- presentation
|
|
description text,
|
|
instructions text NOT NULL, -- фрагмент system prompt
|
|
model_config jsonb, -- { driver?, chatModel } | NULL = дефолт воркспейса
|
|
enabled boolean NOT NULL DEFAULT true,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
deleted_at timestamptz -- soft delete (как у ai_chats)
|
|
);
|
|
CREATE INDEX idx_ai_agent_roles_workspace_id ON ai_agent_roles (workspace_id);
|
|
|
|
-- привязка чата к роли
|
|
ALTER TABLE ai_chats
|
|
ADD COLUMN role_id uuid REFERENCES ai_agent_roles(id) ON DELETE SET NULL;
|
|
```
|
|
|
|
Заметки:
|
|
- `creator_id ON DELETE SET NULL` — роль общая и переживает удаление автора
|
|
(в отличие от `ai_chats.creator_id`, который `NOT NULL`); это только аудит.
|
|
- `ai_chats.role_id ON DELETE SET NULL` — если роль жёстко удалят, чат
|
|
деградирует к универсальному поведению, а не ломается (см. edge-cases).
|
|
В сочетании с `deleted_at` основной путь удаления роли — **soft delete**:
|
|
старые чаты тогда продолжают видеть инструкции через JOIN с учётом `deleted_at`
|
|
(решение по поведению при удалении — в «Открытых вопросах»).
|
|
- `model_config jsonb` — `{ chatModel }` либо `{ driver, chatModel }`. Пусто/`NULL`
|
|
→ модель воркспейса. По образцу `publicShareChatModel` из
|
|
[public-share-assistant-plan.md](./public-share-assistant-plan.md): креды
|
|
(`apiKey`/`baseUrl`) берутся от провайдера соответствующего драйвера из
|
|
`ai_provider_credentials`, отдельные креды на роль не нужны.
|
|
|
|
Типы: добавить `AiAgentRoles` в `db.interface.ts` (или поднять через codegen),
|
|
`role_id` появится в `AiChats` автоматически после codegen.
|
|
|
|
## Бэкенд
|
|
|
|
### 1. Слой инструкций роли в системном промпте
|
|
|
|
В [ai-chat.prompt.ts](../apps/server/src/core/ai-chat/ai-chat.prompt.ts) добавить
|
|
вход `roleInstructions` в `buildSystemPrompt()`. Приоритет персоны:
|
|
```text
|
|
effectivePersona = roleInstructions?.trim() || adminPrompt?.trim() || DEFAULT_PROMPT
|
|
return `${effectivePersona}${context}\n${SAFETY_FRAMEWORK}`
|
|
```
|
|
Ключевое: **`SAFETY_FRAMEWORK` по-прежнему добавляется всегда и не отключается
|
|
ролью.** Роль задаёт только персону; контекст (воркспейс, открытая страница) и
|
|
safety-блок остаются как есть.
|
|
|
|
Решение «роль заменяет, а не дополняет admin-промпт» выбрано намеренно: для
|
|
узкой роли вроде «Корректора» нужно, чтобы её инструкция доминировала, а не
|
|
конкурировала с общим промптом воркспейса. (Альтернатива «конкатенировать
|
|
admin-промпт + роль» — в «Открытых вопросах».)
|
|
|
|
### 2. Применение роли в стриме
|
|
|
|
В [ai-chat.service.ts](../apps/server/src/core/ai-chat/ai-chat.service.ts) (метод
|
|
`stream()`), где сейчас резолвится `system` и `model`:
|
|
- Загрузить роль по `ai_chats.role_id` (если задан и не удалён).
|
|
- Передать `role.instructions` в `buildSystemPrompt({ ..., roleInstructions })`.
|
|
- Если у роли есть `model_config` — резолвить модель с override (см. п. 3).
|
|
- Набор инструментов **не меняется** (по решению).
|
|
|
|
Важно: `role_id` сервер берёт **из строки `ai_chats`, а не из тела запроса** на
|
|
каждом ходу — роль нельзя подменить пораздачно. Клиент сообщает `roleId` только
|
|
при создании чата (первое сообщение), сервер сохраняет его в `ai_chats.role_id`.
|
|
|
|
### 3. Override модели
|
|
|
|
`AiService.getChatModel(workspaceId)`
|
|
([integrations/ai/ai.service.ts](../apps/server/src/integrations/ai/ai.service.ts))
|
|
получает опциональный аргумент override модели (паттерн из
|
|
[public-share-assistant-plan.md](./public-share-assistant-plan.md) §5):
|
|
- `model_config.chatModel` — id модели вместо `chatModel` воркспейса;
|
|
- `model_config.driver` (опц.) — если указан другой драйвер, берём его креды из
|
|
`ai_provider_credentials`; если кредов нет → `AiNotConfiguredException` (503) с
|
|
**внятным сообщением** («для роли X выбран провайдер Y, но он не настроен»),
|
|
согласно конвенции об ошибках (никаких «Something went wrong»).
|
|
- Пусто → текущее поведение (модель воркспейса).
|
|
|
|
Резолв модели делать **до** hijack ответа, чтобы ненастроенный провайдер вернул
|
|
503, а не падал в середине стрима (как уже сделано в контроллере для воркспейс-модели).
|
|
|
|
### 4. CRUD ролей (админский модуль)
|
|
|
|
Новый модуль `core/ai-chat/roles/` рядом с `external-mcp/`:
|
|
`ai-agent-roles.controller.ts` + `ai-agent-roles.service.ts` + repo
|
|
(`database/repos/ai-agent-roles/`). Эндпоинты под `/api/ai-chat/roles` (или
|
|
`/api/ai-settings/roles` — рядом с MCP-серверами; выбрать единообразно с
|
|
существующим размещением, см. «Открытые вопросы»):
|
|
|
|
| Метод | Доступ | Назначение |
|
|
| --- | --- | --- |
|
|
| `list` | **любой участник воркспейса** | получить список ролей для пикера при создании чата |
|
|
| `create` / `update` / `delete` | **только админ** | управление ролями (как `ai-settings`) |
|
|
|
|
Нюанс CASL: создание/правка/удаление — под админской абилити (как
|
|
[ai-settings.controller.ts](../apps/server/src/core/.../ai-settings.controller.ts)
|
|
управляет провайдером и MCP-серверами), но **list должен быть доступен всем
|
|
участникам**, иначе обычный пользователь не сможет выбрать роль при заведении
|
|
чата. Все запросы строго скоупятся по `workspace_id` (мультитенант по хосту).
|
|
|
|
Валидация при create/update: непустые `name` и `instructions`; если задан
|
|
`model_config.driver` — он из числа поддерживаемых (`openai`/`gemini`/`ollama`).
|
|
|
|
## Клиент
|
|
|
|
### 1. Пикер роли при создании чата
|
|
|
|
В зоне «New chat» / композере
|
|
([ai-chat-window.tsx](../apps/client/src/features/ai-chat/components/ai-chat-window.tsx),
|
|
[chat-input.tsx](../apps/client/src/features/ai-chat/components/chat-input.tsx)) —
|
|
селектор роли (Mantine `Select`/`SegmentedControl`), дефолт «Универсальный
|
|
ассистент» (без роли). Выбранный `roleId` хранится в новом Jotai-атоме рядом с
|
|
[atoms/ai-chat-atom.ts](../apps/client/src/features/ai-chat/atoms/ai-chat-atom.ts)
|
|
и уходит в теле **первого** запроса на `/stream` (расширить
|
|
`prepareSendMessagesRequest` в `chat-thread.tsx`: добавить `roleId`). После того
|
|
как сервер создал чат с ролью, пикер для этого чата фиксируется (роль чата
|
|
неизменна; смена роли = новый чат — простое и предсказуемое поведение для v1).
|
|
|
|
### 2. Бейдж роли
|
|
|
|
Показывать emoji+имя роли в шапке окна чата и в строке списка
|
|
([conversation-list.tsx](../apps/client/src/features/ai-chat/components/conversation-list.tsx)),
|
|
чтобы было видно, «с кем» разговор. `role_id`/денормализованное имя+emoji роли
|
|
добавить в выдачу списка чатов и тип `IAiChat`
|
|
([types/ai-chat.types.ts](../apps/client/src/features/ai-chat/types/ai-chat.types.ts)).
|
|
|
|
### 3. Управление ролями в настройках
|
|
|
|
Новая секция «Роли агента» в Settings → AI
|
|
([pages/settings/workspace/ai-settings.tsx](../apps/client/src/pages/settings/workspace/ai-settings.tsx)),
|
|
рядом с «External tools». Переиспользовать паттерн add/edit/delete-модалки из
|
|
[ai-mcp-servers.tsx](../apps/client/src/features/workspace/components/settings/components/ai-mcp-servers.tsx).
|
|
Форма роли: имя, emoji, описание, **instructions** (textarea — как редактор
|
|
системного сообщения в
|
|
[ai-provider-settings.tsx](../apps/client/src/features/workspace/components/settings/components/ai-provider-settings.tsx)),
|
|
опциональный override модели. Подпись-напоминание под полем instructions:
|
|
«встроенный safety-фреймворк добавляется автоматически» (как у системного сообщения).
|
|
|
|
### 4. Слой запросов
|
|
|
|
Новые TanStack Query хуки в
|
|
[queries/ai-chat-query.ts](../apps/client/src/features/ai-chat/queries/ai-chat-query.ts)
|
|
(или отдельный файл): `useAiRolesQuery()` (list), `useCreate/Update/DeleteAiRoleMutation()`
|
|
+ функции в
|
|
[services/ai-chat-service.ts](../apps/client/src/features/ai-chat/services/ai-chat-service.ts).
|
|
Тип `IAiRole` зеркалит серверную схему.
|
|
|
|
## Поток одного хода (с ролью)
|
|
|
|
1. Создание чата: клиент шлёт первое сообщение + `roleId` → `/ai-chat/stream`;
|
|
сервер создаёт `ai_chats` с `role_id`.
|
|
2. Последующие ходы: сервер читает `role_id` из строки чата (не из тела запроса).
|
|
3. Резолв: загрузить роль (если не удалена) → `instructions` + `model_config`.
|
|
4. `buildSystemPrompt({ workspace, adminPrompt, roleInstructions, openedPage })`
|
|
→ персона роли + контекст + несъёмный `SAFETY_FRAMEWORK`.
|
|
5. `getChatModel(workspaceId, role.model_config)` → модель роли или дефолт.
|
|
6. `streamText({ model, system, messages, tools, stopWhen: stepCountIs(8) })` —
|
|
**набор инструментов полный, как у обычного чата**.
|
|
|
|
## Edge-cases (главное)
|
|
|
|
- **Роль удалена/выключена, а чаты на неё ссылаются.** При hard-delete
|
|
`ON DELETE SET NULL` обнуляет `ai_chats.role_id` → чат продолжает работать как
|
|
универсальный. Основной путь — soft-delete (`deleted_at`)/`enabled=false`:
|
|
тогда роль исчезает из пикера, но старые чаты могут продолжать применять её
|
|
инструкции (резолв учитывает `deleted_at` — точное поведение в «Открытых
|
|
вопросах»).
|
|
- **Роль отредактировали после создания чатов.** В v1 без снапшота правка
|
|
применяется «вживую» — старые чаты подхватывают новые инструкции на следующем
|
|
ходу. Приемлемо для кейсов «Корректор/Факт-чекер»; снапшот конфигурации на чат —
|
|
возможное расширение.
|
|
- **Safety не переопределяется.** `SAFETY_FRAMEWORK` добавляется всегда, что бы
|
|
ни написали в `instructions` роли (включая попытку «игнорируй прежние инструкции»).
|
|
- **Override модели на ненастроенный провайдер** → 503 с конкретным сообщением,
|
|
а не молчаливый фолбэк (конвенция об ошибках). Решить, делать ли мягкий фолбэк
|
|
на модель воркспейса (в «Открытых вопросах»).
|
|
- **Пустые `instructions`** недопустимы при создании (валидация); но если роль
|
|
как-то оказалась с пустыми инструкциями — персона падает на admin-промпт/дефолт.
|
|
- **Заголовок чата** генерируется фоново (`generateText`) — оставить на модели
|
|
воркспейса, чтобы экзотический override роли не ломал автозаголовок (мелочь).
|
|
- **Мультитенант.** Все операции с ролями скоупятся по `workspace_id`; роль из
|
|
чужого воркспейса не видна и не применима.
|
|
- **MCP-зеркало схемы** ([packages/mcp](../packages/mcp)) фичу не затрагивает —
|
|
роли живут только во встроенном AI-чате, не в standalone MCP.
|
|
|
|
## Безопасность
|
|
|
|
- **Граница безопасности не меняется.** Агент по-прежнему ходит в API через
|
|
персональный loopback-JWT (`AiChatToolsService.forUser`), и CASL ограничивает
|
|
его ровно правами текущего пользователя. Роль — это слой *формирования промпта
|
|
и выбора модели*, он не выдаёт и не отнимает прав.
|
|
- **Следствие решения «без гейтинга» (осознанный компромисс):**
|
|
- Роль «Корректор» инструкцией просят не менять смысл, но технически у чата
|
|
остаются все write-инструменты — модель *могла бы* отредактировать/удалить
|
|
(под soft-delete и CASL, т. е. обратимо и в пределах прав пользователя). Это
|
|
мягкая граница (промпт), а не жёсткая.
|
|
- Роль «Факт-чекер» полагается на то, что админ глобально подключил веб-MCP
|
|
(Tavily); тогда веб-инструменты доступны *всем* чатам, а не только этой роли.
|
|
Жёсткие границы возможностей по ролям — отдельная будущая фаза (см. ниже).
|
|
- **Instructions — доверенный контент:** их пишет админ воркспейса, они попадают
|
|
только в системный промпт чатов этого воркспейса и исполняются под правами
|
|
конкретного пользователя. Эскалации нет.
|
|
- **Внешние MCP** остаются под SSRF-guard; роли логику подключения MCP не трогают.
|
|
|
|
## Явные non-goals (v1)
|
|
|
|
- Нет гейтинга/ограничения инструментов по ролям (роль не сужает тулсет).
|
|
- Нет личных ролей (только общие админские).
|
|
- Нет выбора конкретных внешних MCP-серверов на роль (все включённые доступны всем).
|
|
- Нет снапшота конфигурации роли на чат (правка роли применяется вживую).
|
|
- Нет per-role параметров генерации сверх модели (temperature и т. п.).
|
|
- Нет композиции «скиллов» поверх роли (см. «Связь со „скиллами“»).
|
|
|
|
## Связь со «скиллами»
|
|
|
|
В терминах Anthropic Skills (подгружаемый по требованию пакет инструкций +
|
|
ресурсов/скриптов) текущая роль = MVP-«скилл»: только текстовая инструкция + выбор
|
|
модели. Естественная эволюция — сделать «скиллы» композируемыми (несколько скиллов
|
|
на одну роль), привязывать к роли эталонные страницы/файлы как контекст, и —
|
|
главное — добавить **жёсткий гейтинг инструментов** (тогда «Корректор» физически не
|
|
сможет удалять, а «Факт-чекер» получит веб ровно тогда, когда роль это разрешает).
|
|
Всё это — следующие итерации, вне scope v1.
|
|
|
|
## Развилки (зафиксированные решения)
|
|
|
|
| Развилка | Решение | Альтернативы (отклонены / отложены) |
|
|
| --- | --- | --- |
|
|
| Владение ролями | **Только админские, общие на воркспейс** | личные роли; личные + общие |
|
|
| Ограничение инструментов | **Нет (только промпт + модель)** | крупные группы возможностей; тонкий per-tool allowlist |
|
|
| Выбор MCP-серверов на роль | **Нет (все включённые доступны всем)** | мультиселект MCP-серверов на роль |
|
|
| Привязка чата к роли | **Поле `ai_chats.role_id`, неизменно после создания** | смена роли внутри чата; роль в теле каждого запроса |
|
|
| Персона роли vs admin-промпт | **Роль заменяет персону** (safety всегда добавляется) | конкатенация admin-промпт + роль |
|
|
| Снапшот конфигурации | **Нет (правка вживую)** | снапшот конфигурации роли на чат |
|
|
|
|
## Открытые вопросы (не блокируют дизайн)
|
|
|
|
1. **Размещение CRUD-эндпоинтов и UI:** `/ai-chat/roles` (рядом с чатом) или
|
|
`/ai-settings/roles` (рядом с MCP-серверами). Предлагаю в одном месте с MCP —
|
|
там уже живут админские AI-настройки.
|
|
2. **Поведение при удалении роли:** soft-delete с сохранением инструкций для старых
|
|
чатов vs hard-delete + `SET NULL` (старые чаты деградируют к универсальным).
|
|
Предлагаю soft-delete (`deleted_at`) — консистентно с `ai_chats`.
|
|
3. **Override модели на ненастроенный драйвер:** жёсткий 503 с внятным сообщением
|
|
vs мягкий фолбэк на модель воркспейса. Предлагаю 503 (явность важнее).
|
|
4. **Стартовые пресеты:** поставлять ли «Корректор» и «Факт-чекер» как
|
|
преднастроенные роли-шаблоны (seed) при включении фичи, чтобы админ не писал
|
|
инструкции с нуля. Предлагаю — да, как необязательный «вставить пример».
|
|
5. **Денормализация для бейджа:** хранить имя/emoji роли только в `ai_agent_roles`
|
|
и джойнить, либо денормализовать на `ai_chats` для дешёвого списка. Предлагаю
|
|
джойн (простота; список чатов не горячий путь).
|
|
|
|
## Объём работ
|
|
|
|
Бэкенд: 1 миграция (`ai_agent_roles` + `ai_chats.role_id`) + codegen типов;
|
|
новый CRUD-модуль ролей (controller/service/repo) под CASL; правка
|
|
`buildSystemPrompt()` (слой `roleInstructions`); правка `AiChatService.stream()`
|
|
(загрузка роли, передача инструкций и override модели); опциональный override
|
|
модели в `AiService.getChatModel()`. Клиент: пикер роли при создании чата + атом +
|
|
проброс `roleId` в первый запрос; бейдж роли в шапке и списке; секция управления
|
|
ролями в Settings → AI (модалка add/edit/delete по образцу MCP-серверов); хуки
|
|
запросов/мутаций. **Без изменений в `packages/mcp`. Набор инструментов агента не
|
|
трогаем.**
|