Skip to content

Персонализация пользователей и сценариев в AdaOS

Этот документ описывает подход к персонализации в AdaOS: профиль пользователя, состояние сценариев и взаимодействие с ними через ноды подсети, hub и Yjs.

Цели:

  • разделить ответственность между root, hub и нодами подсети;
  • не хранить персональные данные на root;
  • обеспечить персональное состояние для каждого пользователя и сценария;
  • дать возможность сценариям/навыкам (по разрешению) управлять параметрами нескольких пользователей внутри одного webspace.

1. Базовые понятия

Root Центральный сервер, который:

  • аутентифицирует пользователя;
  • знает, к каким подсетям у пользователя есть доступ;
  • прокладывает соединение к нужному hub подсети.

Root не участвует в исполнении сценариев и не хранит персональные данные.


Subnet Логическая подсеть, которой управляет один hub (плюс возможный резервный). Все ноды внутри подсети принадлежат только этой подсети и работают в её контексте. Внутри подсети subnet_id не таскается – он известен по факту принадлежности ноды.


Hub (внутри подсети)

  • владелец метаданных подсети (профили, индексы состояний сценариев, реестр навыков/сценариев);
  • точка входа для внешних клиентов (browser, боты и т.п.);
  • консолидация ресурсов нод подсети.

Hub не обязательно выполняет сценарии и навыки: они могут жить на других нодах (member-нодах), hub — регистратор/координатор.


Node (member нода)

Любая нода внутри подсети, на которой реально установлены и выполняются сценарии и навыки.

  • держит локальное состояние тех сценариев, которые на неё установлены;
  • обновляет Scenario State для этих сценариев;
  • общается с hub по контракту подсети.

Webspace Рабочее пространство в browserio (например, «рабочий стол», «комната занятия», «сцена игры»). Webspace логически принадлежит одной подсети и обслуживается конкретной нодой (часто hub, но не обязательно). Для каждого webspace есть один Yjs-документ.


Scenario Описанный в манифесте сценарий. Может:

  • использовать несколько навыков;
  • выполняться на конкретной ноде подсети;
  • иметь своё состояние для каждого пользователя.

User / Device

  • User — логический пользователь, имеющий доступ к подсети.
  • Device — конкретное физическое устройство (телефон, браузер и т.п.), для которого могут быть свои UI-настройки.

2. Где хранятся данные

2.1. Root

Root не хранит PII (персональные данные).

На root могут храниться только:

  • идентификаторы пользователей и привязки к внешней аутентификации;
  • список подсетей, к которым у пользователя есть доступ (как владелец, участник или гость);
  • технические настройки маршрутизации/логирования.

Дальше весь runtime и данные — уже внутри выбранной подсети.


2.2. Внутри подсети: hub и ноды

Все персональные и сценарные данные живут внутри подсети:

  • hub:

  • хранит профили пользователей в этой подсети;

  • хранит/индексирует состояние сценариев (или хотя бы ссылки на состояние, если оно распределено по нодам);
  • публикует проекцию данных в webspace (Yjs);
  • member-ноды:

  • хранят и обновляют Scenario State тех сценариев, которые на них установлены и выполняются;

  • при необходимости синкают состояние с hub (по принятому протоколу).

Конкретная топология (где именно лежит master-состояние сценария — на hub или на ноде) — внутренняя деталь подсети, но контракт с навыками/сценариями единый.


2.3. WebIO / устройства

WebIO (browserio/ionic-клиент):

  • подключается к подсети через root → hub;
  • работает с одним Yjs-документом на webspace;
  • хранит UI-состояние (layout, открытые вкладки и т.п.) локально или в части Yjs.

На устройстве можно дополнительно кешировать настройки, но «истинное» состояние хранится на нодах подсети.


3. Уровни персонализации

Персонализация разделяется по уровням:

  1. Настройки подсети

  2. политика безопасности;

  3. язык/часовой пояс по умолчанию;
  4. ограничения контента.

  5. Конфигурация сценария

  6. параметры конкретного сценария в подсети (например, бренд, домен, режим работы).

  7. Профиль пользователя (в подсети)

  8. ФИО / preferred_name;

  9. locale, timezone;
  10. родительский контроль, флаги is_child;
  11. базовые предпочтения (например, тема интерфейса по умолчанию).

  12. Device overrides

  13. настройки, специфичные для устройства (тема webio на этом устройстве, плотность интерфейса, high contrast).

  14. Scenario State (per user)

  15. логическое состояние сценария для конкретного пользователя:

    • прогресс, результаты форм, история диалога и т.п.;
    • обновляется нодой, на которой установлен сценарий.
  16. Device/UI state

  17. чисто UI-состояние (какая вкладка открыта, положение панелей);

  18. обычно живёт в Yjs / локальном storage.

4. Профиль пользователя внутри подсети

4.1. Логическая структура профиля (на hub)

Пример:

UserProfile {
  "user_id": "u-123",

  "full_name": "Иван Иванов",
  "preferred_name": "Иван",
  "avatar_url": "https://...",

  "locale": "ru-RU",
  "timezone": "Europe/Berlin",

  "is_child": false,
  "guardian_id": null,

  "parental_control": {
    "enabled": false
  },

  "webio": {
    "theme": "system",        // light | dark | system | custom:<id>
    "font_scale": 1.0,
    "high_contrast": false
  }
}

Этот профиль определяется в контексте одной подсети. Hub и ноды знают, к какой подсети они принадлежат, поэтому subnet_id здесь не нужен.

Навыки и сценарии получают только view этого профиля через SDK (ctx.profile), а не прямой доступ к хранилищу.


5. Состояние сценариев

5.1. Кто обновляет состояние

Сценарий и его навыки выполняются на конкретной ноде (member или hub). Эта же нода:

  • держит актуальное Scenario State для данного сценария;
  • обновляет его при каждом шаге сценария;
  • при необходимости синхронизирует состояние с hub (для консолидации, резервирования, аналитики).

5.2. Ключи состояния

Во внутренней модели подсети Scenario State логически определяется ключом:

(webspace_id, scenario_id, user_id[, device_id, instance_id])
  • webspace_id — webspace, в котором сценарий активен;
  • scenario_id — идентификатор сценария;
  • user_id — идентификатор пользователя;
  • device_id — опционально: для device-специфичного UI-состояния;
  • instance_id — опционально, если допускается несколько одновременных экземпляров сценария.

Нода, на которой установлен сценарий, отвечает за хранение/обновление этого состояния. Hub знает, на какой ноде искать нужный сценарий и его состояние.


5.3. Типы состояния

  • Scenario State (логическое)

  • то, что важно для бизнес-логики;

  • обновляется нодой сценария;
  • доступно в SDK как ctx.state.

  • UI State (device)

  • то, что важно только для UI;

  • обычно хранится в Yjs-документе webspace и/или на устройстве;
  • может не синкаться на hub, если это не нужно.

6. Yjs: один документ на webspace, ветки по пользователям и сценариям

6.1. Общий принцип

Для каждого webspace создаётся один Yjs-документ (wsDoc), который обслуживается конкретной нодой (часто той же, где запущен webio/прокси).

Внутри wsDoc живут:

  • проекции профилей пользователей, участвующих в webspace;
  • состояние сценариев для этих пользователей;
  • общее (shared) состояние мультиязычных/мультипользовательских сценариев;
  • UI-состояние.

6.2. Предлагаемая структура wsDoc

Пример логической структуры:

wsDoc:
  profiles: Y.Map<user_id, Y.Map>             // проекция профилей в контексте webspace
  scenario_state: Y.Map<scenario_id, Y.Map>   // -> Y.Map<user_id, Y.Map>
  ui_state: Y.Map<scenario_id, Y.Map>         // -> Y.Map<user_id, Y.Map<device_id, Y.Map>>
  shared_state: Y.Map<scenario_id, Y.Map>     // общее состояние сценария
  • profiles[user_id] — часть профиля, нужная webspace (имя, аватар, роль, язык, тема и т.п.);
  • scenario_state[scenario_id][user_id] — ветка персонального state сценария;
  • ui_state[scenario_id][user_id][device_id] — ветка чисто UI-state;
  • shared_state[scenario_id] — общий объект для совместного сценария.

6.3. Связь нод, hub и Yjs

  • WebIO подключается к wsDoc через ноду подсети;
  • сценарий/навык, выполняющийся на ноде, может:

  • читать/писать свою ветку scenario_state[scenario_id][current_user_id];

  • при наличии прав — обращаться к другим веткам в этом webspace (см. ниже);
  • hub может:

  • читать/писать wsDoc для синхронизации/мониторинга;

  • агрегировать состояние сценариев по нодам.

7. Навыки и персональные данные

7.1. Базовое правило

Навыки и сценарии не работают напрямую с хранилищем PII.

Они:

  • получают ctx.profile — безопасный view профиля текущего пользователя;
  • получают ctx.state — view состояния сценария для текущего пользователя;
  • могут (при наличии разрешения) получать webspace-view с ветками других пользователей.

Где физически лежат данные (hub, локальная БД ноды, внешний storage) — деталь реализации подсети. Для навыка это прозрачно.


7.2. Контекст навыка (идея интерфейса)

Псевдо-интерфейс:

interface AdaProfileView {
  user_id: string;
  locale: string;
  timezone: string;
  is_child: boolean;
  webio: {
    theme: string;
  };
}

interface AdaScenarioStateView {
  get(path: string, defaultValue?: any): any;
  set(path: string, value: any): void;
  merge(patch: object): void;
}

interface AdaWebspaceView {
  listUsers(): string[];                    // все пользователи в webspace
  getUserState(user_id: string): AdaScenarioStateView;
  setUserState(user_id: string, patch: object): void;
}

interface AdaSkillContext {
  profile: AdaProfileView;        // текущий пользователь
  state: AdaScenarioStateView;    // персональное состояние сценария
  webspace?: AdaWebspaceView;     // доступ к другим пользователям webspace (если разрешено)
}

Реализация может отличаться, но принцип важен:

  • навык видит абстракцию, а не физические таблицы;
  • персональные данные и параметры живут «под» этим API.

8. Мультипользовательские сценарии и изменение параметров других пользователей

8.1. Взаимодействие внутри одного webspace

Так как все участники webspace живут в одном wsDoc, сценарий (через SDK) может:

  • прочитать ветки состояний других пользователей;
  • модифицировать их персональные параметры/состояния по сигналу от одного пользователя.

Это позволяет реализовать:

  • совместные уроки, игры;
  • режим «ведущий/участники» (teacher/host);
  • любые сценарии «один меняет состояние других» (например, модератор отключает чат, учитель переводит всех в следующий этап).

Пример псевдокода:

def on_next_stage(ctx: AdaSkillContext):
    # Текущий пользователь – учитель
    for uid in ctx.webspace.listUsers():
        if uid == ctx.profile.user_id:
            continue
        other_state = ctx.webspace.getUserState(uid)
        other_state.set("stage", "quiz")

8.2. Безопасность и права

По умолчанию:

  • контекст навыка содержит только profile и state текущего пользователя;
  • webspace-доступ выдаётся только сценариям/навыкам, которые:

  • объявили это требование в манифесте;

  • разрешены политикой подсети/hub’а.

Рекомендуется:

  • явно описывать в манифесте сценария, что он мультипользовательский и работает с состоянием других пользователей;
  • встраивать модель ролей (например, teacher, host, moderator), чтобы только они могли менять чужие ветки.

9. Приоритеты конфигурации

При вычислении значения настройки используется иерархия:

subnet_config
  < scenario_config
  < user_profile
  < device_overrides

SDK возвращает сценариям и навыкам уже «смёрдженное» значение:

const theme = ctx.config.get("webio.theme");
  • изменения на уровне пользователя попадают в профиль на hub;
  • изменения на уровне устройства — в device-overrides или UI-state в Yjs (в зависимости от типа настройки).

10. Рекомендации для разработчиков

10.1. Разработчики навыков

  • Не работайте напрямую с хранилищем hub/нод.
  • Используйте:

  • ctx.profile для пользовательских настроек;

  • ctx.state для логики сценария;
  • ctx.webspace — только если сценарий мультипользовательский и имеет на это права.
  • Не храните PII в своих внутренних структурах — всё, что нужно, берите из контекста на момент запроса.

10.2. Разработчики сценариев

  • Рассчитывайте на то, что каждому (scenario_id, user_id) соответствует своё состояние (Scenario State).
  • Если сценарий мультипользовательский:

  • используйте shared_state и/или ctx.webspace;

  • учитывайте, что у одного пользователя может быть несколько устройств в одном webspace.

10.3. Разработчики hub/нод/webio

  • На hub:

  • храните профили и индексы состояний сценариев;

  • координируйте доступ к нодам и webspace;
  • при необходимости реплицируйте/резервируйте Scenario State.

  • На нодах:

  • храните и обновляйте Scenario State сценариев, которые на них установлены;

  • реализуйте API для чтения/записи состояния по контракту SDK.

  • В webio:

  • используйте один Yjs-документ на webspace;

  • разделяйте profiles, scenario_state, ui_state, shared_state;
  • применяйте тему/язык и другие настройки, используя смёрдженные значения из контекста.