docs: remove implemented ai-agent-roles plan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,362 +0,0 @@
|
|||||||
# Роли агента (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`. Набор инструментов агента не
|
|
||||||
трогаем.**
|
|
||||||
Reference in New Issue
Block a user