surfaces/docs/new-surface-guide.md

313 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Руководство по созданию новой поверхности
Этот документ описывает, как написать новую новую поверхность (например, 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. Локальное тестирование
Для тестирования новой поверхности вместе с одним локальным агентом используйте паттерн `docker-compose.fullstack.yml`.
В этом режиме:
- Запускается 1 контейнер вашей поверхности
- Запускается 1 контейнер `platform-agent`
- Поднимается локальный shared volume (`surfaces-agents`)
- Поверхность настроена маршрутизировать запросы на `http://platform-agent:8000` (через `AGENT_BASE_URL`)
- Пользователь общается с ботом, а бот напрямую общается с локальным агентом, разделяя с ним общую папку для файлов.
Это самый быстрый способ проверить интеграцию новой платформы без внешнего бэкенда.
---
## 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/`