Перейти к основному содержимому

Уведомления

AgentCore хранит in-app уведомления на каждого пользователя и стримит новые события через per-user WebSocket-канал. Уведомления нужны для работы, которая требует внимания пользователя за пределами текущего чата: approvals, упоминания, сгенерированные документы, операционные алерты.

Модель данных

Уведомления хранятся в таблице notifications:

ПолеНазначение
userIdПолучатель. Любые операции list/read/delete ограничены этим пользователем.
typeТип события, задаётся продюсером: approval_pending, mention, document_ready и т.п.
titleКороткий заголовок для ленты.
bodyОпциональный более длинный текст.
entityType / entityIdОпциональная цель для ссылки: approval, сообщение, документ, генерация.
readAtnull, пока пользователь не отметил как прочитанное.
createdAtКлюч сортировки для ленты и retention-задач.

Индексы (userId, readAt) и (userId, createdAt) делают unread count, пагинацию и retention-сканы дешёвыми.

Типы уведомлений

Бэкенд хранит type как строку — можно добавлять новых продюсеров без миграции БД. Текущие и планируемые типы:

ТипПродюсерСущность
approval_pendingМаршруты agent runner и approvals, когда нужен HITL или эскалация.approval
mentionКоллаборативные фичи, когда пользователя напрямую упоминают в треде или документе.message, comment или document
document_readyГенерация документов и ingestion, когда результат готов.document или document_generation
systemОперационные уведомления, не привязанные к бизнес-сущности.Опционально

Запись уведомления — это долговременная запись. WebSocket-доставка — только realtime-транспорт; клиенты после переподключения всё равно должны запрашивать REST inbox.

REST API

Все маршруты требуют JWT и работают только с текущим пользователем.

ОперацияЭндпоинтПримечания
СписокGET /api/v1/notifications?limit=20&offset=0Возвращает items, pagination, unreadCount.
Только непрочитанныеGET /api/v1/notifications?unreadOnly=trueИспользует readAt IS NULL.
Отметить одно прочитаннымPATCH /api/v1/notifications/:id/readИдемпотентно; уже прочитанные строки возвращаются без изменений.
Отметить всё прочитаннымPOST /api/v1/notifications/read-allОбновляет все непрочитанные строки текущего пользователя.
Удалить одноDELETE /api/v1/notifications/:idУдаляет только строки, принадлежащие текущему пользователю.

Route layer всегда проверяет и id, и userId — угадать чужой id и прочитать или удалить чужое уведомление не получится.

WebSocket-трансляция

Realtime-канал — /ws/notifications.

Клиенты аутентифицируются одним из двух способов:

  1. подключиться с ?token=<jwt>;
  2. подключиться и прислать первым сообщением { "action": "auth", "token": "<jwt>" }.

Если аутентификация не пройдена за 5 секунд — сокет закрывается с кодом 4001.

После аутентификации сокет подписывается на in-process EventEmitter по ключу userId. Продюсеры сначала создают строку в БД, потом вызывают emitNotification(). WebSocket отправляет:

{
"type": "notification.created",
"payload": {
"id": "clx...",
"userId": "clu...",
"type": "approval_pending",
"title": "Approval required",
"body": "A generated reply needs review",
"entityType": "approval",
"entityId": "clp...",
"readAt": null,
"createdAt": "2026-04-16T10:15:00.000Z"
}
}

Emitter живёт внутри процесса, поэтому при горизонтальном масштабировании нужен общий pub/sub-слой, чтобы несколько реплик API могли транслировать подписанным сокетам на разных подах. Redis pub/sub естественно подходит — Redis уже нужен для BullMQ.

Read state

Статус прочтения вычисляется из readAt:

  • уведомление считается непрочитанным, если readAt равно null;
  • отметка «прочитано» выставляет readAt в текущее серверное время;
  • read-all обновляет все непрочитанные уведомления текущего пользователя;
  • удаление строки убирает её из inbox и из подсчёта unread.

Клиенты должны оптимистично обновлять локальный read state, но при этом ресинхронизироваться через GET /notifications после переподключения или упавших write-запросов.

Retention-политика

В request-path нет автоматической чистки. Рекомендованная production-политика — плановая задача очистки:

  • непрочитанные уведомления хранить, пока не прочитали или не удалили явно;
  • прочитанные хранить 90 дней;
  • строки approval_pending и document_ready хранить дольше, если политика аудита требует, чтобы связанный approval или документ оставался обнаруживаемым;
  • удалять небольшими батчами, упорядоченными по createdAt, чтобы избежать длинных блокировок таблицы.

Задача очистки не должна удалять связанную бизнес-сущность. Она удаляет только inbox-указатель.

Пользовательские настройки

Настройки уведомлений моделируются на стороне клиента/продукта как direct или digest доставка:

НастройкаПоведение
directНемедленно показать событие из WebSocket и сохранить строку в inbox.
digestОставить строку в inbox, не прерывать UI, включить в плановый дайджест.
mutedСохранить строки, важные для аудита, но не показывать в проактивном UI.

Пока настройки не хранятся на стороне сервера, клиенты должны держать локальные предпочтения по каждому type уведомления, а канонический unread count всё равно брать с сервера.

Чеклист для продюсера

При добавлении нового продюсера:

  1. создайте строку Notification в том же логическом потоке, что и исходное событие;
  2. укажите entityType и entityId, если пользователь должен куда-то перейти;
  3. вызовите emitNotification(notification) после коммита строки;
  4. добавьте или расширьте тесты REST inbox и WebSocket-трансляции;
  5. задокументируйте, по умолчанию это direct или digest событие.