docs/OPENCLAW-ARCHITECTURE-REVIEW.md

8.3 KiB
Raw Blame History

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

{"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: thinking parameter в API request → возвращает thinking блоки
  • OpenAI: reasoning_effort → скрытый reasoning (не доступен)
  • Только Anthropic (Claude) реально отдаёт thinking content