feat(deploy): platform handoff — agent routing, persistence, docs cleanup

Agent routing:
- Remove !agent command and manual agent selection flow
- Registry auto-assigns agent from user_agents mapping (fallback: agents[0])
- provision_workspace_chat and !new both write agent_id to room_meta
- Reconciliation backfills agent_id from registry on cold start
- Fix duplicate agent_id block in auth.py

Deployment stability:
- Add bot-state named volume to persist lambda_matrix.db and matrix_store
- Fix docker-compose.prod.yml duplicate environment: key (was silently losing all Matrix credentials)
- Fix MATRIX_AGENT_REGISTRY_PATH to use absolute container path /app/config/...
- Add bot-state volume declaration to docker-compose.fullstack.yml

Docs and config:
- Rewrite README.md for platform handoff (deploy table, working commands only)
- Rewrite docs/matrix-prototype.md (remove stale commands and mock descriptions)
- Remove !save/!load/!context/!agent from help text and welcome message
- Add !clear, !list, !remove, !yes/!no to help text
- Clean up .env.example (remove Telegram token, internal vars, real URLs)
- Update config/matrix-agents.example.yaml with user_agents section and comments
- Add explanatory comment to Dockerfile for --ignore-requires-python
- Remove silent uv sync fallbacks in Dockerfile
This commit is contained in:
Mikhail Putilovskij 2026-04-28 03:05:11 +03:00
parent 380961d6e9
commit b1aaa210a1
21 changed files with 311 additions and 937 deletions

View file

@ -4,263 +4,101 @@
Один бот, каждый чат — отдельная комната, все комнаты собраны в personal Space.
При первом входе бот создаёт для пользователя личное пространство (Space) —
это как папка в Element. Внутри Space бот создаёт комнату для каждого нового
чата с агентом. Пользователь видит аккуратную структуру: одно пространство,
внутри — список чатов. История хранится нативно в Matrix — это часть протокола,
ничего дополнительно делать не нужно.
При первом invite бот создаёт для пользователя личное пространство (Space) и первую рабочую комнату.
История хранится нативно в Matrix. UX прагматичный: явные `!`-команды, локальный state-store, нативные Matrix rooms.
Matrix выбран как внутренняя поверхность: команды лаборатории, тестировщики,
разработчики скиллов. Поэтому UX здесь прагматичный: минимум магии, явные
команды `!`, локальный state-store и нативные Matrix rooms.
Matrix — внутренняя поверхность: команда лаборатории, тестировщики, разработчики скиллов.
---
## Аутентификация
## Онбординг
### Флоу
1. Пользователь приглашает бота в личные сообщения или пишет в общей комнате
2. Бот проверяет `@user:matrix.org` — есть ли аккаунт на платформе
3. Если нет — бот отправляет одноразовый код или ссылку
4. Пользователь подтверждает, платформа возвращает токен
5. Бот сохраняет привязку `matrix_user_id → platform_user_id`
1. Пользователь приглашает бота в личные сообщения (DM) на Matrix-сервере
2. Бот принимает invite, создаёт Space `Lambda — {display_name}` и первую комнату `Чат 1`
3. Приглашает пользователя в `Чат 1` и пишет приветствие
4. Дальнейшее общение ведётся в рабочих комнатах, не в DM
### В моке
- Любой пользователь проходит аутентификацию автоматически
- Бот отвечает: «Добро пожаловать, {display_name}. Создаю ваше пространство...»
- Демонстрирует флоу без реальной платформы
---
## Чаты через Space + комнаты (вариант Б)
### Структура
```
Space: «Lambda — {display_name}»
├── 💬 Чат 1 ← первый чат, создаётся автоматически
├── 💬 Чат 1 ← создаётся автоматически при invite
├── 💬 Чат 2
└── 💬 Исследование рынка ← пользователь сам называет
└── 💬 Исследование рынка ← пользователь называет сам через !new
```
### Создание Space
При первом входе бот:
1. Создаёт Space `Lambda — {display_name}`
2. Создаёт первую комнату-чат `Чат 1`
3. Передаёт `invite=[matrix_user_id]` прямо в `room_create(...)` для Space и комнаты
4. Привязывает `chat_id ↔ room_id` в локальном состоянии
5. Пишет приветствие в `Чат 1`
**Требование:** незашифрованные комнаты. E2EE не поддержан (инфраструктурное ограничение).
---
## Работающие команды
### Управление чатами
Команды работают в зарегистрированных комнатах бота:
| Команда | Действие |
|---|---|
| `!new` | Создать новый чат (новую комнату в Space) |
| `!new Название` | Создать чат с именем |
| `!help` | Показать шпаргалку по доступным командам |
| `!rename Название` | Переименовать текущую комнату |
| `!archive` | Архивировать чат и вывести бота из комнаты |
| `!chats` | Показать список чатов |
| `!settings`, `!skills`, `!soul`, `!safety`, `!plan`, `!status`, `!whoami` | Настройки и диагностика |
| `!chats` | Список активных чатов |
| `!rename <название>` | Переименовать текущую комнату |
| `!archive` | Архивировать чат |
| `!help` | Справка |
### Создание нового чата
1. Пользователь пишет `!new` или `!new Анализ конкурентов`
2. Бот создаёт новую комнату в Space
3. Сразу приглашает пользователя через `room_create(..., invite=[user_id])`
4. Регистрирует комнату в локальном состоянии и `ChatManager`
5. Пользователь переходит в новую комнату — начинает диалог
### Контекст
### В моке
- Space и комнаты создаются реально через matrix-nio
- Сообщения передаются в MockPlatformClient с `chat_id` (C1, C2...)
- История хранится в Matrix нативно
- Дефолтные `skills`, `safety`, `soul`, `plan` подмешиваются даже после частичных локальных обновлений настроек
| Команда | Действие |
|---|---|
| `!clear` | Сбросить контекст текущего чата (создаёт новый thread у агента) |
| `!reset` | Псевдоним для `!clear` |
### Переименование и архивирование
### Подтверждения
- `!rename` обновляет имя комнаты через state event `m.room.name`
- `!archive` архивирует чат в `ChatManager` и делает `room_leave(...)`
- Если бот потерял локальное состояние и видит комнату как `unregistered:*`, то `!rename` и `!archive` возвращают защитное сообщение вместо сломанного действия
| Команда | Действие |
|---|---|
| `!yes` | Подтвердить действие агента |
| `!no` | Отменить действие агента |
### Вложения (файловая очередь)
Matrix-клиенты отправляют файлы и текст отдельными событиями. Файл без текстовой инструкции ставится в очередь, а не уходит агенту сразу.
| Команда | Действие |
|---|---|
| `!list` | Показать файлы в очереди |
| `!remove <n>` | Удалить файл из очереди по номеру |
| `!remove all` | Очистить всю очередь |
Как отправить файлы агенту:
1. Отправь один или несколько файлов в рабочую комнату
2. Напиши текстовое сообщение с инструкцией, например: `что на изображении?`
3. Бот отправит агенту текст вместе со всеми файлами из очереди
---
## Основной диалог
## Диалог
### Флоу сообщения
1. Пользователь пишет текст в комнату-чат
2. Бот показывает typing (m.typing event)
3. Запрос уходит в платформу (MockPlatformClient)
4. Бот отвечает в той же комнате
### Вложения
- Файлы, изображения отправляются как Matrix media events
- Бот принимает `m.file`, `m.image`, `m.audio`
- Передаёт в платформу как `attachments` через `IncomingMessage`
- В моке: подтверждение получения + заглушка-ответ
### Реакции как действия
Matrix поддерживает реакции на сообщения (`m.reaction`).
Используем это для подтверждения действий агента:
```
Агент: Хочу отправить письмо на vasya@mail.ru
Тема: «Отчёт за неделю»
👍 — подтвердить ❌ — отменить
```
Пользователь ставит реакцию — бот обрабатывает. Нативно и удобно.
### Треды для длинных задач
Если агент выполняет долгую задачу (deep research, генерация документа),
бот создаёт тред от своего первого ответа и пишет промежуточные статусы туда.
Основной чат не засоряется.
```
Бот: Начинаю исследование по теме «AI агенты 2025» [→ в треде]
└── Ищу источники... (1/4)
└── Анализирую статьи... (2/4)
└── Формирую отчёт... (3/4)
└── Готово. Отчёт: [...]
```
- Любое текстовое сообщение уходит агенту, бот показывает typing-индикатор
- Ответ стримится по WebSocket и выводится в ту же комнату
- Каждая комната имеет свой `platform_chat_id` — контексты изолированы между комнатами
---
## Настройки и диагностика
## Передача файлов
Отдельной комнаты `Настройки` в текущей версии нет. Команды вызываются как обычные
`!`-команды из зарегистрированных комнат бота, а `!settings` отдаёт сводный dashboard
по скиллам, личности, безопасности и активным чатам.
### Пользователь → Агент
Бот сохраняет файл в shared volume: `/agents/surfaces/matrix/{user}/{room}/inbox/{stamp}-{filename}`
и передаёт агенту относительный путь как `workspace_path`.
### Коннекторы
```
!connectors — показать список
!connect gmail — подключить Gmail (OAuth ссылка)
!connect github — подключить GitHub
!connect calendar — подключить Google Calendar
!connect notion — подключить Notion
!disconnect gmail — отключить
```
Статус:
```
Коннекторы:
✅ Gmail — подключён (user@gmail.com)
❌ GitHub — не подключён → !connect github
❌ Google Calendar — не подключён
❌ Notion — не подключён
```
В моке: OAuth ссылка-заглушка → «Подключено ✓»
### Скиллы
```
!skills — показать список
!skill on browser — включить Browser Use
!skill off browser — выключить
```
Статус:
```
Скиллы:
✅ web-search — поиск в интернете
✅ fetch-url — чтение веб-страниц
✅ email — чтение почты (требует Gmail)
❌ browser — управление браузером
❌ image-gen — генерация изображений
❌ video-gen — генерация видео
✅ files — работа с файлами
❌ calendar — календарь (требует Google Calendar)
```
В моке: состояние хранится локально.
### Личность агента
```
!soul — показать текущий SOUL.md
!soul name Лямбда — задать имя агента
!soul style brief — стиль: brief | friendly | formal
!soul priority «разбирать почту утром» — приоритетная задача
!soul reset — сбросить к дефолту
```
В моке: SOUL.md генерируется и хранится локально, агент обращается по имени.
### Безопасность
```
!safety — показать настройки
!safety on email-send — требовать подтверждение перед отправкой письма
!safety off calendar-create — не спрашивать для создания событий
```
Статус:
```
Подтверждение требуется для:
✅ отправка письма
✅ удаление файлов
✅ публикация в соцсетях
❌ создание события в календаре
❌ поиск в интернете
```
### Подписка
```
!plan — показать текущий план
```
```
Подписка: Beta (бесплатно)
Токены этот месяц: 800 / 1000
━━━━━━━━░░ 80%
```
Заглушка, реализует другая команда.
### Статус и диагностика
```
!status — состояние платформы и чатов
!whoami — текущий аккаунт платформы
```
```
Статус:
Платформа: ✅ доступна
Аккаунт: user@lambda.lab
Активных чатов: 3
```
### Агент → Пользователь
Агент эмитит путь к файлу в своём workspace. Бот читает файл из `/agents/...`
и отправляет пользователю как Matrix file message.
---
## FSM состояния
## Известные ограничения
```
[Invite] → AuthPending → AuthConfirmed
SpaceSetup → Idle (в комнате Настройки)
[новая комната] → ChatCreated → Idle (в чате)
ReceivingMessage → WaitingResponse → Idle
WaitingReaction (confirm) → [✅/❌] → Idle
LongTask → [тред со статусами] → Done → Idle
```
---
## Стек
- Python 3.11+
- matrix-nio (async) — Matrix клиент
- MockPlatformClient → `platform/interface.py`
- structlog для логирования
- SQLite / in-memory store для хранения `matrix_user_id → platform_user_id`, состояния скиллов и маппинга `chat_id → room_id`
---
## Ограничения текущей версии
- Ручной QA и текущая разработка идут только в незашифрованных комнатах
- После рестарта бот делает bootstrap sync и стартует с `since`, поэтому старые события не должны переигрываться повторно
- Если удалить локальную БД/стор, старые Matrix rooms останутся, но команды, завязанные на локальную регистрацию чатов, перестанут работать для этих комнат до повторного онбординга
| Проблема | Причина |
|---|---|
| `!save` / `!load` / `!context` | Нестабильны: `!save` зависит от агента (пишет файл в workspace), сессии хранятся in-memory и теряются при рестарте |
| Первый чанк ответа иногда пропадает после tool/file flow | Баг в upstream `platform-agent`; подробности: `docs/reports/2026-04-22-platform-streaming-final-bug-report-ru.md` |
| Персистентность истории между рестартами агента | `platform-agent` использует `MemorySaver` (in-memory) |
| E2EE комнаты | `python-olm` не собирается на macOS/ARM |
| `!settings` и настройки скиллов/SOUL/безопасности | Заглушки MVP, требуют готового SDK платформы |