# Руководство по созданию новой поверхности Этот документ описывает, как написать новую новую поверхность (например, Discord, Slack или Custom Web) по образцу текущей Matrix-поверхности в ветке `main`. Он основан на актуальной реализации Matrix surface в репозитории и отражает текущую продакшн-логику, а не устаревший легаси. --- ## 1. Общая архитектура ### 1.1. Что такое поверхность Поверхность — это тонкий адаптер между конкретной платформой (Платформа) и общим ядром бота. В репозитории есть разделение: - `core/` — общее ядро и бизнес-логика - `adapter//` — реализация конкретной поверхности - `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// bot.py converter.py agent_registry.py files.py handlers/ store.py ``` ### 2.2. Принцип reuse По примеру Matrix surface, New surface должен переиспользовать общий `core` и общий `sdk`. Не дублируйте бизнес-логику, а реализуйте только адаптер: - `adapter//converter.py` — конвертация событий платформы ⇄ внутренние структуры - `adapter//bot.py` — основной runtime, старт Платформа client, loop, отправка/прием - `adapter//agent_registry.py` — загрузка `config/-agents.yaml` - `adapter//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/-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 ` / `!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/-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/-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//`. 2. Сделать `adapter//converter.py`: - превратить native нативные сообщения в `IncomingMessage` - превратить команды в `IncomingCommand` - превратить yes/no-подтверждения в `IncomingCallback` 3. Сделать `adapter//agent_registry.py` на основе `adapter/matrix/agent_registry.py`. 4. Сделать `adapter//files.py` на основе `adapter/matrix/files.py`. 5. Сделать `adapter//bot.py`: - инстанцировать runtime - читать env vars `PLATFORM_*` - загружать реестр агентов - обрабатывать входящие события - отправлять `Outgoing*` обратно в Платформа 6. Реализовать команды управления чатами и очередь вложений. 7. Прописать `config/-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/`