OpenClaw architecture review — what to borrow for Team Board agents

This commit is contained in:
Markov 2026-02-27 06:44:57 +01:00
parent 10babfce66
commit 6fbd6348f5

View File

@ -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
- Раскрывающийся блок `<details>` для 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 && (
<details className="text-gray-400 text-sm">
<summary>💭 Размышления агента</summary>
<pre>{message.thinking}</pre>
</details>
)}
```
**Хранение в БД:**
- Поле `thinking` в Message модели (nullable text)
- Или отдельная таблица reasoning_blocks
- Проще: JSON поле `metadata` с ключом `thinking`
**Что нужно от LLM:**
- Anthropic: `thinking` parameter в API request → возвращает `thinking` блоки
- OpenAI: `reasoning_effort` → скрытый reasoning (не доступен)
- Только Anthropic (Claude) реально отдаёт thinking content