# Lambda Lab 3.0 — Surfaces Команда поверхностей. Telegram и Matrix боты для взаимодействия пользователя с AI-агентом Lambda. ## Статус | Поверхность | Статус | |---|---| | Telegram | 🔨 В разработке, отдельный worktree `feat/telegram-adapter` | | Matrix | ✅ Рабочий прототип, запускается через root `docker compose` вместе с `platform-agent` | --- ## Концепция Пользователь получает персонального AI-агента через привычный мессенджер. Агент выполняет реальные задачи: разбирает почту, ищет информацию, работает с файлами, управляет календарём. **Поверхности** — тонкие клиенты. Вся бизнес-логика на стороне платформы. Задача команды: сделать интерфейс удобным, надёжным и легко расширяемым. --- ## Архитектура ``` surfaces-bot/ core/ — общее ядро, не зависит от транспорта protocol.py — унифицированные структуры (IncomingMessage, OutgoingUI, ...) handler.py — EventDispatcher: IncomingEvent → OutgoingEvent handlers/ — обработчики по типам событий store.py — StateStore Protocol + InMemoryStore + SQLiteStore chat.py — ChatManager: метаданные чатов C1/C2/C3 auth.py — AuthManager: аутентификация settings.py — SettingsManager: коннекторы, скиллы, SOUL, безопасность adapter/ telegram/ — aiogram 3.x адаптер matrix/ — matrix-nio адаптер sdk/ interface.py — PlatformClient Protocol (контракт к SDK) mock.py — MockPlatformClient (заглушка) docs/ — документация .claude/agents/ — агенты для Claude Code ``` **Ключевой принцип:** добавить новую поверхность = написать один адаптер-конвертер. Ядро (`core/`) не трогается. Подробнее: [`docs/surface-protocol.md`](docs/surface-protocol.md) --- ## Функционал прототипа ### Telegram ([подробнее](docs/telegram-prototype.md)) - **Чаты** — основной Telegram UX сейчас развивается в отдельном worktree `feat/telegram-adapter` - **Forum Topics mode** — бот умеет подключать forum-группу через `/forum`; чат может быть привязан к отдельной теме - **DM-режим** — базовый диалог и переключение чатов сохраняются - **Аутентификация** — привязка Telegram аккаунта к аккаунту платформы - **Диалог** — typing indicator, передача файлов, подтверждение опасных действий через inline-кнопки - **Настройки** через `/settings`: коннекторы (Gmail, GitHub, Notion...), скиллы, личность агента (SOUL), безопасность, подписка ### Matrix ([подробнее](docs/matrix-prototype.md)) - **Онбординг** — при первом invite бот создаёт private Space `Lambda — {display_name}` и первую комнату `Чат 1`, сразу приглашая туда пользователя - **Чаты** — `!new`, `!chats`, `!rename`, `!archive`, `!help`; новые комнаты регистрируются в локальном `ChatManager` - **Диалог** — сообщения, вложения, подтверждения `!yes` / `!no` и routing через `EventDispatcher` - **Стабильность** — перед `sync_forever()` бот делает bootstrap sync и стартует с `since`, чтобы не переигрывать старую timeline после рестарта - **Текущее ограничение** — encrypted DM официально не поддержан; ручное тестирование Matrix ведётся в незашифрованных комнатах и зависит от локального state-store бота - **Backend selection** — `MATRIX_PLATFORM_BACKEND=mock` остаётся значением по умолчанию; `MATRIX_PLATFORM_BACKEND=real` использует `platform-agent` из compose и upstream `AgentApi` по contract `/v1/agent_ws/{chat_id}/` - **Ограничения real backend** — локальный runtime использует shared `/workspace`, файлы передаются как относительные пути в `attachments`, а transport layer со стороны `surfaces` использует прямой upstream `platform-agent_api.AgentApi` без локального subclass; prod-default lifecycle открывает отдельное соединение на каждый запрос, но после tool/file flow всё ещё остаётся подтверждённый upstream streaming bug, из-за которого начало ответа может пропадать --- ## Замена SDK Вся работа с платформой идёт через `PlatformClient` Protocol: ```python class PlatformClient(Protocol): async def get_or_create_user(self, external_id: str, platform: str, ...) -> User: ... async def send_message(self, user_id: str, chat_id: str, text: str, ...) -> MessageResponse: ... async def get_settings(self, user_id: str) -> UserSettings: ... async def update_settings(self, user_id: str, action: Any) -> None: ... ``` Бот не управляет lifecycle контейнеров — это делает Master (платформа). Бот передаёт `user_id` + `chat_id` + сообщение; платформа сама решает нужно ли поднять контейнер. Сейчас: `MockPlatformClient` в `sdk/mock.py`, а Matrix real backend собирается через `sdk/real.py` при `MATRIX_PLATFORM_BACKEND=real`. Файловый контракт уже path-based: бот пишет файлы в shared `/workspace` и передаёт платформе относительные пути в `attachments`. Когда SDK готов: добавляем `SdkPlatformClient`, меняем одну строку в DI. Адаптеры и ядро не трогаем. --- ## Запуск Matrix-поверхности ### 1. Зависимости и тесты ```bash uv sync pytest tests/ -v ``` ### 2. Переменные окружения ```bash cp .env.example .env ``` Обязательные переменные: ```env # Matrix аккаунт бота MATRIX_HOMESERVER=https://matrix.example.org MATRIX_USER_ID=@lambda-bot:example.org MATRIX_PASSWORD=... # или MATRIX_ACCESS_TOKEN=... # Выбор backend: mock (по умолчанию) или real (подключение к platform-agent) MATRIX_PLATFORM_BACKEND=real MATRIX_AGENT_REGISTRY_PATH=config/matrix-agents.yaml # compose runtime: platform-agent service name + shared /workspace AGENT_BASE_URL=http://platform-agent:8000 SURFACES_WORKSPACE_DIR=/workspace # platform-agent provider PROVIDER_MODEL=openai/gpt-4o-mini PROVIDER_URL=https://openrouter.ai/api/v1 PROVIDER_API_KEY=... ``` ### 3. Registry агентов 1. Скопируй `config/matrix-agents.example.yaml` в `config/matrix-agents.yaml` 2. Укажи `MATRIX_AGENT_REGISTRY_PATH=config/matrix-agents.yaml` 3. Этот registry нужен для будущего выбора upstream-агента; сам командный переключатель `!agent` в текущем коде ещё не реализован ### 4. Compose runtime Root `docker-compose.yml` теперь является основным локальным runtime для Matrix и platform-agent. Он поднимает `matrix-bot`, `platform-agent` и общий volume `/workspace`. ```bash docker compose up --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`. На `2026-04-21` локальный compose runtime использует vendored upstream-версии платформы без локальных патчей: - `platform-agent`: `5e7c2df954cc3cd2f5bf8ae688e10a20038dde61` - `platform-agent_api`: `8a4f4db6d36786fe8af7feefffe506d4a54ac6bd` ### 4. Staged attachments в Matrix Если Matrix-клиент отправляет файлы отдельными media events, бот не вызывает агента сразу. Вместо этого он сохраняет файлы в shared `/workspace`, ставит их в очередь для конкретного чата и пользователя, и ждёт следующего обычного сообщения. Как отправить файлы агенту: 1. Отправь один или несколько файлов в рабочую Matrix-комнату. 2. При необходимости проверь очередь командой `!list`. 3. Напиши обычное текстовое сообщение, например: - `что на изображении?` - `прочитай pdf и сделай summary` - `сравни эти два файла` 4. Это сообщение уйдёт агенту вместе со всеми staged файлами из очереди. Команды: - `!list` — показать staged вложения - `!remove ` — удалить вложение по номеру - `!remove all` — очистить все staged вложения Следующее обычное сообщение пользователя уходит агенту вместе со всеми staged файлами. Пример: ```text [отправил 2 изображения] !list 1. IMG_3183.png 2. minion.jpeg что изображено на фото ``` В этом сценарии вопрос `что изображено на фото` будет отправлен агенту вместе с обоими файлами. Важно: - если после файлов отправить `!list` или `!remove`, агент не вызывается - если платформа вернула ошибку на этих вложениях, они остаются в staged-очереди - в таком случае следующее обычное сообщение снова попытается отправить те же файлы - чтобы разорвать этот цикл, используй `!remove ` или `!remove all` Известное ограничение текущего platform-agent: - большие изображения могут не пройти в provider из-за лимита на размер data URI - в таком случае Matrix-бот ответит `Сервис временно недоступен...`, а проблемные файлы останутся в очереди до явного удаления ### 5. Запуск бота вручную ```bash # Первый запуск или сброс состояния rm -f lambda_matrix.db && rm -rf matrix_store PYTHONPATH=. uv run python -m adapter.matrix.bot ``` ### 6. Онбординг пользователя Напиши боту в **личные сообщения (DM)** на Matrix-сервере. Для поддерживаемого dev-сценария используй незашифрованную комнату: E2EE сейчас не считается поддержанным режимом для Matrix-поверхности. Бот автоматически: 1. Создаст private Space `Lambda — {твоё имя}` 2. Создаст рабочую комнату `Чат 1` и пригласит туда Дальнейшее общение ведётся в рабочей комнате, не в DM. --- ## Функционал Matrix MVP ### Работает | Функция | Команда | Примечание | |---|---|---| | Онбординг | *(автоматически при invite)* | Создаёт Space + рабочую комнату | | Новый чат | `!new` | Создаёт дополнительную комнату | | Список чатов | `!chats` | Активные чаты пользователя | | Переименование | `!rename <название>` | | | Архивация | `!archive` | | | Диалог с агентом | *(любое сообщение)* | Стриминг ответа через WebSocket | | Изоляция контекста | *(автоматически)* | Каждая комната получает отдельный `platform_chat_id` | | Сохранение контекста | `!save [имя]` | Агент сохраняет краткое резюме разговора | | Список сохранений | `!load` | Выбор по номеру | | Состояние контекста | `!context` | Текущая сессия и список сохранений | | Справка | `!help` | | | Подтверждения | `!yes` / `!no` | Для опасных действий | | Staged вложения | `!list`, `!remove `, `!remove all` | Файлы без текстовой инструкции ставятся в очередь до следующего сообщения | ### Не работает — блокеры на стороне platform-agent | Функция | Почему не работает | |---|---| | `!load` в другом чате | platform-agent использует `StateBackend` — файлы живут в памяти отдельно для каждого `thread_id`. Файл, сохранённый в чате A, не виден в чате B. Фикс: переключить platform-agent на `FilesystemBackend` с общим хранилищем. | | Стриминг после tool/file flow | В текущем upstream `platform-agent` первый `MsgEventTextChunk` иногда рождается уже обрезанным до попадания в websocket-клиент. Наш transport layer после cleanup максимально близок к upstream и больше не пытается локально “лечить” этот поток. Подробности и raw evidence: `docs/reports/2026-04-22-platform-streaming-final-bug-report-ru.md`. | | Счётчик токенов в `!context` | pinned `platform-agent_api.AgentApi` потребляет `MsgEventEnd` внутри клиента и не публикует `tokens_used` наружу. Сейчас `surfaces` честно показывает `0`, пока upstream не добавит поддержанный способ получить это значение. | | `!reset` | platform-agent не имеет endpoint `/reset`. Задокументировано в ТЗ к платформе. | | Персистентность между рестартами | platform-agent использует `MemorySaver` (in-memory). Все разговоры теряются при рестарте процесса. | | E2EE комнаты | `python-olm` не собирается на macOS/ARM. Ограничение инфраструктуры. | ### Не работает — пока не реализовано нами | Функция | Статус | |---|---| | `!settings`, `!skills`, `!soul`, `!safety` | Заглушки MVP. Требуют готового SDK платформы. | | Вложения без текстовой инструкции | Поддержан staged UX только для Matrix. Для других поверхностей ещё не перенесено. | --- ## Документация | Файл | Содержание | |---|---| | [`docs/surface-protocol.md`](docs/surface-protocol.md) | Унификация поверхностей — все структуры, как добавить новую поверхность | | [`docs/telegram-prototype.md`](docs/telegram-prototype.md) | Функционал Telegram прототипа | | [`docs/matrix-prototype.md`](docs/matrix-prototype.md) | Функционал Matrix прототипа | | [`docs/api-contract.md`](docs/api-contract.md) | Контракт к SDK платформы | | [`docs/user-flow.md`](docs/user-flow.md) | FSM и user journey | | [`docs/claude-code-guide.md`](docs/claude-code-guide.md) | Гайд по работе с Claude Code | | [`docs/reports/2026-04-22-platform-streaming-final-bug-report-ru.md`](docs/reports/2026-04-22-platform-streaming-final-bug-report-ru.md) | Финальный аудит platform streaming bug после cleanup transport layer | --- ## Команда Поверхности и интеграции Lambda Lab 3.0, МАИ