docs(05-04): document split deployment artifacts

- document prod vs fullstack compose usage
- align operator docs with shared /agents contract
This commit is contained in:
Mikhail Putilovskij 2026-04-28 01:15:41 +03:00
parent 85e2fda6bc
commit 22a3a2b60a
2 changed files with 50 additions and 24 deletions

View file

@ -7,7 +7,7 @@
| Поверхность | Статус | | Поверхность | Статус |
|---|---| |---|---|
| Telegram | 🔨 В разработке, отдельный worktree `feat/telegram-adapter` | | 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` + сообщение; платформа сама решает нужно ли поднять контейнер. Бот передаёт `user_id` + `chat_id` + сообщение; платформа сама решает нужно ли поднять контейнер.
Сейчас: `MockPlatformClient` в `sdk/mock.py`, а Matrix real backend собирается через `sdk/real.py` при `MATRIX_PLATFORM_BACKEND=real`. Сейчас: `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. Адаптеры и ядро не трогаем. Когда SDK готов: добавляем `SdkPlatformClient`, меняем одну строку в DI. Адаптеры и ядро не трогаем.
--- ---
@ -121,9 +121,13 @@ MATRIX_PASSWORD=... # или MATRIX_ACCESS_TOKEN=...
# Выбор backend: mock (по умолчанию) или real (подключение к platform-agent) # Выбор backend: mock (по умолчанию) или real (подключение к platform-agent)
MATRIX_PLATFORM_BACKEND=real MATRIX_PLATFORM_BACKEND=real
# compose runtime: platform-agent service name + shared /workspace # production handoff: bot connects to externally managed agent endpoint
AGENT_BASE_URL=http://platform-agent:8000 AGENT_BASE_URL=https://lambda.coredump.ru/agent_0/
SURFACES_WORKSPACE_DIR=/workspace SURFACES_WORKSPACE_DIR=/agents
SURFACES_SHARED_VOLUME=surfaces-agents
# internal full-stack compose defaults
AGENT_ID=matrix-dev
# platform-agent provider # platform-agent provider
PROVIDER_MODEL=openai/gpt-4o-mini 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` 2. Если готовишься к multi-agent routing, добавь `MATRIX_AGENT_REGISTRY_PATH=config/matrix-agents.yaml` в `.env`
3. Этот registry сейчас является конфигурационным артефактом Task 1; текущий Matrix runtime его ещё не читает 3. Этот registry сейчас является конфигурационным артефактом Task 1; текущий Matrix runtime его ещё не читает
### 4. Compose runtime ### 4. Compose artifacts
Root `docker-compose.yml` теперь является основным локальным runtime для Matrix и platform-agent. Production handoff uses `docker-compose.prod.yml`.
Он поднимает `matrix-bot`, `platform-agent` и общий volume `/workspace`. Этот файл поднимает только `matrix-bot`, монтирует shared volume в `/agents` и ожидает, что `AGENT_BASE_URL`
указывает на уже управляемый внешней платформой agent endpoint.
```bash ```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), Internal full-stack E2E uses `docker-compose.fullstack.yml`.
монтирует live-код из `external/platform-agent/src` и `external/platform-agent_api`, и подготавливает shared `/workspace` Этот файл поднимает `matrix-bot` вместе с локальным `platform-agent`, использует тот же shared volume
с правами для agent runtime. (`SURFACES_SHARED_VOLUME`) и ждёт `service_healthy` вместо sleep-based sequencing.
Matrix бот подключается к `platform-agent` по service name, а не к отдельно запущенному `localhost`.
```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-версии платформы без локальных патчей: На `2026-04-21` локальный compose runtime использует vendored upstream-версии платформы без локальных патчей:
@ -159,7 +172,7 @@ Matrix бот подключается к `platform-agent` по service name, а
### 4. Staged attachments в Matrix ### 4. Staged attachments в Matrix
Если Matrix-клиент отправляет файлы отдельными media events, бот не вызывает агента сразу. Если Matrix-клиент отправляет файлы отдельными media events, бот не вызывает агента сразу.
Вместо этого он сохраняет файлы в shared `/workspace`, ставит их в очередь для конкретного чата и пользователя, и ждёт следующего обычного сообщения. Вместо этого он сохраняет файлы в shared `/agents`, ставит их в очередь для конкретного чата и пользователя, и ждёт следующего обычного сообщения.
Как отправить файлы агенту: Как отправить файлы агенту:

View file

@ -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-бота** обслуживает всех пользователей. - **Один инстанс Matrix-бота** обслуживает всех пользователей.
- **Один агент-контейнер на пользователя.** Изоляция по agent_id, не через chat_id внутри одного инстанса. - **Один агент-контейнер на пользователя.** Изоляция по 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 ```python
from lambda_agent_api.agent_api import AgentApi 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): 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 конфига 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: if not agent:
agent = AgentApi( agent = AgentApi(
agent_id, agent_id,
get_agent_base_url(agent_id), # ws://lambda.coredump.ru:7000/agent_0/ get_agent_base_url(agent_id), # ws://lambda.coredump.ru:7000/agent_0/
on_disconnect=on_agent_disconnect, on_disconnect=on_agent_disconnect,
chat_id=0, # default, один чат на агента chat_id=platform_chat_id, # отдельный thread на Matrix room
) )
await agent.connect() await agent.connect()
connected_agents[agent_id] = agent connected_agents[(agent_id, platform_chat_id)] = agent
async for event in agent.send_message(text): async for event in agent.send_message(text):
... ...
@ -86,7 +99,7 @@ async def on_message(matrix_user_id: str, text: str):
AgentApi( AgentApi(
agent_id: str, agent_id: str,
base_url: str, # ws://host:port/agent_N/ 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, on_disconnect: callable,
) )
``` ```
@ -111,7 +124,7 @@ AgentApi(
2. Matrix-бот читает файл: `/agents/{N}/output/report.pdf` 2. Matrix-бот читает файл: `/agents/{N}/output/report.pdf`
3. Отправляет как Matrix file message пользователю 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. Уточнить у Азамата сроки мержа перед деплоем. - Ветка `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` для каждого агента — уточнить у платформы значения. - Composio `AGENT_ID` в `.env` для каждого агента — уточнить у платформы значения.
- Что происходит с историей при рестарте агента — `MemorySaver` не персистентный. - Что происходит с историей при рестарте агента — `MemorySaver` не персистентный.