docs/OPENCLAW-ARCHITECTURE-REVIEW.md

167 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Обзор архитектуры 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