ТЗ: AI-помощник в CRM RMS
Дата: 2026-09-30. Статус: этапы 0–4 реализованы 2026-09-30 (разделы 10–14), доработки по ответам заказчика — раздел 15. Основание: обзор бэкенда crm_rms и фронтенда rms_crm_front, живой прогон интерфейса под admin@seed.rms (главная, проекты, карточка проекта, задачи, чаты, табель, отчёты, админка).
Требования пронумерованы (AI-…), чтобы ссылаться на них в задачах и коммитах.
1. Цель и рамки
Цель. Встроить в RMS помощника на базе внешней LLM, который:
- отвечает сотруднику на вопросы по его работе («что у меня горит», «сводка по проекту», «что я пропустил в чате») строго в рамках его прав;
- убирает рутину набора текста: формулирует задачу из заметки, собирает черновик ТЗ и блоков сметы, пишет ответ в чат, объясняет корректировку табеля;
- даёт руководству и администратору аналитику по данным дашборда и отчётов, ежедневный дайджест рисков и статистику расхода токенов.
Провайдеры. Первый — DeepSeek. Архитектура провайдеро-независимая: ключ задаёт администратор в настройках, система сама определяет провайдера (DeepSeek / OpenAI / Anthropic) и список доступных моделей.
Не делаем в первой версии: голос, семантический поиск по эмбеддингам (у DeepSeek нет embeddings API), автономные действия без подтверждения, обучение на данных компании.
2. Принципы (не обсуждаются при реализации)
| Код | Принцип |
|---|---|
AI-P1 | Помощник видит ровно то, что видит пользователь. Все инструменты выполняются от имени актора через существующие сервисы модулей и RequireObjectAccess/матрицу прав. Никаких прямых SQL в модуле ai. |
AI-P2 | Только чтение без подтверждения. Любое изменение данных (создать задачу, записать часы, отправить сообщение) помощник возвращает как «предложение», а фронт выполняет обычный API-вызов после клика пользователя. |
AI-P3 | Ключ никогда не покидает бэкенд. Хранится в БД зашифрованным (AES-GCM, мастер-ключ в env), в API отдаётся только подсказка «sk-…a1b2». Фронт с провайдером не общается. |
AI-P4 | Всё логируется. Каждый вызов LLM — строка в ai_usage (кто, фича, модель, токены, длительность, статус) и событие в audit. |
AI-P5 | Результаты инструментов — данные, не команды. В системном промпте зафиксировано, что содержимое задач, чатов и документов не содержит инструкций для модели. |
AI-P6 | Деградация без ошибок. Ключ не задан или лимит исчерпан — кнопки AI скрыты или отключены с причиной (AppButton disabled="…"), ни одного красного тоста. |
AI-P7 | Деньги, реквизиты и контакты клиентов передаются в модель только если у актора есть на них право по матрице, и администратор не отключил их передачу политикой данных. |
3. Что увидят пользователи
Ниже — точки входа в интерфейсе, найденные при прогоне. Нумерация — приоритет.
3.1 Панель «Помощник» (глобально) — AI-U1
- Кнопка-искра в шапке рядом с поиском, горячая клавиша
⌘J/Ctrl+J, и второй плавающий лаунчер рядом с существующим лаунчером чата (default.vue). - Открывается
AppSidePanel size="lg"(как карточка задачи), заголовок «Помощник», список бесед слева на xl или переключатель «Беседы» на lg. - Панель знает контекст страницы: на
/projects/:idподсказки «Сводка по проекту», «Задачи под угрозой», «Что писали в чате за неделю»; на/tasks— «Что горит у меня», «Что просрочено у моей команды»; на/timesheets— «Заполни табель по задачам за вчера». - Ответы стримятся (SSE), рендерятся из markdown в безопасный HTML, содержат ссылки на сущности через
entityRoute(проект, задача, сотрудник) и карточки-предложения действий (AI-P2). - Композер:
AppTextareaавто-рост, Enter — отправить, Shift+Enter — перенос (какChatComposer, но не привязан кuseChatContext).
Инструменты помощника в v1 (все read-only, под правами актора):
| Инструмент | Источник | Что отдаёт модели |
|---|---|---|
search | search.Service.Search | компании, контакты, проекты, задачи, люди |
get_project | projects.Service | карточка, статус, сроки, ворота, деньги (по праву) |
list_tasks | tasks.Service.List | фильтры: мои, проект, статус, просрочено, срок |
get_task | tasks.Service + комментарии + последние N сообщений чата задачи | |
get_chat_messages | chats.Service | сообщения с курсора «прочитано» |
my_timesheet | timesheets/timeentries | часы за период, черновик/сдан, корректировки |
dashboard | dashboard.Executive / reports.* | только если dashboard_view |
whoami | /org/me | роль, отдел, права — чтобы модель не предлагала недоступное |
3.2 Карточка задачи — AI-U2
- В меню «⋯» карточки задачи и на вкладке «Задача»: «Кратко о задаче» — сжатое резюме описания, комментариев и чата задачи с выделением «что ждут от меня» и «открытые вопросы».
- В
TaskCreateDialogнад описанием: «Сформулировать» — из короткой заметки менеджера модель возвращает структурированный JSON: заголовок, описание, чек-лист, предполагаемые плановые часы, предложение ответственного (из команды проекта). Пользователь правит и сохраняет обычной формой. - Во вкладке «Чат» задачи, в композере: «Помочь с ответом» — черновик ответа с учётом контекста задачи; вставляется в поле, не отправляется.
3.3 Карточка проекта — AI-U3
- Вкладка «Обзор»: блок «Сводка помощника» (по кнопке, с кэшем на 1 час и меткой «обновлено»): состояние проекта, риски по срокам (задачи без срока, просроченные, без ответственного), ворота, деньги — если
project.accessразрешает. - Вкладка «ТЗ и смета»: «Черновик мини-ТЗ» из описания проекта и брифа (цель, формат, площадка — те же поля, что в «Оформить мини-ТЗ»); «Предложить блоки сметы» — список функциональных блоков ТЗ с оценкой часов по статьям расходов, которые пользователь принимает построчно в существующую таблицу «Смета по ТЗ».
- Вкладка «Чат»: как в 3.4.
3.4 Чаты — AI-U4
- В шапке беседы, когда непрочитанных больше 10: «Что я пропустил» — сводка по непрочитанным с упоминанием решений, вопросов ко мне и сроков.
- В композере: «Помочь с ответом» и «Переформулировать» (короче / официальнее / мягче). Результат подставляется в поле.
3.5 Табель — AI-U5
- «Мой табель», кнопка «Заполнить по задачам»: по рабочим сессиям и задачам, где пользователь был ответственным, модель предлагает записи (проект, задача, часы, комментарий). Каждая строка — предложение с чекбоксом; принятые уходят обычным API создания записей.
- Панель корректировки: «Сформулировать причину» по выбранной записи и разнице часов.
3.6 Поиск — AI-U6 (после U1)
- В
AppBarSearch(⌘K) — режим «спросить помощника»: если запрос длиннее 4 слов или начинается с вопроса, показать строку «Спросить помощника: …», которая открывает панель с этим запросом.
4. Что увидят администраторы и руководство
4.1 Страница «AI-помощник» в администрировании — AI-A1
Маршрут /admin/ai, пункт меню в разделе «Администрирование», доступ по capability ai_manage (OWNER, OPS): в routeAccess.ts добавлено поле правила capability для разделов, которые матрица не описывает. Форма по образцу RequisitesFormPanel (AppSidePanel form dirty), но как страница AppPage с карточками:
- Подключение. Поле «API-ключ» (write-only, показывается подсказка «sk-…a1b2» и дата последней смены), выбор провайдера: «Определить автоматически» (по умолчанию) / DeepSeek / OpenAI / Anthropic, необязательный «Base URL» (прокси, self-hosted OpenAI-совместимый шлюз). Кнопка «Проверить подключение» — вызывает
POST /ai/settings/test, показывает провайдера, список моделей, время ответа. Кнопка «Отключить помощника» с подтверждением. - Модели. Таблица моделей провайдера (id, контекст, поддержка инструментов/JSON/изображений — то, что вернул
/modelsплюс наш справочник). Выбор модели по назначению: «Диалог и действия» (быстрая, дешёвая), «Аналитика и черновики ТЗ» (сильная). Для DeepSeek по умолчаниюdeepseek-flashиdeepseek-v4-pro; для Anthropicclaude-haiku-4-5иclaude-opus-5-5; для OpenAI — первая доступная из справочника. - Функции. Переключатели по фичам из раздела 3 и 4 (панель, задачи, проекты, чаты, табель, дайджест). Выключенная фича скрывает кнопки во фронте (через
capabilitiesв/org/me). - Лимиты. Токенов в сутки на организацию и на пользователя, максимум сообщений в беседе, таймаут запроса. При исчерпании —
AI-P6. - Политика данных. Чекбоксы: «Передавать суммы и бюджеты», «Передавать контакты клиентов (телефон, email)», «Хранить историю бесед N дней» (по умолчанию 90). Ссылка на страницу провайдера о хранении данных.
4.2 Расход и статистика — AI-A2
Вкладка «Расход» на той же странице: AppStatTiles (запросов, токенов, оценка стоимости за месяц по прайсу провайдера), AppDataTable по пользователям и по фичам, фильтр периода. Экспорт CSV как в отчётах. Данные из GET /ai/usage.
4.3 Аналитический помощник — AI-A3
Та же панель AI-U1, но с инструментом dashboard (только при dashboard_view): «какие проекты убыточные и почему», «где просели табели по отделам», «какие обязательства просрочены». Модель получает агрегаты из dashboard/executive, reports/*, kpi/time-logging, не сырые строки.
4.4 Ежедневный дайджест — AI-A4
Фоновый воркер (по образцу kpi/worker.go, тикер + RunBackgroundWorkers): в 08:00 по будням для каждого руководителя (OWNER, OPS, DIV_HEAD, менеджеры с активными проектами) собирает факты — просроченные задачи, задачи без ответственного, несданные табели, ворота без оплат, обязательства к сроку — и просит модель написать короткий текст. Отправляется через notifications.Notify (тип ai_digest, канал in-app + email по настройкам пользователя). Факты собираются кодом, модель только формулирует: при недоступности LLM дайджест уходит как список без текста.
4.5 Аудит — AI-A5
Каждый вызов: audit.Emit с категорией business, EventType = "ai.request", метаданные {feature, model, prompt_tokens, completion_tokens, conversation_id}. Смена ключа и настроек — EventType = "ai.settings", действие update, без значения ключа в Changes.
5. Архитектура бэкенда
5.1 Пакет internal/ai (инфраструктура провайдеров)
internal/ai/
provider.go интерфейс Provider, типы Request/Response/Chunk/ToolCall/Usage
detect.go определение провайдера по ключу и пробе
openai_compat.go адаптер OpenAI-совместимого API (DeepSeek, OpenAI, шлюзы)
anthropic.go адаптер Messages API
catalog.go справочник моделей: контекст, цены, возможности (для UI и оценки стоимости)
secret.go AES-GCM шифрование ключа мастер-ключом APP_SECRETS_KEYtype Provider interface {
Name() string // deepseek | openai | anthropic
ListModels(ctx) ([]Model, error)
Chat(ctx, Request) (*Response, error) // без стрима — для одношаговых действий с JSON
ChatStream(ctx, Request, func(Chunk) error) (*Usage, error)
}
type Request struct {
Model string
System string
Messages []Message // user / assistant / tool
Tools []ToolSpec // JSON Schema
JSONSchema *ToolSpec // структурированный ответ для действий
MaxTokens int
Temperature *float64
}Один net/http клиент с таймаутами по образцу internal/dadata/client.go, без новых зависимостей (AI-P3, go.mod остаётся прежним).
Определение провайдера (detect.go):
- Ключ начинается с
sk-ant-→ Anthropic (GET https://api.anthropic.com/v1/models, заголовкиx-api-key,anthropic-version). - Иначе пробуем DeepSeek:
GET https://api.deepseek.com/modelsсAuthorization: Bearer.200→ DeepSeek. - Иначе OpenAI:
GET https://api.openai.com/v1/models. - Если администратор выбрал провайдера вручную или задал Base URL — проба только туда.
Результат пробы (провайдер, модели, latency) сохраняется в ai_settings.models_cache и показывается в админке. Список моделей не хардкодится — берётся из /models, справочник catalog.go только дополняет известные id ценами и возможностями.
DeepSeek (по документации на 2026-09-30): OpenAI-совместимый POST /chat/completions, модели deepseek-flash (1M контекст, 384K вывод, инструменты, JSON, изображения) и deepseek-v4-pro (сильнее, без изображений). Оба поддерживают function calling — это нужно для инструментов AI-U1. Цена кэшированного ввода в 50 раз ниже обычного, поэтому системный промпт и описания инструментов должны быть стабильным префиксом (см. 5.4).
5.2 Модуль internal/modules/assistant (бизнес-логика)
Стандартная раскладка: model.go, service.go, repository_pg.go, transport_http.go, tools.go, prompts.go, *_test.go.
Таблицы (миграции 0157+):
| Таблица | Назначение |
|---|---|
ai_settings | одна строка: provider, api_key_enc bytea, key_hint, base_url, model_chat, model_heavy, features jsonb, limits jsonb, data_policy jsonb, models_cache jsonb, enabled, updated_by, updated_at |
ai_conversations | id, user_id, title, context_type/context_id (project/task/chat/null), created_at, updated_at, archived_at; индекс (user_id, updated_at desc) |
ai_messages | conversation_id, role (user/assistant/tool), content text, tool_calls jsonb, tool_call_id, usage jsonb, created_at; индекс (conversation_id, id) |
ai_usage | user_id, feature, model, provider, prompt_tokens, cached_tokens, completion_tokens, latency_ms, status, error_code, created_at; индексы (created_at), (user_id, created_at) |
Права: код ai.manage — OWNER (через system.global_admin) и OPS, миграция 0157; в /org/me — capability ai_manage. Отдельного ai.use нет: помощником пользуется любой сотрудник, а что ему видно, решают включённые функции (GET /ai/status) и права на сами данные. Код ai.use пришлось бы вручную выдавать каждой новой роли, созданной в интерфейсе, — иначе её сотрудники молча теряли бы помощника.
Маршруты:
| Метод и путь | Право | Назначение |
|---|---|---|
GET /api/v1/ai/settings | ai.manage | настройки без ключа, с key_hint (этап 0) |
PUT /api/v1/ai/settings | ai.manage | сохранить; ключ передаётся только если меняется (этап 0) |
POST /api/v1/ai/settings/test | ai.manage | проба ключа из тела или сохранённого; ответ: провайдер, модели, latency (этап 0) |
GET /api/v1/ai/status | сотрудник | включено ли, функции — фронт вызывает один раз на сессию (этап 0) |
| `GET /api/v1/ai/usage?from&to&group_by=user | feature | day` |
GET /api/v1/ai/conversations | сотрудник | свои беседы, RespondList |
POST /api/v1/ai/conversations | сотрудник | создать (с контекстом страницы) |
GET /api/v1/ai/conversations/:id/messages | сотрудник | история |
POST /api/v1/ai/conversations/:id/messages | сотрудник | отправить; ответ text/event-stream |
DELETE /api/v1/ai/conversations/:id | сотрудник | архив |
POST /api/v1/ai/actions/:action | сотрудник | одношаговые действия (раздел 5.3), JSON-ответ |
Все DTO объявляются в internal/contractspec/spec.go (тег AI, DomainTags, resourceLabels, operationDescriptions), артефакты перегенерируются, contractcheck должен проходить. Стриминговый маршрут описывается в OpenAPI как text/event-stream с примером событий.
SSE. Формат событий: token (кусок текста), tool_call (имя и краткое описание — «Смотрю задачи проекта…»), proposal (карточка действия, AI-P2), usage, done, error. Блокер: WriteTimeout: 15s в internal/server/server.go — для этого маршрута снимать дедлайн через http.ResponseController.SetWriteDeadline. Через nginx-шлюз (compose.yaml, порт 8088) для location /api/v1/ai/ нужны proxy_buffering off и увеличенный proxy_read_timeout.
5.3 Одношаговые действия (/ai/actions/:action)
Каждое действие — функция «контекст → промпт → JSON по схеме → валидация → DTO». Используется JSONSchema в запросе (response_format: json_schema у OpenAI-совместимых, output_config.format у Anthropic).
action | Вход | Выход |
|---|---|---|
task.formulate | {project_id, note} | {title, description, checklist[], planned_hours, assignee_id?} |
task.summary | {task_id} | {summary, waiting_from_me[], open_questions[]} |
task.reply_draft | {task_id, tone?} | {text} |
project.summary | {project_id} | {status_text, risks[], next_steps[]} (кэш 1 ч в ai_messages с context_type=project) |
project.spec_draft | {project_id, brief} | {goal, format, site, blocks[]} |
project.estimate_blocks | {project_id, spec_version_id} | {blocks[{name, cost_item, hours, rationale}]} |
chat.missed | {chat_id} | {summary, decisions[], questions_to_me[], deadlines[]} |
chat.reply_draft | {chat_id, tone?} / chat.rephrase {text, style} | {text} |
timesheet.suggest | {date_from, date_to} | {entries[{project_id, task_id, hours, comment}]} |
timesheet.correction_reason | {entry_id, delta_hours} | {text} |
Сервис действия сам собирает контекст через сервисы модулей от имени актора (AI-P1) и отрезает поля по политике данных (AI-P7).
5.4 Промпты и кэш
- Системный промпт один на всю систему: роль помощника, правила
AI-P2/AI-P5, формат ссылок на сущности ([Задача #12](task:12)), язык — русский. Описания инструментов — детерминированный порядок. Это стабильный префикс для кэша провайдера (у DeepSeek и Anthropic кэш по префиксу). - Динамика (кто спрашивает, роль, страница, дата) — отдельным блоком после префикса.
- Лимит истории беседы: последние 30 сообщений или 60K токенов; старше — сворачиваются в одно сообщение «Ранее в беседе…» (делает сама модель дешёвой моделью при превышении).
5.5 Ограничения и защита
- Таймаут запроса к провайдеру 60 с (стрим — 120 с), 2 повтора на 429/5xx с паузой.
- Лимиты
AI-A1.4проверяются до вызова; счётчик — суммаai_usageза сутки. - Ошибки провайдера мапятся в
transport.ErrCode*с русскими текстами (реестрapperr, тестerror_texts_test.go): «Помощник временно недоступен», «Дневной лимит исчерпан», «Ключ не принят провайдером». - Ключ в логах и в
auditне появляется; вPUTsettings полеapi_keyне логируется middleware.
6. Фронтенд
src/api/ai/index.ts: обычные методы черезapiFetch; для стрима — отдельная функция наfetch+ReadableStreamс ручнымAuthorization(первое SSE-использование во фронте,resolveWebSocketAuthне подходит).src/composables/useAiAssistant.ts: состояние панели, беседы, стрим, контекст страницы (изroute), предложения действий.- Компоненты в
src/components/ai/:AiAssistantPanel.vue(вdefault.vueрядом с чатом, регистрируется вuseRightPanels),AiMessage.vue,AiComposer.vue,AiSuggestionChips.vue,AiProposalCard.vue,AiActionButton.vue(кнопка-искра для встраивания в карточки, сама прячется, если помощник выключен или функция не включена вGET /ai/status). - Markdown: добавить
markdown-it+DOMPurify— единственныйv-htmlв проекте, обёрнут вAiMarkdown.vueвнутриcomponents/ui, guard разрешаетv-htmlтолько там. Ссылкиtask:12/project:3рендерятся вentityRoute. - Админка:
src/pages/admin/ai.vue→src/views/admin/AdminAiPage.vue, форма по образцуRequisitesFormPanel, вкладки «Подключение», «Модели», «Функции и лимиты», «Расход». - Меню: пункт «AI-помощник» в «Администрирование»,
routeAccess—users_and_rights. - Все правила guard соблюдаются (только
App*-компоненты, токены цветов, безIntl, лимит 600 строк на файл). Новое правило guard:v-htmlразрешён только вAiMarkdown.vue. - e2e: сценарий «админ задаёт ключ → проверяет → включает панель → сотрудник видит кнопку; ключ убран → кнопки исчезли». Для e2e провайдер подменяется заглушкой через
base_urlна локальный mock-сервер.
7. Этапы и оценка
| Этап | Содержание | Результат для пользователя | Оценка |
|---|---|---|---|
| 0. Ядро | internal/ai (провайдеры, детект, шифрование, справочник), таблицы, ai.manage, admin API, страница /admin/ai с проверкой подключения, ai_usage + аудит | Админ вводит ключ, видит провайдера и модели, включает фичи | 5–7 дней |
| 1. Панель | Беседы, SSE-стрим, инструменты read-only (search, projects, tasks, chats, timesheet, whoami), контекст страницы, лимиты, markdown, AiAssistantPanel | Любой сотрудник спрашивает про свою работу; руководитель — про дашборд (AI-A3) | 7–10 дней |
| 2. Действия | /ai/actions/* для задач, проектов, чатов, табеля; кнопки-искры в карточках; предложения с подтверждением | «Сформулировать задачу», «Кратко о задаче», «Что я пропустил», «Черновик ТЗ», «Заполнить табель» | 7–10 дней |
| 3. Руководству | Дайджест-воркер, вкладка «Расход», поиск через ⌘K, политика данных | Утренний дайджест рисков, контроль стоимости | 4–6 дней |
| 4. Позже | Извлечение реквизитов из PDF/фото договора в реестр (vision у deepseek-flash), семантический поиск (нужен провайдер эмбеддингов), голосовой ввод | по решению |
Этапы 0 и 1 — минимально ценная версия. Каждый этап закрывается по Definition of Done из CLAUDE.md: документация в docs/concepts/ai-assistant.md, регенерация контрактов, rbac_matrix.md, README (новые env: APP_SECRETS_KEY, APP_AI_ENABLED), contractcheck.
8. Стоимость (ориентир по прайсу DeepSeek на 2026-09-30)
| Сценарий | Токены за вызов (вход / выход) | deepseek-flash | deepseek-v4-pro |
|---|---|---|---|
| Реплика в панели с 1–2 инструментами | ~6 000 / 500 | ~$0,001–0,002 | ~$0,005–0,010 |
| «Кратко о задаче», «Что я пропустил» | ~4 000 / 400 | ~$0,001 | ~$0,004 |
| Черновик ТЗ, блоки сметы | ~5 000 / 1 500 | ~$0,002 | ~$0,008 |
| Дайджест на одного руководителя | ~3 000 / 400 | ~$0,001 | — |
При 60 сотрудниках и 15 обращениях в день на человека — порядка 1–2 $ в день на deepseek-flash. Кэш префикса снижает вход в 50 раз, ночной тариф — вдвое. Для Anthropic (claude-opus-5-5: $4 / $20 за 1M) те же сценарии дороже примерно в 15–20 раз, claude-haiku-4-5 — в 3–5 раз. Оценка стоимости выводится в «Расходе» по справочнику catalog.go, а не запрашивается у провайдера.
9. Открытые вопросы к заказчику
Хранить ли историю бесед (по умолчанию 90 дней) или только статистику?Решено 2026-09-30: хранить в БД бессрочно, пока сотрудник сам не удалит беседу.Передавать ли суммы и контакты клиентов во внешний сервис по умолчанию?Решено 2026-09-30: передавать, но защититься от утечек — секреты вырезаются перед отправкой.Дайджест — только in-app или и на почту?Решено 2026-09-30: только в RMS, на почту не слать. Получатели — как в разделе 13.- Нужен ли отдельный ключ на дивизион или один на организацию — один на организацию.
Приоритет междуСделаны оба.AI-U2иAI-U3внутри этапа 2.
10. Этап 0: что сделано (2026-09-30)
internal/ai: интерфейсProvider, адаптер OpenAI-совместимого API (DeepSeek, OpenAI, шлюзы; у DeepSeek рассуждение выключается полемthinking), адаптер Anthropic на официальном Go SDK (anthropic-sdk-go, новая зависимость), определение провайдера по ключу, справочник цен, AES-GCM шифрование ключа.internal/modules/assistant: настройки, проверка подключения,GET /ai/status, журналai_usage, аудитai.settings_updated/ai.connection_testedбез значения ключа.- Миграция
0157_ai_assistant.sql, переменнаяAPP_SECRETS_KEY(config, compose, README). - Фронтенд: «Администрирование → AI-помощник» — вкладки «Подключение» (ключ, провайдер, адрес, проверка, модели с ценами) и «Функции и лимиты» (функции, лимиты, данные).
- Проверено вживую на ключе DeepSeek: провайдер определён, модели
deepseek-flashиdeepseek-v4-pro, ответ модели меньше секунды; ключ в БД только зашифрован, в ответах API, аудите и логах его нет.
Не вошло в этап 0 и перенесено: GET /ai/usage и вкладка «Расход» — в этап 3 по плану; стриминг и WriteTimeout — в этап 1.
11. Этап 1: что сделано (2026-09-30)
- Беседы (
ai_conversations,ai_messages, миграция0158), ответ потоком SSE, до 5 обращений к инструментам за вопрос. - Инструменты читают данные через собственный API приложения с токеном сотрудника (
HandlerAPI) — права и вырезание денег по матрице действуют без дублирования; поверх — политика данных. Отличие от раздела 3.1: инструментаwhoamiнет — сотрудник, должность и отдел уходят в изменчивую часть системного промпта. - Адаптеры: вызовы инструментов и потоковый ответ у DeepSeek/OpenAI (SSE) и Anthropic (SDK, кэш системного промпта).
- Фронтенд: панель «Помощник» (⌘J / Ctrl+J, кнопка в шапке), подсказки по странице, беседы, «Остановить», безопасный рендер Markdown
AppMarkdownбезv-html— вместоmarkdown-itиDOMPurifyиз раздела 6, без новых зависимостей. - Проверено вживую на DeepSeek: «Что у меня горит?» — 2,4 с с вызовом инструмента; риски проекта — ответ со ссылками на задачи; инженеру панель руководства отдаёт только его охват.
12. Этап 2: что сделано (2026-09-30)
POST /ai/actions/:action:task_summary,task_formulate,project_summary,chat_missed,reply_draft,rephrase. Данные собирает код через API от имени сотрудника, модель отвечает JSON по схеме (DeepSeek —response_format: json_object), результат — предложение без сохранения.- Интерфейс: «Кратко о задаче» в карточке задачи, «Сформулировать» в форме новой задачи, «Сводка помощника» в обзоре проекта, «Что я пропустил» в чате, меню ✦ в поле ввода чата (помочь с ответом, короче, официальнее, мягче).
- Проверено вживую на DeepSeek: 1–3 секунды на действие.
Дополнено в тот же день: spec_draft («Заполнить черновик» в новой версии ТЗ), estimate_rows («Предложить строки» сметы), timesheet_fill («Заполнить по задачам» — ответ модели проверяется кодом: только пустые рабочие дни, свои задачи, до 8 ч в день), correction_reason («Сформулировать» причину корректировки), предложения в панели — назначить ответственного и перенести срок с кнопкой «Применить» (миграция 0159). Отличие от раздела 3.5: табель заполняется по задачам сотрудника, а не по закрытым рабочим сессиям — время сессий и так записывается в часы автоматически.
13. Этап 3: что сделано (2026-09-30)
- Утренняя сводка (
AI-A4): воркер в процессе API раз в 5 минут, по будням в назначенный час (5–12, по умолчанию 8:00). Получатели — OWNER, OPS, DIV_HEAD и, по переключателю, менеджеры активных проектов. Факты — просроченные задачи, срок сегодня, без ответственного, табели прошлого месяца, обязательства к сроку — читаются через API от имени получателя с короткоживущим токеном; модель пишет текст, при сбое уходит список фактов. Сводка — беседа помощника, уведомлениеai.digestоткрывает её в панели, письмо — по настройке. Одна сводка в день на получателя — таблицаai_digests(миграция0160). «Прислать мне сейчас» — проверка до включения. - Расход (
AI-A2):GET /ai/usage(по дням, сотрудникам, функциям) иGET /ai/usage/summary, оценка стоимости по справочнику цен, вкладка «Расход» с плитками, таблицей и CSV. - Аудит (
AI-A5): событиеai.requestна каждый вызов модели — функция, модель, токены, итог, без текстов. - Поиск (
AI-U6): строка «Спросить помощника» в ⌘K для запросов-вопросов.
Отличия от разделов 4.4 и 9: получатели по умолчанию — руководство и менеджеры активных проектов; письмо по умолчанию выключено, включается в настройках сводки; сводка приходит беседой, а не только текстом уведомления, — чтобы можно было сразу задать уточняющий вопрос. Политика данных (AI-A1.5) сделана в этапе 0.
14. Этап 4: что сделано (2026-09-30)
- Реквизиты из файла:
POST /ai/extract/requisites— фото, скан, PDF (текстовый слой или первые 3 страницы картинками через poppler), DOCX, TXT. Изображения — модели сVision(у DeepSeekdeepseek-flash). Код проверяет ИНН и ОГРН по контрольным цифрам (новый пакетinternal/legalid), счета — по ключу с БИК, сверяет с ЕГРЮЛ и справочником банков через DaData и дополняет пустые поля. Кнопка «Распознать из файла» — в «Новой компании» и в реквизитах нашей компании; замечания подсвечивают поля. - Поиск по смыслу:
GET /ai/search— модель подбирает поисковые фразы, обычный поиск ищет по ним с правами сотрудника; раздел «Похоже по смыслу» в ⌘K. - Голосовой ввод: микрофон в панели помощника и в чатах, распознавание речи браузера.
- Провайдеры:
Message.Images— изображения в запросе (OpenAI-совместимый API — частиimage_url, Anthropic — блоки изображений).
Отличия от раздела 7: семантический поиск без эмбеддингов (у DeepSeek их нет) — расширение запроса моделью; при подключении OpenAI-ключа можно добавить векторный поиск отдельно. Голос — браузерный, без серверной расшифровки. Реквизиты заполняют форму компании, а не отдельный «реестр».
15. Доработки по ответам заказчика (2026-09-30)
- История бесед хранится бессрочно: срок хранения убран, «Удалить беседу» удаляет её вместе с сообщениями (с подтверждением). Беседы, убранные раньше в архив, удалены миграцией
0162. - Длинные беседы (раздел 5.4): начало беседы сворачивается дешёвой моделью в «Ранее в беседе…», модели уходят свёртка и последние реплики.
- Защита от утечек: суммы и контакты передаются (включено миграцией
0162), а пароли, ключи и токены, номера карт, паспорта и СНИЛС вырезаются из всего, что уходит провайдеру, —assistant.Redact, проверено e2e на заглушке провайдера. - Повторы (раздел 5.5): два повтора на 429 и 5xx с паузой 1 и 3 с; поток — только пока ответ не начался.
- Утренняя сводка только в RMS, письмо убрано.
- Сводка проекта (AI-U3) хранится час у каждого сотрудника, с меткой времени и «Обновить».
- Плавающая кнопка помощника над кнопкой чата (AI-U1).
- e2e из раздела 6 —
e2e/ai-assistant.spec.ts, запуск сE2E_AI_MOCK=1.
Не делается: отдельный ключ на дивизион (вопрос 4) — один ключ на организацию.