From 6fbd6348f5c75d49bf9d1677f2296d770d3b7485 Mon Sep 17 00:00:00 2001 From: Markov Date: Fri, 27 Feb 2026 06:44:57 +0100 Subject: [PATCH] =?UTF-8?q?OpenClaw=20architecture=20review=20=E2=80=94=20?= =?UTF-8?q?what=20to=20borrow=20for=20Team=20Board=20agents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OPENCLAW-ARCHITECTURE-REVIEW.md | 166 ++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 OPENCLAW-ARCHITECTURE-REVIEW.md diff --git a/OPENCLAW-ARCHITECTURE-REVIEW.md b/OPENCLAW-ARCHITECTURE-REVIEW.md new file mode 100644 index 0000000..f770279 --- /dev/null +++ b/OPENCLAW-ARCHITECTURE-REVIEW.md @@ -0,0 +1,166 @@ +# Обзор архитектуры 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