9.3 KiB
Deployment Architecture — Matrix Bot + Agents
Сформировано 2026-04-27 по итогам обсуждения с платформой.
Compose Artifacts
- Production deploy:
docker-compose.prod.ymlBot-only handoff через published image (SURFACES_BOT_IMAGE). Поднимает толькоmatrix-bot, монтирует shared volume в/agents. Платформа предоставляет 25-30 agent containers/services отдельно; бот подключается к ним черезbase_urlизmatrix-agents.yaml. - Internal full-stack E2E:
docker-compose.fullstack.ymlВнутренний harness для тестирования. Локально собираетmatrix-botчерез Dockerfile targetdevelopment, поднимает одинplatform-agent, health-gated startup.
Production operators: docker-compose.prod.yml. Internal E2E: docker-compose.fullstack.yml.
Топология
lambda.coredump.ru
├── :7000 (reverse proxy, path-based routing)
│ ├── /agent_0/ → agent_0 container
│ ├── /agent_1/ → agent_1 container
│ └── /agent_N/ → agent_N container
│
└── Matrix bot instance (один инстанс на всех)
└── volume /agents/ (shared с агентами)
├── /agents/0/ ← workspace agent_0
├── /agents/1/ ← workspace agent_1
└── /agents/N/
- Один инстанс Matrix-бота обслуживает всех пользователей.
- Один агент-контейнер на пользователя. Production scale target: 25-30 внешних агентов. Изоляция по
agent_id, не через один общий agent instance. - Shared volume
/agents/смонтирован в Matrix-бот. В internal full-stack harness тот же volume mounted as/workspaceinsideplatform-agent, чтобы bot-side absolute paths и agent workspace относились к одному и тому же хранилищу.
Конфиг (два словаря)
# config/matrix-agents.yaml
user_agents:
"@user0:matrix.lambda.coredump.ru": agent-0
"@user1:matrix.lambda.coredump.ru": agent-1
"@user2:matrix.lambda.coredump.ru": agent-2
agents:
- id: agent-0
label: "Agent 0"
base_url: "http://lambda.coredump.ru:7000/agent_0/"
workspace_path: "/agents/0"
- id: agent-1
label: "Agent 1"
base_url: "http://lambda.coredump.ru:7000/agent_1/"
workspace_path: "/agents/1"
- id: agent-2
label: "Agent 2"
base_url: "http://lambda.coredump.ru:7000/agent_2/"
workspace_path: "/agents/2"
user_agents— маппинг Matrix user_id → agent_id. Если пользователь не найден — используется первый агент из списка.agents[].base_url— HTTP URL агент-эндпоинта. Бот подключается через AgentApi.agents[].workspace_path— абсолютный путь к воркспейсу агента внутри контейнера бота (т.е. на shared volume). Бот сохраняет входящие файлы прямо в{workspace_path}/, читает исходящие из{workspace_path}/.- Для 25-30 агентов продолжайте тот же паттерн до нужного номера:
/agent_17/+/agents/17,/agent_29/+/agents/29.
Surface Image Build Contract
Production image содержит только Matrix surface. Он не содержит platform-agent и не требует локального external/ build context.
docker login
export SURFACES_BOT_IMAGE=mput1/surfaces-bot:latest
docker build --target production \
--build-arg LAMBDA_AGENT_API_REF=master \
-t "$SURFACES_BOT_IMAGE" .
docker push "$SURFACES_BOT_IMAGE"
Published image:
mput1/surfaces-bot:latest
sha256:2f135f3535f7765d4377b440cdabe41195ad2efbc3e175def159ae4689ef90bd
SURFACES_BOT_IMAGE должен указывать на registry namespace, куда текущий Docker account может пушить. Ошибка insufficient_scope означает, что пользователь не залогинен в этот namespace, repository не создан, или у аккаунта нет push-доступа.
Production Dockerfile ставит platform/agent_api из Git по тому же принципу, что и platform-agent production image:
git+https://git.lambda.coredump.ru/platform/agent_api.git
Локальный docker-compose.fullstack.yml остаётся dev/E2E harness: он использует target development и additional_contexts.agent_api=./external/platform-agent_api, чтобы можно было тестировать surface вместе с локальным checkout SDK.
Agent API (используем master ветку platform/agent_api)
from lambda_agent_api.agent_api import AgentApi
connected_agents: dict[tuple[str, int], AgentApi] = {}
def on_agent_disconnect(agent: AgentApi):
connected_agents.pop((agent.id, agent.chat_id), None)
async def on_message(matrix_user_id: str, matrix_room_id: str, text: str):
agent_id = get_agent_id_by_user(matrix_user_id) # из user_agents конфига
platform_chat_id = get_room_platform_chat_id(matrix_room_id)
agent = connected_agents.get((agent_id, platform_chat_id))
if not agent:
agent = AgentApi(
agent_id,
get_agent_base_url(agent_id), # ws://lambda.coredump.ru:7000/agent_0/
on_disconnect=on_agent_disconnect,
chat_id=platform_chat_id, # отдельный thread на Matrix room
)
await agent.connect()
connected_agents[(agent_id, platform_chat_id)] = agent
async for event in agent.send_message(text):
...
Параметры конструктора (master):
AgentApi(
agent_id: str,
base_url: str, # ws://host:port/agent_N/
chat_id: int = 0, # surfaces must supply per-room platform_chat_id
on_disconnect: callable,
)
Lifecycle: агент автоматически отключается после нескольких минут бездействия.
on_disconnect удаляет из пула → следующее сообщение создаёт новое соединение.
Передача файлов
Пользователь → Агент (входящий файл)
- Matrix-бот получает файл от пользователя
- Сохраняет в workspace агента:
/agents/{N}/{filename} - Если файл уже существует, выбирает следующее имя:
filename (1).ext,filename (2).ext - Вызывает
agent.send_message(text, attachments=["filename"])— путь относительно/workspaceагента
Агент → Пользователь (исходящий файл)
- Агент эмитит
MsgEventSendFile(path="report.pdf") - Matrix-бот читает файл:
/agents/{N}/report.pdf - Отправляет как Matrix file message пользователю
Ключевое: production handoff через docker-compose.prod.yml и internal E2E через docker-compose.fullstack.yml используют один и тот же /agents contract на стороне поверхности. Прямой HTTP-доступ к файлам не нужен.
Текущее состояние platform-agent (main)
- Composio интегрирован в main (
#9-интеграция-composIO) - Агент требует в
.env:AGENT_ID,COMPOSIO_API_KEY - Backend:
IsolatedShellBackend(main) /CompositeBackend(ветка#19, не merged) - Memory:
MemorySaver— история слетает при рестарте контейнера (known limitation)
platform-master (будущее, пока не используем)
Ветка feat/storage реализует реальный Master-сервис:
POST /api/v1/create {chat_id}→ поднимает/переиспользует sandbox-контейнер- TTL-based lifecycle (300с default, конфигурируемо)
ChatStorage— API для upload/download файлов через Master- Auth + p2p lease — вне текущего scope MVP
Для деплоя MVP используем статический конфиг без Master.
При готовности Master: get_agent_url() будет вызывать POST /api/v1/create, URL возвращается в ответе.
Открытые вопросы
platform-agent_api #9-clientside-tool-callубираетattachmentsиMsgEventSendFile— пока используем master. Уточнить у платформы сроки мержа перед деплоем.- История при рестарте агента теряется —
platform-agentиспользуетMemorySaver(in-memory). Ограничение платформы. AGENT_IDиCOMPOSIO_API_KEYдля каждого агент-контейнера — значения предоставляет платформа.