8.3 KiB
8.3 KiB
Обзор архитектуры 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 токенов для ответа
Компакция:
- Определить что session близка к лимиту (token estimate)
- Запустить memory flush (silent turn — "запиши важное")
- Суммаризировать старые сообщения через LLM → compact summary
- Заменить старые сообщения на summary в истории
- Персистить в 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.
Как должно быть:
- Tracker получает message.new → отправляет агенту через WS
- Агент генерирует ответ → стримит через WS (
agent.stream.delta) - Tracker принимает
agent.replyчерез WS → сохраняет в БД → broadcast всем - Или: агент отправляет
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):
{"type": "agent.stream.delta", "data": {"agent_id": "...", "block_type": "thinking", "text": "Думаю..."}}
{"type": "agent.stream.delta", "data": {"agent_id": "...", "block_type": "text", "text": "Ответ"}}
Frontend (ChatPanel):
{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:
thinkingparameter в API request → возвращаетthinkingблоки - OpenAI:
reasoning_effort→ скрытый reasoning (не доступен) - Только Anthropic (Claude) реально отдаёт thinking content