docs: generalize new surface guide and clean up legacy docs

This commit is contained in:
Mikhail Putilovskij 2026-05-03 00:01:25 +03:00
parent 0f79494fbe
commit e7e3912b5f
9 changed files with 59 additions and 433 deletions

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

@ -0,0 +1,340 @@
# Руководство по созданию новой поверхности
Этот документ описывает, как написать новую новую поверхность (например, Discord, Slack или Custom Web) по образцу текущей Matrix-поверхности в ветке `main`.
Он основан на актуальной реализации Matrix surface в репозитории и отражает текущую продакшн-логику, а не устаревший легаси.
---
## 1. Общая архитектура
### 1.1. Что такое поверхность
Поверхность — это тонкий адаптер между конкретной платформой (Платформа) и общим ядром бота.
В репозитории есть разделение:
- `core/` — общее ядро и бизнес-логика
- `adapter/<platform>/` — реализация конкретной поверхности
- `sdk/real.py` — работа с реальной платформой / агентом
- `config/` — статическая конфигурация агентов
- `docs/surface-protocol.md` — общий контракт поверхностей
### 1.2. Как это работает
Поверхность должна:
- принимать нативные события от Платформа
- преобразовывать их в единый внутренний контракт (`IncomingMessage`, `IncomingCommand`, `IncomingCallback`)
- передавать их в `core`
- получать ответы из `core` (`OutgoingMessage`, `OutgoingUI`, `OutgoingTyping`, `OutgoingNotification`)
- преобразовывать ответы обратно в нативные нативные сообщения
Поверхность не должна:
- управлять жизненным циклом агентских контейнеров
- хранить долгую историю бесед вне `core`/платформы
- аутентифицировать пользователей сама (если это не часть Платформа API)
---
## 2. Структура новой поверхности
### 2.1. Основные каталоги
Рекомендуемая структура для новой платформы:
```
adapter/<platform>/
bot.py
converter.py
agent_registry.py
files.py
handlers/
store.py
```
### 2.2. Принцип reuse
По примеру Matrix surface, New surface должен переиспользовать общий `core` и общий `sdk`.
Не дублируйте бизнес-логику, а реализуйте только адаптер:
- `adapter/<platform>/converter.py` — конвертация событий платформы ⇄ внутренние структуры
- `adapter/<platform>/bot.py` — основной runtime, старт Платформа client, loop, отправка/прием
- `adapter/<platform>/agent_registry.py` — загрузка `config/<platform>-agents.yaml`
- `adapter/<platform>/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`
Для Платформа реализуйте аналогичную логику для 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. Рекомендуемая Версия для новой платформы
Создайте `config/<platform>-agents.yaml` с тем же смыслом.
- `user_agents` — маппинг external 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`
Для New surface тот же принцип:
- каждая внешняя беседа должна привязываться к одному внутреннему `chat_id`
- этот `chat_id` используется для вызовов агента
- если в Платформа есть несколько комнат/топиков, каждая должна иметь свой `surface_ref`
### 6.2. Команды управления чатами
Matrix поддерживает следующие команды, которые нужно сохранить в Платформа:
- `!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
- следующий текст отправляется агенту вместе со всеми файлами из очереди
В Платформа можно реализовать ту же модель:
- `!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`)
Для New surface используйте аналогичные переменные:
- `PLATFORM_PLATFORM_BACKEND=real`
- `PLATFORM_AGENT_REGISTRY_PATH=/app/config/<platform>-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`
В New surface реализуйте ту же логику, заменив префиксы на `PLATFORM_`.
---
## 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`
Для Платформа создайте аналогичные тесты:
- проверка загрузки вложений
- проверка маршрутизации по `agent_id`
- проверка восстановления `platform_chat_id`
- проверка конвертации команд
### 8.2. Smoke-проверка deployment
Для Matrix surface есть `docker-compose.prod.yml` и `docker-compose.fullstack.yml`.
Для New surface должно быть достаточно:
- bot-only production deployment
- shared volume `/agents`
- независимая проверка `config/<platform>-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/<platform>/`.
2. Сделать `adapter/<platform>/converter.py`:
- превратить native нативные сообщения в `IncomingMessage`
- превратить команды в `IncomingCommand`
- превратить yes/no-подтверждения в `IncomingCallback`
3. Сделать `adapter/<platform>/agent_registry.py` на основе `adapter/matrix/agent_registry.py`.
4. Сделать `adapter/<platform>/files.py` на основе `adapter/matrix/files.py`.
5. Сделать `adapter/<platform>/bot.py`:
- инстанцировать runtime
- читать env vars `PLATFORM_*`
- загружать реестр агентов
- обрабатывать входящие события
- отправлять `Outgoing*` обратно в Платформа
6. Реализовать команды управления чатами и очередь вложений.
7. Прописать `config/<platform>-agents.yaml`.
8. Прописать `docker-compose.platform.yml` или аналог, чтобы surface монтировал `/agents`.
9. Написать тесты по аналогии с `tests/adapter/matrix/`.
10. Проверить, что все env vars читаются из окружения и не зависят от устаревших Matrix-переменных.
---
## 10. Важные замечания
- Текущий Matrix surface на ветке `main` — активная реализация, а не устаревший легаси.
- Документация и код согласованы: `agent_registry`, `files`, `routed_platform`, `reconciliation` работают вместе.
- Обязательно явно задавайте `SURFACES_WORKSPACE_DIR=/agents` в production, если `workspace_path` в реестре указывает на `/agents/*`.
- Для New 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/`