feat(deploy): finalize MVP deployment and file transfer approach

This commit is contained in:
Mikhail Putilovskij 2026-05-02 23:45:52 +03:00
parent 6369721876
commit 0f79494fbe
43 changed files with 3078 additions and 645 deletions

340
docs/max-surface-guide.md Normal file
View file

@ -0,0 +1,340 @@
# Руководство по созданию новой поверхности Max
Этот документ описывает, как написать новую поверхность для Max по образцу текущей Matrix-поверхности в ветке `feat/deploy`.
Он основан на актуальной реализации Matrix surface в репозитории и отражает текущую продакшн-логику, а не устаревший легаси.
---
## 1. Общая архитектура
### 1.1. Что такое поверхность
Поверхность — это тонкий адаптер между конкретной платформой (Max) и общим ядром бота.
В репозитории есть разделение:
- `core/` — общее ядро и бизнес-логика
- `adapter/<platform>/` — реализация конкретной поверхности
- `sdk/real.py` — работа с реальной платформой / агентом
- `config/` — статическая конфигурация агентов
- `docs/surface-protocol.md` — общий контракт поверхностей
### 1.2. Как это работает
Поверхность должна:
- принимать нативные события от Max
- преобразовывать их в единый внутренний контракт (`IncomingMessage`, `IncomingCommand`, `IncomingCallback`)
- передавать их в `core`
- получать ответы из `core` (`OutgoingMessage`, `OutgoingUI`, `OutgoingTyping`, `OutgoingNotification`)
- преобразовывать ответы обратно в нативные Max-сообщения
Поверхность не должна:
- управлять жизненным циклом агентских контейнеров
- хранить долгую историю бесед вне `core`/платформы
- аутентифицировать пользователей сама (если это не часть Max API)
---
## 2. Структура новой поверхности
### 2.1. Основные каталоги
Рекомендуемая структура для Max:
```
adapter/max/
bot.py
converter.py
agent_registry.py
files.py
handlers/
store.py
```
### 2.2. Принцип reuse
По примеру Matrix surface, Max surface должен переиспользовать общий `core` и общий `sdk`.
Не дублируйте бизнес-логику, а реализуйте только адаптер:
- `adapter/max/converter.py` — конвертация событий Max ⇄ внутренние структуры
- `adapter/max/bot.py` — основной runtime, старт Max client, loop, отправка/прием
- `adapter/max/agent_registry.py` — загрузка `config/max-agents.yaml`
- `adapter/max/files.py` — хранение входящих/исходящих вложений
---
## 3. Контракт входящих/исходящих событий
### 3.1. Внутренний формат
Смотрите `core/protocol.py`. Основные типы:
- `IncomingMessage` — обычное текстовое сообщение + вложения
- `IncomingCommand` — управляющая команда
- `IncomingCallback` — подтверждение / интерактивные действия
- `OutgoingMessage` — ответ пользователю
- `OutgoingUI` — интерфейсные элементы (кнопки и т.п.)
- `OutgoingTyping` — индикатор печати
- `OutgoingNotification` — системное уведомление
### 3.2. Пример конверсии Matrix
В Matrix-реализации `adapter/matrix/converter.py`:
- текст `!yes` / `!no` превращается в `IncomingCallback` с `action: confirm/cancel`
- `!list`/`!remove` говорят не агенту, а surface-процессу
- вложения `m.file`, `m.image`, `m.audio`, `m.video` нормализуются в `Attachment`
Для Max реализуйте аналогичную логику для native команд вашего клиента.
---
## 4. Реестр агентов и маршрутизация
### 4.1. Что хранит реестр
В текущей Matrix реализации есть `config/matrix-agents.yaml` и `adapter/matrix/agent_registry.py`.
Структура:
```yaml
user_agents:
"@user0:matrix.example.org": agent-0
"@user1:matrix.example.org": agent-1
agents:
- id: agent-0
label: "Agent 0"
base_url: "http://lambda.coredump.ru:7000/agent_0/"
workspace_path: "/agents/0"
```
### 4.2. Логика выбора агента
- `user_agents` маппит конкретного пользователя на `agent_id`
- если user_id не найден, используется первый агент из списка
- `agents[].base_url` определяет URL агента
- `agents[].workspace_path` определяет путь внутри surface-контейнера для этого агента
Это важно: именно на этом контракте строится разделение агентов по рабочим каталогам.
### 4.3. Рекомендуемая Max-версия
Создайте `config/max-agents.yaml` с тем же смыслом.
- `user_agents` — маппинг Max user_id → agent_id
- `agents` — список агентов
- `workspace_path` для каждого агента должен быть абсолютным путем внутри surface-контейнера, например `/agents/0`
---
## 5. Файловый контракт
### 5.1. Shared volume
Текущее Matrix-решение использует shared volume:
- surface монтирует общий том как `/agents`
- каждый агент видит свою поддиректорию как `/workspace`
Топология:
```
Bot (/agents) Agent (/workspace = /agents/N/)
/agents/0/report.pdf ←──→ /workspace/report.pdf
```
### 5.2. Правила записи файлов
В `adapter/matrix/files.py` реализовано:
- входящий файл сохраняется прямо в `{workspace_root}/{filename}`
- возвращается путь `workspace_path` относительный внутри рабочего каталога агента
- при коллизии имен создаётся `file (1).ext`, `file (2).ext`
- `Attachment.workspace_path` передаётся агенту
Для исходящих файлов:
- surface читает файл из `workspace_root / workspace_path`
- загружает его в платформу
### 5.3. Пример поведения
- Пользователь отправляет файл → surface скачивает файл и кладёт его в agent workspace
- Агент получает `attachments=["report.pdf"]` и работает с относительным `workspace_path`
- Агент пишет результат в `/workspace/result.txt`
- surface читает `/agents/{N}/result.txt` и отправляет файл пользователю
---
## 6. Чат-менеджмент и контекст
### 6.1. `platform_chat_id`
Matrix-реализация использует `platform_chat_id` как стабильный идентификатор чата на стороне агента.
- `room_meta.platform_chat_id` определяется и сохраняется в `adapter/matrix/store.py`
- `reconcile_startup_state()` восстанавливает отсутствующие `platform_chat_id` при рестарте
- `RoutedPlatformClient` перенаправляет запросы агенту по `agent_id` + `platform_chat_id`
Для Max surface тот же принцип:
- каждая внешняя беседа должна привязываться к одному внутреннему `chat_id`
- этот `chat_id` используется для вызовов агента
- если в Max есть несколько комнат/топиков, каждая должна иметь свой `surface_ref`
### 6.2. Команды управления чатами
Matrix поддерживает следующие команды, которые нужно сохранить в Max:
- `!new [название]` — создать новый чат
- `!chats` — список активных чатов
- `!rename <название>` — переименовать текущий чат
- `!archive` — архивировать чат
- `!clear` / `!reset` — сбросить контекст текущего чата
- `!yes` / `!no` — подтвердить или отменить действие агента
- `!list` — показать очередь вложений
- `!remove <n>` / `!remove all` — удалить вложение из очереди
- `!help` — справка
Эти команды реализованы в Matrix через `adapter/matrix/handlers/`.
### 6.3. Очередь вложений
Matrix surface поддерживает staged attachments:
- файл может быть отправлен без текста
- surface сохраняет файл в `staged_attachments` для конкретного room_id + user_id
- следующий текст отправляется агенту вместе со всеми файлами из очереди
В Max можно реализовать ту же модель:
- `!list` показывает текущую очередь
- `!remove` удаляет файл из очереди
- команда-индикатор или следующее текстовое сообщение отправляет queued attachments агенту
---
## 7. Runtime и окружение
### 7.1. Переменные среды
Для Matrix surface текущий runtime ожидает:
- `MATRIX_HOMESERVER` — URL Matrix-сервера
- `MATRIX_USER_ID``@bot:example.org`
- `MATRIX_PASSWORD` или `MATRIX_ACCESS_TOKEN`
- `MATRIX_PLATFORM_BACKEND` — должно быть `real` для продакшна
- `MATRIX_AGENT_REGISTRY_PATH` — путь к `config/matrix-agents.yaml`
- `AGENT_BASE_URL` — fallback URL агента
- `SURFACES_WORKSPACE_DIR` — путь к shared volume внутри контейнера (по умолчанию `/workspace` в коде, но в docs рекомендуют `/agents`)
Для Max surface используйте аналогичные переменные:
- `MAX_PLATFORM_BACKEND=real`
- `MAX_AGENT_REGISTRY_PATH=/app/config/max-agents.yaml`
- `SURFACES_WORKSPACE_DIR=/agents`
- `AGENT_BASE_URL` — если хотите общий fallback
### 7.2. Environment contract
В коде `adapter/matrix/bot.py`:
- `_agent_base_url_from_env()` читает `AGENT_BASE_URL` или `AGENT_WS_URL`
- `_load_agent_registry_from_env()` читает `MATRIX_AGENT_REGISTRY_PATH`
- `_build_platform_from_env()` выбирает `RealPlatformClient` при `MATRIX_PLATFORM_BACKEND=real`
В Max surface реализуйте ту же логику, заменив префиксы на `MAX_`.
---
## 8. Тестирование и валидация
### 8.1. Юнит-тесты
В ветке есть покрытие для Matrix surface:
- `tests/adapter/matrix/test_files.py`
- `tests/adapter/matrix/test_dispatcher.py`
- `tests/adapter/matrix/test_routed_platform.py`
- `tests/adapter/matrix/test_reconciliation.py`
- `tests/adapter/matrix/test_context_commands.py`
Для Max создайте аналогичные тесты:
- проверка загрузки вложений
- проверка маршрутизации по `agent_id`
- проверка восстановления `platform_chat_id`
- проверка конвертации команд
### 8.2. Smoke-проверка deployment
Для Matrix surface есть `docker-compose.prod.yml` и `docker-compose.fullstack.yml`.
Для Max surface должно быть достаточно:
- bot-only production deployment
- shared volume `/agents`
- независимая проверка `config/max-agents.yaml`
- проверка, что surface запускается без локального агента
### 8.3. Проверка контрактов
Особое внимание:
- `agent_registry` должен загружать `workspace_path`
- file flow должен поддерживать `workspace_path` в `Attachment`
- отправка файлов должна использовать `resolve_workspace_attachment_path()`
- `platform_chat_id` должен существовать до вызова агента
---
## 9. Реализация шаг за шагом
1. Скопировать `adapter/matrix/` как шаблон для `adapter/max/`.
2. Сделать `adapter/max/converter.py`:
- превратить native Max-сообщения в `IncomingMessage`
- превратить команды в `IncomingCommand`
- превратить yes/no-подтверждения в `IncomingCallback`
3. Сделать `adapter/max/agent_registry.py` на основе `adapter/matrix/agent_registry.py`.
4. Сделать `adapter/max/files.py` на основе `adapter/matrix/files.py`.
5. Сделать `adapter/max/bot.py`:
- инстанцировать runtime
- читать env vars `MAX_*`
- загружать реестр агентов
- обрабатывать входящие события
- отправлять `Outgoing*` обратно в Max
6. Реализовать команды управления чатами и очередь вложений.
7. Прописать `config/max-agents.yaml`.
8. Прописать `docker-compose.max.yml` или аналог, чтобы surface монтировал `/agents`.
9. Написать тесты по аналогии с `tests/adapter/matrix/`.
10. Проверить, что все env vars читаются из окружения и не зависят от устаревших Matrix-переменных.
---
## 10. Важные замечания
- Текущий Matrix surface на ветке `feat/deploy` — активная реализация, а не устаревший легаси.
- Документация и код согласованы: `agent_registry`, `files`, `routed_platform`, `reconciliation` работают вместе.
- Обязательно явно задавайте `SURFACES_WORKSPACE_DIR=/agents` в production, если `workspace_path` в реестре указывает на `/agents/*`.
- Для Max surface сохраните ту же архитектуру: surface = thin adapter, агенты = внешние сервисы.
- Не пытайтесь в surface реализовывать логику запуска/стопа агент-контейнеров.
---
## 11. Полезные ссылки внутри репозитория
- `README.md`
- `docs/deploy-architecture.md`
- `docs/surface-protocol.md`
- `adapter/matrix/bot.py`
- `adapter/matrix/converter.py`
- `adapter/matrix/agent_registry.py`
- `adapter/matrix/files.py`
- `adapter/matrix/routed_platform.py`
- `adapter/matrix/reconciliation.py`
- `tests/adapter/matrix/`