From 7849a1fddb737d14f2f5aa937121875c7d857a49 Mon Sep 17 00:00:00 2001 From: Markov Date: Sun, 15 Feb 2026 22:45:21 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20WebSocket=20=D0=BF=D1=80=D0=BE=D1=82=D0=BE=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB,=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=D1=8B,=20capabilities=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IDEAS.md | 374 +++++++++++++++++++++---------------------------------- 1 file changed, 142 insertions(+), 232 deletions(-) diff --git a/IDEAS.md b/IDEAS.md index dca2693..6c2cfbe 100644 --- a/IDEAS.md +++ b/IDEAS.md @@ -61,9 +61,9 @@ | Компонент | Описание | |-----------|----------| -| **Tracker** | Ядро: проекты, задачи, чаты, события. Внутренний, WebSocket сервер | +| **Tracker** | Ядро: проекты, задачи, чаты, события. Внутренний, WebSocket сервер. Роутинг событий агентам | | **Web Client** | Next.js + BFF (Python). Авторизация через Authentik. Единственное что торчит наружу | -| **Агенты** | Отдельные сервисы. Подключаются к трекеру по WebSocket | +| **Агенты** | Независимые приложения (любой язык). Подключаются к Tracker по WebSocket | ### Репозитории @@ -221,48 +221,15 @@ Task A ──depends_on──→ Task B --- -## Агенты и адаптеры +## Агенты -### Адаптер (провайдер) +### Принцип -Адаптер — это подключение к конкретной нейросети. Один адаптер может использоваться несколькими агентами. +**Агент = независимое приложение.** Написано на чём угодно (Python, Go, Rust, JS — без разницы). Подключается к Tracker по WebSocket. Может быть даже человек с WebSocket-клиентом. -```yaml -Адаптер: - id: uuid - название: "Claude Opus" - провайдер: anthropic | openai | google | openclaw | cli - - # Конфиг — JSON поле, разное для каждого провайдера - config: - # Anthropic - api_key: "sk-ant-..." - model: "claude-opus-4-5" - max_tokens: 8192 - - # Или OpenAI - api_key: "sk-..." - model: "gpt-4" - organization: "org-..." - - # Или OpenClaw - gateway_url: "http://localhost:18789" - gateway_token: "..." - agent_id: "main" - - # Или CLI - command: "claude" - working_dir: "/workspace" - - # Возможности адаптера (что умеет эта нейросеть) - capabilities: ["coding", "analytics", "review", "design", "docs", "testing"] -``` +Tracker не знает и не заботится, что внутри агента — нейросеть, скрипт или человек. -**Важно:** Capabilities привязаны к адаптеру, не к агенту. Потому что возможности определяются нейросетью. - -### Агент - -Агент — это "личность" с ролью, которая использует адаптер. +### Структура агента ```yaml Агент: @@ -270,20 +237,21 @@ Task A ──depends_on──→ Task B имя: "Кодер" slug: "coder" - # Привязка к адаптеру - адаптер_id: uuid - - # Роль - системный_промпт: "Ты — backend разработчик..." + # Capabilities — что умеет этот агент + capabilities: ["coding", "backend", "testing"] # Подписка подписка: - режим: mentions # all | mentions | assigned + режим: assigned # all | mentions | assigned проекты: ["*"] # или конкретные # Ограничения макс_параллельных: 2 таймаут: 600 # секунд + + # Статус (управляется Tracker) + status: online | offline | busy + last_heartbeat: timestamp ``` ### Лейблы @@ -301,30 +269,20 @@ CREATE TABLE labels ( Примеры: `coding`, `design`, `analytics`, `testing`, `docs`, `backend`, `frontend`, `urgent`, `bug` -### Связка: лейблы задач ↔ capabilities адаптеров +### Связка: лейблы задач ↔ capabilities агентов ``` -Задача с лейблом "coding" → только агенты с адаптером, у которого capability "coding" -Задача с лейблом "design" → только агенты с адаптером, у которого capability "design" +Задача с лейблом "coding" → только агенты с capability "coding" +Задача с лейблом "design" → только агенты с capability "design" ``` -Агент-дизайнер НЕ возьмёт задачу с лейблом `coding` — у его адаптера нет такой capability. - -### Типы провайдеров - -| Провайдер | Описание | Конфиг | -|-----------|----------|--------| -| `anthropic` | Claude API | api_key, model, max_tokens | -| `openai` | GPT, Codex | api_key, model, organization | -| `google` | Gemini | api_key, model | -| `openclaw` | OpenClaw Gateway | gateway_url, gateway_token, agent_id | -| `cli` | Claude Code CLI | command, working_dir | +Агент-дизайнер НЕ возьмёт задачу с лейблом `coding` — у него нет такой capability. ### Подписка на события | Режим | Агент получает | |-------|---------------| -| `all` | Все сообщения в подписанных проектах | +| `all` | Все события в подписанных проектах | | `mentions` | Только когда @упомянут | | `assigned` | Назначенные задачи + mentions | @@ -332,30 +290,108 @@ CREATE TABLE labels ( ## WebSocket протокол -### Трекер → Клиент +### Подключение и Init Handshake + +При подключении агент проходит аутентификацию и получает начальный контекст: ``` -task.created — задача создана -task.updated — задача обновлена -task.ready — задача готова к выполнению (перенесена из Backlog) +1. Агент подключается: ws://tracker:8100/ws +2. Агент → Tracker: auth + { + "type": "auth", + "token": "agent-token-xxx" + } + +3. Tracker → Агент: auth.ok + init (начальный контекст) + { + "type": "auth.ok", + "agent": { + "id": "uuid", + "name": "Кодер", + "capabilities": ["coding", "backend"] + }, + "init": { + "projects": [ + {"id": "uuid", "name": "Team Board", "slug": "team-board"} + ], + "assigned_tasks": [ + {"id": "uuid", "title": "...", "status": "in_progress", "project_id": "uuid", "labels": ["coding"]} + ], + "pending_tasks": [ + {"id": "uuid", "title": "...", "status": "ready", "project_id": "uuid", "labels": ["coding"]} + ] + } + } + +4. При ошибке: + { + "type": "auth.error", + "message": "Invalid token" + } + → соединение закрывается +``` + +**init** содержит: +- **projects** — проекты, в которых участвует агент +- **assigned_tasks** — задачи, назначенные на этого агента (in_progress, review) +- **pending_tasks** — задачи, подходящие по capabilities и готовые к взятию (ready) + +### Роутинг событий (Tracker → Агент) + +Tracker фильтрует события по трём критериям: + +**1. Capabilities** — лейблы задачи ∩ capabilities агента +- Задача с лейблом `coding` → только агентам с capability `coding` +- Нет совпадения → событие не отправляется + +**2. Подписка агента** (subscription mode) +- `assigned` → только свои задачи + @mentions +- `mentions` → @mentions в чатах и комментариях +- `all` → все события (фильтруются по capabilities) + +**3. Чаты** — отдельная логика: +- **Чат задачи** → assignee + участники обсуждения +- **Чат проекта** → все агенты проекта (без фильтра по capabilities — обсуждение для всех) +- **Лобби** → все подключённые агенты + +### Трекер → Клиент (события) + +``` +task.created — задача создана (с учётом роутинга) +task.updated — задача обновлена (статус, описание, лейблы) +task.ready — задача готова к выполнению task.assigned — задача назначена агенту task.blocked — задача заблокирована зависимостью task.unblocked — зависимость выполнена, задача разблокирована -chat.message — новое сообщение в чате -agent.status — статус агента изменился +chat.message — новое сообщение в чате (с учётом роутинга) +agent.status — статус агента изменился (online/offline/busy) file.attached — файл прикреплён к задаче ``` -### Клиент → Трекер +### Клиент → Трекер (команды) ``` +auth — аутентификация (первое сообщение) task.take — агент берёт задачу task.complete — агент завершил задачу +task.update — агент обновляет задачу (статус, описание) task.comment — комментарий к задаче task.attach — прикрепить файл chat.send — отправить сообщение в чат -agent.register — регистрация агента -agent.heartbeat — пульс +agent.heartbeat — пульс (каждые 30 сек) +``` + +### Heartbeat + +``` +Агент → Tracker (каждые 30 сек): +{ + "type": "agent.heartbeat", + "status": "idle" | "busy", + "current_tasks": ["task-uuid-1"] +} + +Если heartbeat не пришёл 90 секунд → agent.status = offline ``` --- @@ -463,27 +499,18 @@ review_history: ## База данных (ключевые таблицы) ```sql --- Адаптеры (подключения к нейросетям) -CREATE TABLE adapters ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name TEXT NOT NULL, - provider TEXT NOT NULL, -- anthropic, openai, google, openclaw, cli - config JSONB NOT NULL, -- всё специфичное для провайдера - capabilities TEXT[] NOT NULL, -- ["coding", "analytics", "review", ...] - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- Агенты (личности с ролями) +-- Агенты (независимые приложения) CREATE TABLE agents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, slug TEXT UNIQUE NOT NULL, - adapter_id UUID REFERENCES adapters(id), - system_prompt TEXT, - subscription_mode TEXT DEFAULT 'mentions', -- all, mentions, assigned + token TEXT UNIQUE NOT NULL, -- токен для WebSocket auth + capabilities TEXT[] NOT NULL DEFAULT '{}', -- ["coding", "analytics", ...] + subscription_mode TEXT DEFAULT 'assigned', -- all, mentions, assigned max_concurrent INT DEFAULT 1, timeout_seconds INT DEFAULT 600, - status TEXT DEFAULT 'idle', + status TEXT DEFAULT 'offline', -- online, offline, busy + last_heartbeat TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); @@ -544,176 +571,59 @@ CREATE TABLE task_files ( ### Принцип -**Агент = отдельный процесс.** Не привязан к OpenClaw или конкретному серверу. Может быть запущен локально, на другом сервере, в Docker — где угодно. Подключается к Tracker по WebSocket. - -### Архитектура - -``` -┌─────────────────────────┐ -│ Agent Process │ -│ ┌───────────────────┐ │ -│ │ Agent SDK │ │ ← общая Python-библиотека -│ │ ┌──────────────┐ │ │ -│ │ │ Adapter │ │ │ ← OpenClaw / OpenAI / Ollama / CLI -│ │ └──────────────┘ │ │ -│ └─────────┬─────────┘ │ -└────────────┼────────────┘ - │ WebSocket (локально или через интернет) - ┌──────┴──────┐ - │ Tracker │ - │ AgentManager │ - └─────────────┘ -``` - -### Agent SDK (Python-пакет) - -Общая библиотека для всех агентов: - -```python -from team_board.agent import Agent -from team_board.adapters import OpenClawAdapter - -adapter = OpenClawAdapter( - gateway_url="http://localhost:18789", - gateway_token="...", -) - -agent = Agent( - name="Кодер", - tracker_url="ws://localhost:8100", - adapter=adapter, - system_prompt="Ты — backend разработчик...", -) - -agent.run() # подключается к Tracker, слушает задачи -``` - -Что делает SDK: -- **WebSocket клиент** → подключение к Tracker, переподключение при разрыве -- **Heartbeat** → автоматический пинг каждые N секунд -- **Обработка задач** → получил задачу → передал адаптеру → вернул результат -- **Файловые операции** → загрузка/скачивание файлов задачи - -### Адаптеры (BaseAdapter) - -```python -class BaseAdapter(ABC): - @abstractmethod - async def send(self, messages: list[Message]) -> Response: - """Отправить сообщения нейросети и получить ответ.""" - ... - - @abstractmethod - async def capabilities(self) -> list[str]: - """Возвращает capabilities адаптера.""" - ... -``` - -Реализации: -| Адаптер | Подключение | Особенности | -|---------|-------------|-------------| -| `OpenClawAdapter` | Webhooks / sessions_spawn | Доступ к инструментам OpenClaw | -| `AnthropicAdapter` | Claude API напрямую | Чистый API, без инструментов | -| `OpenAIAdapter` | GPT / Codex API | | -| `OllamaAdapter` | Локальная модель | Бесплатно, офлайн | -| `CLIAdapter` | Claude Code CLI | Доступ к файловой системе | +**Агент = независимое приложение.** Написано на чём угодно. Подключается к Tracker по WebSocket. Tracker не управляет запуском — агент сам стартует и подключается. ### AgentManager (в Tracker) -Tracker управляет жизненным циклом агентов: +Tracker отслеживает подключённых агентов: ```yaml Реестр агентов: - id: uuid name: "Кодер" - adapter: "Claude Opus" - status: online | offline | busy | dead - host: "localhost" # где запущен - pid: 12345 # PID процесса (если локальный) + status: online | offline | busy + capabilities: ["coding", "backend"] last_heartbeat: timestamp current_tasks: [task_id] ``` Функции AgentManager: -1. **Запуск** — поднимает агента как отдельный процесс (локально: subprocess/systemd) -2. **Мониторинг** — следит за heartbeat, помечает dead если пропал -3. **Перезапуск** — dead агент → повторный запуск (макс 3 попытки) -4. **Назначение задач** — матчит задачу → подходящего агента по capabilities + загрузке -5. **Остановка** — graceful shutdown через WebSocket команду - -### Запуск агентов - -**Локально (MVP):** -```bash -# Tracker запускает как subprocess -python -m team_board.agent --name coder --tracker ws://localhost:8100 -``` - -**Через systemd (production):** -```ini -[Unit] -Description=Team Board Agent: Coder - -[Service] -ExecStart=/opt/team-board/venv/bin/python -m team_board.agent --name coder -Restart=on-failure -RestartSec=5 -``` - -**Удалённо (будущее):** -- SSH: `ssh remote-host 'python -m team_board.agent --tracker ws://tracker:8100'` -- Docker: `docker run team-board-agent --tracker ws://tracker:8100` -- HTTP API на удалённом хосте - -### Heartbeat и мониторинг - -``` -Агент → Tracker: agent.heartbeat (каждые 30 сек) - { - status: "idle" | "busy", - current_tasks: [...], - uptime: 3600, - memory_mb: 150 - } - -Если heartbeat не пришёл 90 секунд → статус = dead → перезапуск -``` - -### Таблица БД - -```sql --- Расширение таблицы agents -ALTER TABLE agents ADD COLUMN host TEXT DEFAULT 'localhost'; -ALTER TABLE agents ADD COLUMN pid INT; -ALTER TABLE agents ADD COLUMN last_heartbeat TIMESTAMPTZ; -ALTER TABLE agents ADD COLUMN restart_count INT DEFAULT 0; -ALTER TABLE agents ADD COLUMN max_restarts INT DEFAULT 3; -``` +1. **Регистрация** — агент подключился → auth → попал в реестр +2. **Мониторинг** — следит за heartbeat, помечает offline если пропал +3. **Назначение задач** — матчит задачу → подходящего агента по capabilities + загрузке +4. **Роутинг событий** — фильтрация по capabilities + подписке + чатам --- ## Открытые вопросы -1. Как интегрировать CLI-агентов (Claude Code, Cline)? -2. Workspace для агентов — общий или изолированный? -3. Прогресс работы агента в реалтайме? -4. Биллинг/учёт использования API? -5. Шаблоны агентов — как быстро создать нового? +1. Workspace для агентов — общий или изолированный? +2. Прогресс работы агента в реалтайме? +3. Биллинг/учёт использования API? --- ## Приоритеты реализации -1. [ ] Tracker: базовый WebSocket сервер -2. [ ] Tracker: проекты, задачи, подзадачи -3. [ ] Tracker: зависимости задач -4. [ ] Tracker: чаты (лобби + проект) -5. [ ] Tracker: файлы в задачах -6. [ ] Web Client: авторизация (Authentik) -7. [ ] Web Client: канбан-доска -8. [ ] Web Client: чат -9. [ ] Шаблон агента -10. [ ] Подключение первого агента (OpenClaw) +### Сделано ✅ +1. [x] Tracker: REST API (проекты, задачи, агенты, лейблы) +2. [x] Tracker: базовый WebSocket (connection manager, heartbeat) +3. [x] Tracker: Docker Compose (postgres + redis + tracker) +4. [x] Web Client: канбан-доска с drag-and-drop +5. [x] Web Client: BFF с JWT авторизацией +6. [x] CI/CD: Gitea Actions auto-deploy + +### В работе 🔧 +7. [ ] Web Client: детальный вид задачи (описание, комменты, assignee) +8. [ ] Tracker: WebSocket протокол v2 (init handshake, роутинг событий) + +### Следующее 📋 +9. [ ] Tracker: чаты (лобби + проект + задача) +10. [ ] Tracker: файлы в задачах +11. [ ] Web Client: чат +12. [ ] Web Client: управление агентами +13. [ ] Первый агент (OpenClaw → Tracker) +14. [ ] Web Client: Authentik OAuth ---