Skip to content

ТЗ: 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, под правами актора):

ИнструментИсточникЧто отдаёт модели
searchsearch.Service.Searchкомпании, контакты, проекты, задачи, люди
get_projectprojects.Serviceкарточка, статус, сроки, ворота, деньги (по праву)
list_taskstasks.Service.Listфильтры: мои, проект, статус, просрочено, срок
get_tasktasks.Service + комментарии + последние N сообщений чата задачи
get_chat_messageschats.Serviceсообщения с курсора «прочитано»
my_timesheettimesheets/timeentriesчасы за период, черновик/сдан, корректировки
dashboarddashboard.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 с карточками:

  1. Подключение. Поле «API-ключ» (write-only, показывается подсказка «sk-…a1b2» и дата последней смены), выбор провайдера: «Определить автоматически» (по умолчанию) / DeepSeek / OpenAI / Anthropic, необязательный «Base URL» (прокси, self-hosted OpenAI-совместимый шлюз). Кнопка «Проверить подключение» — вызывает POST /ai/settings/test, показывает провайдера, список моделей, время ответа. Кнопка «Отключить помощника» с подтверждением.
  2. Модели. Таблица моделей провайдера (id, контекст, поддержка инструментов/JSON/изображений — то, что вернул /models плюс наш справочник). Выбор модели по назначению: «Диалог и действия» (быстрая, дешёвая), «Аналитика и черновики ТЗ» (сильная). Для DeepSeek по умолчанию deepseek-flash и deepseek-v4-pro; для Anthropic claude-haiku-4-5 и claude-opus-5-5; для OpenAI — первая доступная из справочника.
  3. Функции. Переключатели по фичам из раздела 3 и 4 (панель, задачи, проекты, чаты, табель, дайджест). Выключенная фича скрывает кнопки во фронте (через capabilities в /org/me).
  4. Лимиты. Токенов в сутки на организацию и на пользователя, максимум сообщений в беседе, таймаут запроса. При исчерпании — AI-P6.
  5. Политика данных. Чекбоксы: «Передавать суммы и бюджеты», «Передавать контакты клиентов (телефон, 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_KEY
go
type 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):

  1. Ключ начинается с sk-ant- → Anthropic (GET https://api.anthropic.com/v1/models, заголовки x-api-key, anthropic-version).
  2. Иначе пробуем DeepSeek: GET https://api.deepseek.com/models с Authorization: Bearer. 200 → DeepSeek.
  3. Иначе OpenAI: GET https://api.openai.com/v1/models.
  4. Если администратор выбрал провайдера вручную или задал 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_conversationsid, user_id, title, context_type/context_id (project/task/chat/null), created_at, updated_at, archived_at; индекс (user_id, updated_at desc)
ai_messagesconversation_id, role (user/assistant/tool), content text, tool_calls jsonb, tool_call_id, usage jsonb, created_at; индекс (conversation_id, id)
ai_usageuser_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/settingsai.manageнастройки без ключа, с key_hint (этап 0)
PUT /api/v1/ai/settingsai.manageсохранить; ключ передаётся только если меняется (этап 0)
POST /api/v1/ai/settings/testai.manageпроба ключа из тела или сохранённого; ответ: провайдер, модели, latency (этап 0)
GET /api/v1/ai/statusсотрудниквключено ли, функции — фронт вызывает один раз на сессию (этап 0)
`GET /api/v1/ai/usage?from&to&group_by=userfeatureday`
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 не появляется; в PUT settings поле 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-flashdeepseek-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. Открытые вопросы к заказчику ​

  1. Хранить ли историю бесед (по умолчанию 90 дней) или только статистику? Решено 2026-09-30: хранить в БД бессрочно, пока сотрудник сам не удалит беседу.
  2. Передавать ли суммы и контакты клиентов во внешний сервис по умолчанию? Решено 2026-09-30: передавать, но защититься от утечек — секреты вырезаются перед отправкой.
  3. Дайджест — только in-app или и на почту? Решено 2026-09-30: только в RMS, на почту не слать. Получатели — как в разделе 13.
  4. Нужен ли отдельный ключ на дивизион или один на организацию — один на организацию.
  5. Приоритет между 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 (у DeepSeek deepseek-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) — один ключ на организацию.

Загружаем документацию…