16 KiB
Руководство по созданию новой поверхности
Этот документ описывает, как написать новую новую поверхность (например, 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.yamladapter/<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.
Структура:
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_idagents— список агентов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.pyreconcile_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.orgMATRIX_PASSWORDилиMATRIX_ACCESS_TOKENMATRIX_PLATFORM_BACKEND— должно бытьrealдля продакшнаMATRIX_AGENT_REGISTRY_PATH— путь кconfig/matrix-agents.yamlAGENT_BASE_URL— fallback URL агентаSURFACES_WORKSPACE_DIR— путь к shared volume внутри контейнера (по умолчанию/workspaceв коде, но в docs рекомендуют/agents)
Для New surface используйте аналогичные переменные:
PLATFORM_PLATFORM_BACKEND=realPLATFORM_AGENT_REGISTRY_PATH=/app/config/<platform>-agents.yamlSURFACES_WORKSPACE_DIR=/agentsAGENT_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.pytests/adapter/matrix/test_dispatcher.pytests/adapter/matrix/test_routed_platform.pytests/adapter/matrix/test_reconciliation.pytests/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. Реализация шаг за шагом
- Скопировать
adapter/matrix/как шаблон дляadapter/<platform>/. - Сделать
adapter/<platform>/converter.py:- превратить native нативные сообщения в
IncomingMessage - превратить команды в
IncomingCommand - превратить yes/no-подтверждения в
IncomingCallback
- превратить native нативные сообщения в
- Сделать
adapter/<platform>/agent_registry.pyна основеadapter/matrix/agent_registry.py. - Сделать
adapter/<platform>/files.pyна основеadapter/matrix/files.py. - Сделать
adapter/<platform>/bot.py:- инстанцировать runtime
- читать env vars
PLATFORM_* - загружать реестр агентов
- обрабатывать входящие события
- отправлять
Outgoing*обратно в Платформа
- Реализовать команды управления чатами и очередь вложений.
- Прописать
config/<platform>-agents.yaml. - Прописать
docker-compose.platform.ymlили аналог, чтобы surface монтировал/agents. - Написать тесты по аналогии с
tests/adapter/matrix/. - Проверить, что все 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.mddocs/deploy-architecture.mddocs/surface-protocol.mdadapter/matrix/bot.pyadapter/matrix/converter.pyadapter/matrix/agent_registry.pyadapter/matrix/files.pyadapter/matrix/routed_platform.pyadapter/matrix/reconciliation.pytests/adapter/matrix/