# Обзор архитектуры OpenClaw — что можно позаимствовать ## 1. Agent Loop (Agentic Loop) **Как работает в OpenClaw:** - Gateway получает сообщение → резолвит сессию → запускает `runEmbeddedPiAgent` - Runs сериализуются per-session (queue) — нет race conditions - Pi Agent Core внутри: `session.prompt()` → LLM → tool calls → results → LLM → ... - События стримятся: `text_delta`, `tool_execution_start/end`, `message_end`, `agent_end` - Timeout enforcement — если агент завис, run абортится **Что забрать:** - ✅ **Run queue per session** — сериализация запросов, нет параллельных runs в одной сессии - ✅ **Timeout на run** — агент не может зависнуть навечно - ✅ **Event streaming** — стримить text deltas, tool calls, reasoning в реальном времени --- ## 2. Память (Memory) **Как работает в OpenClaw:** - Два слоя: `memory/YYYY-MM-DD.md` (daily, append-only) + `MEMORY.md` (curated long-term) - **Memory flush перед компакцией** — silent agentic turn: "запиши важное прежде чем контекст сожмётся" - **Semantic search** (memory_search) — hybrid BM25 + vector, по чанкам ~400 токенов - **memory_get** — точечное чтение файла по path + line range - Агент **сам пишет** в файлы через read/write/edit tools **Что забрать:** - ✅ **Memory flush перед компакцией** — критично для long-running агентов - ✅ **Semantic search** — когда памяти станет много (>50K chars), нужен поиск - ✅ **Двухуровневая память** — уже решили (agent.md + projects/{uuid}/) - 🔜 **Vector search** — отложить до масштаба --- ## 3. Контекстное окно и компакция **Как работает в OpenClaw:** - Каждая модель имеет `contextWindow` (макс токенов) - **Auto-compaction**: когда сессия близка к лимиту → суммаризация старых сообщений → compact entry - **Session pruning**: обрезка старых tool results (in-memory, не трогает JSONL) - **Cache-TTL pruning** (Anthropic): если кэш протух — обрезать перед re-cache - `reserveTokensFloor` — всегда оставлять N токенов для ответа **Компакция:** 1. Определить что session близка к лимиту (token estimate) 2. Запустить memory flush (silent turn — "запиши важное") 3. Суммаризировать старые сообщения через LLM → compact summary 4. Заменить старые сообщения на summary в истории 5. Персистить в JSONL **Что забрать:** - ✅ **Auto-compaction** — обязательно для long-running агентов - ✅ **Token estimation** — считать примерный размер контекста перед каждым вызовом - ✅ **Reserve tokens** — не заполнять окно до упора - ✅ **Memory flush trigger** — перед компакцией напоминать агенту записать важное - 🔜 **Session pruning** — обрезка tool results (для Anthropic cache optimization) --- ## 4. Streaming и доставка ответов **Как работает в OpenClaw:** - **Block streaming** — отправка завершённых блоков текста по мере генерации - **Token streaming** (Telegram) — обновление draft-bubble частичным текстом - **Reasoning streaming** — отдельный поток для thinking/reasoning блоков - Chunking: min/max chars, break preference (text_end vs message_end) **Что забрать для Team Board:** - ✅ **Streaming через WS** — агент стримит text deltas через WebSocket - ✅ **Reasoning blocks** — отдельный тип сообщения `thinking` в WS protocol - ✅ **Block replies** — отправлять частичные ответы (не ждать завершения) ### Как реализовать в Team Board: **Новые WS event types:** ``` agent.stream.start → агент начал генерацию agent.stream.delta → { text: "частичный текст", type: "text"|"thinking" } agent.stream.tool → { tool: "update_task", status: "running"|"done" } agent.stream.end → генерация завершена, финальное сообщение ``` **Frontend:** - Показывать typing indicator + streaming text в ChatPanel - Раскрывающийся блок `
` для thinking/reasoning - Tool calls показывать как "🔧 Обновляет задачу..." inline --- ## 5. Ответ агента через WS (не REST) **Текущая проблема:** агент вызывает REST `POST /messages` чтобы ответить → broadcast через WS. Лишний round-trip. **Как должно быть:** 1. Tracker получает message.new → отправляет агенту через WS 2. Агент генерирует ответ → стримит через WS (`agent.stream.delta`) 3. Tracker принимает `agent.reply` через WS → сохраняет в БД → broadcast всем 4. Или: агент отправляет `chat.send` через WS (уже поддерживается!) **Минимальное изменение:** - Router при auto-reply использует WS `chat.send` вместо REST POST - Для explicit send_message tool — тоже через WS - REST send_message остаётся для внешних клиентов --- ## 6. Что НЕ нужно забирать - ❌ **Multi-channel routing** (WhatsApp, Telegram, Discord) — у нас один канал (WebSocket) - ❌ **Node/device pairing** — не наш use case - ❌ **Canvas/A2UI** — пока не нужно - ❌ **OAuth proxy** — решается через litellm или провайдер SDK - ❌ **Plugin system** — оверинжиниринг для наших масштабов --- ## 7. Roadmap заимствований ### Phase 1 (сейчас) - [ ] Agent отвечает через WS вместо REST - [ ] Streaming text deltas через WS events - [ ] Reasoning/thinking block в UI (раскрывающийся) ### Phase 2 (скоро) - [ ] Auto-compaction для длинных сессий - [ ] Memory flush перед компакцией - [ ] Token estimation (считать размер контекста) - [ ] Run timeout (kill зависших агентов) ### Phase 3 (позже) - [ ] Semantic memory search (vector + BM25) - [ ] Session pruning (обрезка tool results) - [ ] Block streaming (частичные ответы до завершения) --- ## 8. Thinking/Reasoning в UI **Реализация раскрывающегося блока:** **Backend (Tracker WS protocol):** ```json {"type": "agent.stream.delta", "data": {"agent_id": "...", "block_type": "thinking", "text": "Думаю..."}} {"type": "agent.stream.delta", "data": {"agent_id": "...", "block_type": "text", "text": "Ответ"}} ``` **Frontend (ChatPanel):** ```tsx {message.thinking && (
💭 Размышления агента
{message.thinking}
)} ``` **Хранение в БД:** - Поле `thinking` в Message модели (nullable text) - Или отдельная таблица reasoning_blocks - Проще: JSON поле `metadata` с ключом `thinking` **Что нужно от LLM:** - Anthropic: `thinking` parameter в API request → возвращает `thinking` блоки - OpenAI: `reasoning_effort` → скрытый reasoning (не доступен) - Только Anthropic (Claude) реально отдаёт thinking content