Персонализация пользователей и сценариев в 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. Уровни персонализации
Персонализация разделяется по уровням:
-
Настройки подсети
-
политика безопасности;
- язык/часовой пояс по умолчанию;
-
ограничения контента.
-
Конфигурация сценария
-
параметры конкретного сценария в подсети (например, бренд, домен, режим работы).
-
Профиль пользователя (в подсети)
-
ФИО / preferred_name;
locale,timezone;- родительский контроль, флаги
is_child; -
базовые предпочтения (например, тема интерфейса по умолчанию).
-
Device overrides
-
настройки, специфичные для устройства (тема webio на этом устройстве, плотность интерфейса, high contrast).
-
Scenario State (per user)
-
логическое состояние сценария для конкретного пользователя:
- прогресс, результаты форм, история диалога и т.п.;
- обновляется нодой, на которой установлен сценарий.
-
Device/UI state
-
чисто UI-состояние (какая вкладка открыта, положение панелей);
- обычно живёт в 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— 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. Приоритеты конфигурации
При вычислении значения настройки используется иерархия:
SDK возвращает сценариям и навыкам уже «смёрдженное» значение:
- изменения на уровне пользователя попадают в профиль на 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; - применяйте тему/язык и другие настройки, используя смёрдженные значения из контекста.