From 22a3a2b60a07506b3033dc96e84698e175736a11 Mon Sep 17 00:00:00 2001 From: Mikhail Putilovskij Date: Tue, 28 Apr 2026 01:15:41 +0300 Subject: [PATCH] docs(05-04): document split deployment artifacts - document prod vs fullstack compose usage - align operator docs with shared /agents contract --- README.md | 41 ++++++++++++++++++++++++------------- docs/deploy-architecture.md | 33 ++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 24f6c36..b444948 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ | Поверхность | Статус | |---|---| | Telegram | 🔨 В разработке, отдельный worktree `feat/telegram-adapter` | -| Matrix | ✅ Рабочий прототип, запускается через root `docker compose` вместе с `platform-agent` | +| Matrix | ✅ MVP runtime: `docker-compose.prod.yml` для bot-only handoff, `docker-compose.fullstack.yml` для internal E2E | --- @@ -90,7 +90,7 @@ class PlatformClient(Protocol): Бот передаёт `user_id` + `chat_id` + сообщение; платформа сама решает нужно ли поднять контейнер. Сейчас: `MockPlatformClient` в `sdk/mock.py`, а Matrix real backend собирается через `sdk/real.py` при `MATRIX_PLATFORM_BACKEND=real`. -Файловый контракт уже path-based: бот пишет файлы в shared `/workspace` и передаёт платформе относительные пути в `attachments`. +Файловый контракт уже path-based: бот пишет файлы в shared `/agents` и передаёт платформе относительные пути в `attachments`, которые агент читает внутри своего `/workspace`. Когда SDK готов: добавляем `SdkPlatformClient`, меняем одну строку в DI. Адаптеры и ядро не трогаем. --- @@ -121,9 +121,13 @@ MATRIX_PASSWORD=... # или MATRIX_ACCESS_TOKEN=... # Выбор backend: mock (по умолчанию) или real (подключение к platform-agent) MATRIX_PLATFORM_BACKEND=real -# compose runtime: platform-agent service name + shared /workspace -AGENT_BASE_URL=http://platform-agent:8000 -SURFACES_WORKSPACE_DIR=/workspace +# production handoff: bot connects to externally managed agent endpoint +AGENT_BASE_URL=https://lambda.coredump.ru/agent_0/ +SURFACES_WORKSPACE_DIR=/agents +SURFACES_SHARED_VOLUME=surfaces-agents + +# internal full-stack compose defaults +AGENT_ID=matrix-dev # platform-agent provider PROVIDER_MODEL=openai/gpt-4o-mini @@ -137,19 +141,28 @@ PROVIDER_API_KEY=... 2. Если готовишься к multi-agent routing, добавь `MATRIX_AGENT_REGISTRY_PATH=config/matrix-agents.yaml` в `.env` 3. Этот registry сейчас является конфигурационным артефактом Task 1; текущий Matrix runtime его ещё не читает -### 4. Compose runtime +### 4. Compose artifacts -Root `docker-compose.yml` теперь является основным локальным runtime для Matrix и platform-agent. -Он поднимает `matrix-bot`, `platform-agent` и общий volume `/workspace`. +Production handoff uses `docker-compose.prod.yml`. +Этот файл поднимает только `matrix-bot`, монтирует shared volume в `/agents` и ожидает, что `AGENT_BASE_URL` +указывает на уже управляемый внешней платформой agent endpoint. ```bash -docker compose up --build +docker compose --env-file .env -f docker-compose.prod.yml up -d --build ``` -Compose собирает `platform-agent` из актуального upstream `external/platform-agent` Dockerfile (`development` target), -монтирует live-код из `external/platform-agent/src` и `external/platform-agent_api`, и подготавливает shared `/workspace` -с правами для agent runtime. -Matrix бот подключается к `platform-agent` по service name, а не к отдельно запущенному `localhost`. +Internal full-stack E2E uses `docker-compose.fullstack.yml`. +Этот файл поднимает `matrix-bot` вместе с локальным `platform-agent`, использует тот же shared volume +(`SURFACES_SHARED_VOLUME`) и ждёт `service_healthy` вместо sleep-based sequencing. + +```bash +docker compose --env-file .env -f docker-compose.fullstack.yml up --build +``` + +`docker-compose.fullstack.yml` собирает `platform-agent` из актуального upstream `external/platform-agent` +(`development` target), монтирует live-код из `external/platform-agent/src` и `external/platform-agent_api`, +а shared volume виден как `/agents` в bot container и как `/workspace` в `platform-agent`. +Старый root compose harness остаётся только как historical local reference и больше не является рекомендуемым runtime path. На `2026-04-21` локальный compose runtime использует vendored upstream-версии платформы без локальных патчей: @@ -159,7 +172,7 @@ Matrix бот подключается к `platform-agent` по service name, а ### 4. Staged attachments в Matrix Если Matrix-клиент отправляет файлы отдельными media events, бот не вызывает агента сразу. -Вместо этого он сохраняет файлы в shared `/workspace`, ставит их в очередь для конкретного чата и пользователя, и ждёт следующего обычного сообщения. +Вместо этого он сохраняет файлы в shared `/agents`, ставит их в очередь для конкретного чата и пользователя, и ждёт следующего обычного сообщения. Как отправить файлы агенту: diff --git a/docs/deploy-architecture.md b/docs/deploy-architecture.md index 8746e56..3ac891a 100644 --- a/docs/deploy-architecture.md +++ b/docs/deploy-architecture.md @@ -4,6 +4,18 @@ --- +## Compose Artifacts + +- **Production deploy:** `docker-compose.prod.yml` + Bot-only handoff. Поднимает только `matrix-bot`, монтирует shared volume в `/agents`, требует внешний `AGENT_BASE_URL`. +- **Internal full-stack E2E:** `docker-compose.fullstack.yml` + Внутренний harness. Поднимает `matrix-bot` и `platform-agent`, использует тот же volume name и health-gated startup через `condition: service_healthy`. + +Production operators should run the bot with `docker-compose.prod.yml`; internal verification should use `docker-compose.fullstack.yml`. +Старый root compose harness больше не является primary runtime contract для Phase 05. + +--- + ## Топология ``` @@ -22,7 +34,7 @@ lambda.coredump.ru - **Один инстанс Matrix-бота** обслуживает всех пользователей. - **Один агент-контейнер на пользователя.** Изоляция по agent_id, не через chat_id внутри одного инстанса. -- **Shared volume** `/agents/` смонтирован и в Matrix-бот, и в каждый агент-контейнер. Агент видит свой подкаталог как `/workspace`. +- **Shared volume** `/agents/` смонтирован в Matrix-бот. В internal full-stack harness тот же volume mounted as `/workspace` inside `platform-agent`, чтобы bot-side absolute paths и agent workspace относились к одному и тому же хранилищу. --- @@ -58,24 +70,25 @@ agents: ```python from lambda_agent_api.agent_api import AgentApi -connected_agents: dict[str, AgentApi] = {} +connected_agents: dict[tuple[str, int], AgentApi] = {} def on_agent_disconnect(agent: AgentApi): - del connected_agents[agent.id] + connected_agents.pop((agent.id, agent.chat_id), None) -async def on_message(matrix_user_id: str, text: str): +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) + 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=0, # default, один чат на агента + chat_id=platform_chat_id, # отдельный thread на Matrix room ) await agent.connect() - connected_agents[agent_id] = agent + connected_agents[(agent_id, platform_chat_id)] = agent async for event in agent.send_message(text): ... @@ -86,7 +99,7 @@ async def on_message(matrix_user_id: str, text: str): AgentApi( agent_id: str, base_url: str, # ws://host:port/agent_N/ - chat_id: int = 0, # default — один чат на агента + chat_id: int = 0, # surfaces must supply per-room platform_chat_id on_disconnect: callable, ) ``` @@ -111,7 +124,7 @@ AgentApi( 2. Matrix-бот читает файл: `/agents/{N}/output/report.pdf` 3. Отправляет как Matrix file message пользователю -**Ключевое:** поверхность видит `/agents/` целиком через shared volume. Прямой HTTP-доступ к файлам не нужен. +**Ключевое:** production handoff через `docker-compose.prod.yml` и internal E2E через `docker-compose.fullstack.yml` используют один и тот же `/agents` contract на стороне поверхности. Прямой HTTP-доступ к файлам не нужен. --- @@ -140,6 +153,6 @@ AgentApi( ## Что НЕ решено / открытые вопросы - Ветка `platform-agent_api #9-clientside-tool-call` убирает `attachments` и `MsgEventSendFile` — пока игнорируем, используем master. Уточнить у Азамата сроки мержа перед деплоем. -- `chat_id` — при нашей модели C1/C2/C3 каждый чат должен иметь отдельный `chat_id`. Нужно решить: один `AgentApi` на агента (chat_id=0) или по инстансу на чат (chat_id=1/2/3). Пока берём `chat_id=0` (один контекст на пользователя). +- `chat_id` — каждый Matrix chat room должен иметь собственный `platform_chat_id`. `!clear` должен ротировать `platform_chat_id` только для текущей комнаты, чтобы получить новый thread и чистый контекст без смены Matrix room. - Composio `AGENT_ID` в `.env` для каждого агента — уточнить у платформы значения. - Что происходит с историей при рестарте агента — `MemorySaver` не персистентный.