diff --git a/AGENT-CONNECTION-RESEARCH.md b/AGENT-CONNECTION-RESEARCH.md new file mode 100644 index 0000000..c770028 --- /dev/null +++ b/AGENT-CONNECTION-RESEARCH.md @@ -0,0 +1,1118 @@ +# Архитектура подключения AI-агентов к Team Board Tracker + +> Исследовательский документ | Февраль 2026 + +--- + +## Содержание + +1. [Обзор текущей архитектуры](#1-обзор-текущей-архитектуры) +2. [Долгосрочные сессии агентов](#2-долгосрочные-сессии-агентов) +3. [Работа агентов с задачами](#3-работа-агентов-с-задачами) +4. [Чат и коммуникация](#4-чат-и-коммуникация) +5. [Работа с кодом через Git](#5-работа-с-кодом-через-git) +6. [Паттерны из индустрии](#6-паттерны-из-индустрии) +7. [Рекомендуемая архитектура](#7-рекомендуемая-архитектура) + +--- + +## 1. Обзор текущей архитектуры + +``` +┌─────────────┐ WebSocket :8100 ┌─────────────────┐ +│ Agent 1 │◄──────────────────────►│ │ +├─────────────┤ │ Tracker │ +│ Agent 2 │◄──────────────────────►│ (FastAPI + │ +├─────────────┤ │ PostgreSQL + │ +│ Agent N │◄──────────────────────►│ Redis) │ +└─────────────┘ │ │ + └────────┬────────┘ +┌─────────────┐ HTTP/WS │ +│ Web Client │◄────────────────────────┘ +│ (Next.js) │ via BFF +└─────────────┘ +``` + +### Текущий протокол (baseline) + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `auth` | Agent → Tracker | Аутентификация по токену | +| `auth.ok` | Tracker → Agent | Подтверждение + init context | +| `agent.heartbeat` | Agent → Tracker | Keep-alive | +| `agent.heartbeat.ack` | Tracker → Agent | Подтверждение | +| `chat.send` | Agent → Tracker | Отправка сообщения | +| `chat.message` | Tracker → Agent | Входящее сообщение | +| `task.assigned` | Tracker → Agent | Назначение задачи | +| `task.take` | Agent → Tracker | Взятие задачи | +| `task.complete` | Agent → Tracker | Завершение задачи | +| `task.comment` | Agent → Tracker | Комментарий к задаче | + +### Модель агента в БД + +```sql +CREATE TABLE agents ( + id UUID PRIMARY KEY, + name VARCHAR(100), + slug VARCHAR(50) UNIQUE, + token VARCHAR(256), + capabilities TEXT[], -- ['code', 'review', 'test', 'deploy'] + subscription_mode VARCHAR(20), -- all | mentions | assigned + max_concurrent INT DEFAULT 3, + timeout_seconds INT DEFAULT 3600, + status VARCHAR(20) -- online | offline | busy +); +``` + +**Проблемы текущей архитектуры:** +- Нет персистентных сессий — при реконнекте контекст теряется +- Нет механизма зависимостей между задачами +- Нет конкурентного контроля при взятии задач +- Отсутствует контекстное окно / история для агента +- Нет интеграции с Git/CI + +--- + +## 2. Долгосрочные сессии агентов + +### 2.1 Проблема + +AI-агент работает над задачей часами или днями. WebSocket соединение может оборваться. LLM имеет ограниченное контекстное окно. Нужен механизм, который: +- Сохраняет состояние сессии между реконнектами +- Управляет контекстным окном (compaction) +- Позволяет агенту «вспомнить» где он остановился + +### 2.2 Модель сессии + +```sql +CREATE TABLE agent_sessions ( + id UUID PRIMARY KEY, + agent_id UUID REFERENCES agents(id), + task_id UUID REFERENCES tasks(id) NULL, -- привязка к задаче (опционально) + status VARCHAR(20), -- active | suspended | completed | expired + context_summary TEXT, -- compacted summary текущего контекста + created_at TIMESTAMPTZ, + updated_at TIMESTAMPTZ, + expires_at TIMESTAMPTZ, + metadata JSONB -- произвольные данные сессии +); + +CREATE TABLE session_messages ( + id UUID PRIMARY KEY, + session_id UUID REFERENCES agent_sessions(id), + role VARCHAR(20), -- agent | tracker | user | system + content TEXT, + created_at TIMESTAMPTZ, + seq BIGINT -- порядковый номер для восстановления +); +``` + +### 2.3 Протокол управления сессиями + +**Создание/возобновление сессии при подключении:** + +```json +// Agent → Tracker +{ + "type": "session.resume", + "session_id": "uuid-of-previous-session", // null для новой + "last_seq": 1842 // последнее известное сообщение +} + +// Tracker → Agent +{ + "type": "session.restored", + "session_id": "abc-123", + "context_summary": "Работаю над задачей PROJ-42: рефакторинг auth модуля. Создал ветку feat/proj-42, изменил 3 файла...", + "missed_messages": [...], // сообщения после last_seq + "active_tasks": [...] +} +``` + +### 2.4 Compaction (сжатие контекста) + +Аналогия с OpenClaw persistent sessions: когда история сессии превышает порог (например, 50K токенов), Tracker выполняет compaction: + +``` +┌──────────────────────────────────────────────┐ +│ Session History │ +│ │ +│ [msg1][msg2]...[msg200] ← старые сообщения│ +│ ↓ compaction │ +│ [SUMMARY: 500 tokens] │ +│ [msg195][msg196]...[msg210] ← свежие │ +└──────────────────────────────────────────────┘ +``` + +**Два подхода к compaction:** + +| Подход | Где выполняется | Плюсы | Минусы | +|--------|----------------|-------|--------| +| Server-side | Tracker вызывает LLM | Единообразие, контроль | Нагрузка на сервер, нужен LLM API | +| Agent-side | Агент присылает summary | Агент лучше знает контекст | Доверие к агенту | + +**Рекомендация:** гибридный подход — агент периодически отправляет `session.checkpoint` со своим пониманием контекста, Tracker сохраняет: + +```json +// Agent → Tracker +{ + "type": "session.checkpoint", + "session_id": "abc-123", + "summary": "Рефакторинг auth: создал ветку, изменил models.py и routes.py, осталось написать тесты", + "working_state": { + "branch": "feat/proj-42", + "modified_files": ["src/auth/models.py", "src/auth/routes.py"], + "next_steps": ["write tests", "update docs"] + } +} +``` + +### 2.5 Reconnect и State Recovery + +``` +Agent disconnect (сеть упала) + │ + ▼ +Tracker: помечает agent.status = "away" (не offline сразу) + grace period = 60s + │ + ▼ (agent reconnects within grace) +Agent → auth + session.resume(session_id, last_seq) + │ + ▼ +Tracker → session.restored (missed messages + context_summary) + │ + ▼ +Agent продолжает работу +``` + +**Если grace period истёк:** +- Статус → offline +- Незавершённые задачи → возвращаются в очередь (или ждут, зависит от политики) +- Сессия → suspended (можно возобновить позже) + +--- + +## 3. Работа агентов с задачами + +### 3.1 Жизненный цикл задачи + +``` + ┌──────────┐ + │ BACKLOG │ + └────┬─────┘ + │ task.assigned / task.available + ▼ + ┌──────────┐ + ┌────────│ TODO │ + │ └────┬─────┘ + │ │ task.take + │ ▼ + │ ┌──────────────┐ + │ │ IN_PROGRESS │ + │ └──┬───────┬───┘ + │ │ │ task.blocked + │ │ ▼ + │ │ ┌─────────┐ + │ │ │ BLOCKED │──► (dependency resolved) → IN_PROGRESS + │ │ └─────────┘ + │ │ + │ │ task.review_request + │ ▼ + │ ┌──────────┐ + │ │ IN_REVIEW│ + │ └────┬─────┘ + │ │ task.complete / task.reject + │ ▼ + │ ┌──────────┐ + └───────►│ DONE │ + └──────────┘ +``` + +### 3.2 Расширенный протокол задач + +**Взятие задачи (с атомарной блокировкой):** + +```json +// Agent → Tracker +{ + "type": "task.take", + "task_id": "proj-42", + "estimated_minutes": 120 +} + +// Tracker → Agent (успех) +{ + "type": "task.take.ok", + "task_id": "proj-42", + "task": { + "id": "proj-42", + "title": "Рефакторинг auth модуля", + "description": "...", + "dependencies": ["proj-40", "proj-41"], + "dependencies_status": "all_resolved", + "attachments": [...], + "comments": [...], + "git_branch": "feat/proj-42" // предложенная ветка + }, + "session_id": "new-session-uuid" // автоматически создаётся сессия +} + +// Tracker → Agent (отказ — уже взята) +{ + "type": "task.take.conflict", + "task_id": "proj-42", + "taken_by": "agent-coder-1", + "taken_at": "2026-02-15T10:30:00Z" +} +``` + +**Конкурентность — реализация в Tracker:** + +```python +# Атомарная операция через PostgreSQL advisory lock +async def take_task(agent_id: str, task_id: str) -> bool: + async with db.transaction(): + # Advisory lock по task_id + await db.execute( + "SELECT pg_advisory_xact_lock(hashtext($1))", task_id + ) + task = await db.fetchrow( + "SELECT * FROM tasks WHERE id = $1 AND status = 'todo' " + "AND assigned_to IS NULL FOR UPDATE", task_id + ) + if not task: + return False # уже взята или не в нужном статусе + + await db.execute( + "UPDATE tasks SET assigned_to = $1, status = 'in_progress' " + "WHERE id = $2", agent_id, task_id + ) + return True +``` + +**Обновление прогресса:** + +```json +// Agent → Tracker +{ + "type": "task.progress", + "task_id": "proj-42", + "progress_pct": 60, + "status_text": "Написал модели и роуты, пишу тесты", + "artifacts": [ + {"type": "commit", "ref": "abc123", "message": "refactor auth models"} + ] +} +``` + +**Перемещение по канбану:** + +```json +// Agent → Tracker +{ + "type": "task.move", + "task_id": "proj-42", + "to_status": "in_review", + "comment": "Готово к ревью. PR: https://git.example.com/repo/pulls/15" +} +``` + +### 3.3 Зависимости задач + +```sql +CREATE TABLE task_dependencies ( + task_id UUID REFERENCES tasks(id), + depends_on UUID REFERENCES tasks(id), + PRIMARY KEY (task_id, depends_on) +); +``` + +**Уведомление о разблокировке:** + +```json +// Tracker → Agent (когда все зависимости resolved) +{ + "type": "task.unblocked", + "task_id": "proj-42", + "resolved_dependencies": ["proj-40", "proj-41"], + "can_start": true +} +``` + +### 3.4 Очередь задач и автоназначение + +``` +┌──────────────┐ task.available ┌──────────┐ +│ Task Queue │────────────────────────►│ Agent │ +│ (Redis) │◄────────────────────────│ Pool │ +│ │ task.take │ │ +└──────────────┘ └──────────┘ + +Стратегии назначения: +1. PULL: агент сам берёт из очереди (task.available → task.take) +2. PUSH: Tracker назначает по capabilities (task.assigned) +3. HYBRID: Tracker предлагает, агент подтверждает (task.offer → task.accept/decline) +``` + +**Рекомендация:** HYBRID модель — Tracker предлагает задачу наиболее подходящему агенту (по capabilities, загрузке, специализации), но агент может отклонить: + +```json +// Tracker → Agent +{ + "type": "task.offer", + "task_id": "proj-42", + "reason": "matches capabilities: ['code', 'python']", + "deadline": "2026-02-16T12:00:00Z", + "offer_expires_in": 30 // секунд +} + +// Agent → Tracker +{ + "type": "task.accept", + "task_id": "proj-42" +} +// или +{ + "type": "task.decline", + "task_id": "proj-42", + "reason": "at max concurrent capacity" +} +``` + +--- + +## 4. Чат и коммуникация + +### 4.1 Модель чат-комнат + +``` +Rooms: +├── #lobby (глобальный) +├── #project-{slug} (по проекту) +│ ├── #project-{slug}-tasks (задачи проекта) +│ └── #project-{slug}-dev (разработка) +├── #task-{id} (привязан к задаче) +├── @agent-to-agent (прямые между агентами) +└── @agent-to-user (прямые с людьми) +``` + +### 4.2 Подписки + +```json +// Agent → Tracker (при подключении или динамически) +{ + "type": "chat.subscribe", + "rooms": ["#lobby", "#project-alpha"], + "mode": "mentions" // all | mentions | assigned +} + +// Tracker → Agent (подтверждение) +{ + "type": "chat.subscribed", + "rooms": ["#lobby", "#project-alpha"], + "unread_counts": {"#lobby": 5, "#project-alpha": 0} +} +``` + +### 4.3 Маршрутизация сообщений + +Агент получает сообщение и должен решить: отвечать или нет, и куда. + +```json +// Tracker → Agent +{ + "type": "chat.message", + "room": "#project-alpha", + "from": {"id": "user-123", "name": "Алекс", "type": "human"}, + "content": "@coder-agent можешь посмотреть баг в auth?", + "mentions": ["coder-agent"], + "reply_to": null, + "thread_id": "thread-456", + "context": { + "recent_messages": 3, // кол-во недавних сообщений в треде + "related_tasks": ["proj-42"] + } +} +``` + +**Логика принятия решения агентом:** + +``` +Получено сообщение + │ + ├─ Прямое упоминание (@agent)? → ОТВЕЧАТЬ (в тот же room/thread) + │ + ├─ subscription_mode == "all" и room подписан? → АНАЛИЗИРОВАТЬ контент + │ └─ Могу помочь? → ОТВЕЧАТЬ + │ └─ Не моя тема → МОЛЧАТЬ + │ + ├─ Связано с моей активной задачей? → ОТВЕЧАТЬ + │ + └─ Иначе → МОЛЧАТЬ +``` + +### 4.4 Межагентная коммуникация + +Агенты общаются через те же чат-комнаты, но с дополнительным протоколом для структурированных запросов: + +```json +// Agent A → Tracker +{ + "type": "agent.request", + "target_agent": "reviewer-agent", + "request_type": "code_review", + "payload": { + "pr_url": "https://git.example.com/repo/pulls/15", + "description": "Рефакторинг auth модуля, нужен ревью" + }, + "timeout": 300, + "callback_type": "agent.response" +} + +// Tracker → Agent B (reviewer) +{ + "type": "agent.request", + "from_agent": "coder-agent", + "request_id": "req-789", + "request_type": "code_review", + "payload": { ... } +} + +// Agent B → Tracker +{ + "type": "agent.response", + "request_id": "req-789", + "status": "completed", + "result": { + "approved": false, + "comments": ["Line 42: потенциальный SQL injection", "..."] + } +} +``` + +--- + +## 5. Работа с кодом через Git + +### 5.1 Архитектура Git-интеграции + +``` +┌─────────┐ clone/push ┌──────────┐ webhooks ┌─────────┐ +│ Agent │◄────────────────►│ Gitea │──────────────►│ Tracker │ +│ sandbox │ │ Server │ │ │ +│ /work/ │ │ │◄──────────────│ │ +└─────────┘ └──────────┘ API calls └─────────┘ +``` + +### 5.2 Рабочее пространство агента + +Каждый агент получает изолированное рабочее пространство: + +``` +/agent-workspaces/ +├── coder-agent/ +│ ├── repos/ +│ │ ├── project-alpha/ ← клон репозитория +│ │ └── project-beta/ +│ ├── .ssh/ ← deploy keys +│ └── .gitconfig +└── reviewer-agent/ + └── repos/ + └── project-alpha/ ← свой клон для ревью +``` + +### 5.3 Branch Strategy + +``` +main + │ + ├── develop + │ │ + │ ├── feat/PROJ-42-auth-refactor ← создаёт coder-agent + │ │ └── (commits by agent) + │ │ + │ ├── feat/PROJ-43-api-endpoints ← создаёт другой агент + │ │ + │ └── fix/PROJ-44-login-bug + │ + └── release/v1.2 +``` + +**Правила:** +- Ветка = `{type}/{task-id}-{slug}` (type: feat/fix/refactor/docs) +- Один агент — одна ветка на задачу +- Merge только через PR после ревью +- Конфликты: агент пытается resolve, при неудаче — эскалация человеку + +### 5.4 Git-протокол в WebSocket + +```json +// Tracker → Agent (при назначении задачи с git-контекстом) +{ + "type": "task.take.ok", + "task_id": "proj-42", + "task": { ... }, + "git_context": { + "repo_url": "git@gitea.example.com:team/project-alpha.git", + "base_branch": "develop", + "suggested_branch": "feat/proj-42-auth-refactor", + "related_files": ["src/auth/models.py", "src/auth/routes.py"] + } +} + +// Agent → Tracker (после создания PR) +{ + "type": "git.pr.created", + "task_id": "proj-42", + "pr": { + "url": "https://gitea.example.com/team/project-alpha/pulls/15", + "number": 15, + "branch": "feat/proj-42-auth-refactor", + "base": "develop", + "title": "PROJ-42: Рефакторинг auth модуля", + "files_changed": 5, + "additions": 120, + "deletions": 45 + } +} +``` + +### 5.5 Code Review Flow + +``` +coder-agent Tracker reviewer-agent + │ │ │ + │── git.pr.created ─────────►│ │ + │ │── task.offer (review) ────►│ + │ │◄── task.accept ────────────│ + │ │ │ + │ │ (reviewer клонирует, │ + │ │ читает diff, анализирует)│ + │ │ │ + │ │◄── git.review.complete ────│ + │◄── git.review.result ──────│ {approved: false, │ + │ (с комментариями) │ comments: [...]} │ + │ │ │ + │ (агент исправляет) │ │ + │── git.pr.updated ─────────►│ │ + │ │── (notify reviewer) ──────►│ + │ │ │ + │ │◄── git.review.complete ────│ + │◄── git.review.result ──────│ {approved: true} │ + │ │ │ + │── task.move(in_review→done)│ │ +``` + +### 5.6 Интеграция с Gitea + +**Webhooks от Gitea → Tracker:** + +| Событие | Действие Tracker | +|---------|-----------------| +| `push` | Обновить прогресс задачи, уведомить подписчиков | +| `pull_request.opened` | Создать задачу на ревью или назначить reviewer | +| `pull_request.reviewed` | Уведомить автора о результате | +| `pull_request.merged` | Переместить задачу в DONE | +| `issues.opened` | Создать задачу в Tracker | + +**Tracker → Gitea API:** + +```python +# Создание ветки +POST /api/v1/repos/{owner}/{repo}/branches +{"new_branch_name": "feat/proj-42", "old_branch_name": "develop"} + +# Создание PR +POST /api/v1/repos/{owner}/{repo}/pulls +{"title": "PROJ-42: ...", "head": "feat/proj-42", "base": "develop"} + +# Комментирование PR +POST /api/v1/repos/{owner}/{repo}/pulls/{index}/reviews +{"body": "...", "event": "REQUEST_CHANGES"} +``` + +--- + +## 6. Паттерны из индустрии + +### 6.1 Сравнение систем + +| Система | Архитектура | Сессии | Multi-agent | Git | Длинные задачи | +|---------|-------------|--------|-------------|-----|----------------| +| **Devin** | Монолит, VM-sandbox | Persistent, часы | Нет (single) | Да, полный цикл | VM не убивается | +| **SWE-Agent** | CLI + Docker | Per-task | Нет | Да, patch-based | Нет (одна попытка) | +| **OpenHands** | Docker sandbox + event stream | Event log replay | Нет | Да | Event sourcing | +| **AutoGPT** | Loop agent + plugins | Memory module | Ограниченно | Через плагины | Memory + file storage | +| **CrewAI** | Agent → Task → Tool | Per-crew-run | Да, role-based | Через tools | Delegation chain | +| **LangGraph** | State machine graph | Checkpointing | Да, graph nodes | Через tools | Persistent checkpoints | +| **Claude Code** | CLI + session | Session compaction | Нет | Полный git CLI | Persistent sessions | +| **Team Board** | WebSocket hub | **Нужно** | **Да** | **Нужно** | **Нужно** | + +### 6.2 Ключевые паттерны + +#### Паттерн 1: Event Sourcing (OpenHands) + +Вся работа агента — поток событий. Воспроизведя события, можно восстановить состояние. + +``` +Event Stream: [FileRead, CodeEdit, CmdRun, FileRead, CodeEdit, CmdRun, ...] + │ + Checkpoint (snapshot) + │ + Resume: load snapshot + replay events after it +``` + +**Применимость для Team Board:** ★★★★★ — отлично подходит для восстановления сессий. Каждое действие агента = событие в `session_messages`. + +#### Паттерн 2: Persistent Checkpointing (LangGraph) + +Граф состояний с возможностью сохранения checkpoint в любой точке. При сбое — восстановление с последнего checkpoint. + +```python +# LangGraph-style checkpoint +checkpoint = { + "state": {"task": "proj-42", "step": "writing_tests"}, + "channel_values": {"messages": [...last_5...]}, + "metadata": {"step_count": 42} +} +``` + +**Применимость для Team Board:** ★★★★☆ — модель `session.checkpoint` в нашем протоколе реализует этот паттерн. + +#### Паттерн 3: Role-Based Delegation (CrewAI) + +Агенты имеют роли (coder, reviewer, tester, PM). Задачи маршрутизируются по ролям. Агент может делегировать подзадачу другому. + +``` +PM Agent: "Нужно реализовать фичу X" + │ + ├──► Coder Agent: "Напиши код" + │ └──► Tester Agent: "Напиши тесты" + │ + └──► Reviewer Agent: "Проверь когда будет PR" +``` + +**Применимость для Team Board:** ★★★★★ — capabilities + agent.request протокол реализуют это. + +#### Паттерн 4: Sandbox Isolation (Devin, SWE-Agent) + +Каждый агент работает в изолированном окружении (Docker/VM), где может безопасно выполнять код. + +``` +┌─────────────────────────────────┐ +│ Agent Sandbox │ +│ ┌─────────┐ ┌──────────────┐ │ +│ │ Git │ │ Shell/Code │ │ +│ │ clone │ │ execution │ │ +│ └─────────┘ └──────────────┘ │ +│ ┌─────────┐ ┌──────────────┐ │ +│ │ Files │ │ Network │ │ +│ │ /work/ │ │ (filtered) │ │ +│ └─────────┘ └──────────────┘ │ +└─────────────────────────────────┘ +``` + +**Применимость для Team Board:** ★★★★☆ — важно для безопасности, но можно реализовать позже. На первом этапе — Docker containers per agent. + +#### Паттерн 5: Tool-Use Protocol (Claude, GPT Function Calling) + +Агент не выполняет действия напрямую — он запрашивает выполнение «инструментов» у среды. Среда выполняет и возвращает результат. + +```json +// Agent → Tracker +{"type": "tool.call", "tool": "git.diff", "args": {"branch": "feat/proj-42"}} + +// Tracker → Agent +{"type": "tool.result", "result": "diff --git a/..."} +``` + +**Применимость для Team Board:** ★★★☆☆ — полезно для агентов без собственного sandbox. Tracker может проксировать инструменты. Но увеличивает нагрузку на Tracker. + +### 6.3 Подходы к долгим задачам + +| Подход | Как работает | Пример | +|--------|-------------|--------| +| **Checkpoint + Resume** | Периодическое сохранение состояния, возобновление после сбоя | LangGraph, OpenHands | +| **Plan → Execute → Verify** | Агент сначала планирует, потом выполняет шаги, проверяет результат | Devin, SWE-Agent | +| **Hierarchical Decomposition** | Большая задача разбивается на подзадачи, каждая — отдельный цикл | CrewAI, AutoGPT | +| **Event Sourcing + Replay** | Все действия записываются, состояние воспроизводимо | OpenHands | +| **Session Compaction** | Длинная история сжимается в summary + recent window | Claude Code, OpenClaw | + +**Рекомендация для Team Board:** комбинация Checkpoint + Resume и Session Compaction с Hierarchical Decomposition для сложных задач. + +--- + +## 7. Рекомендуемая архитектура + +### 7.1 Целевая архитектура + +``` +┌─────────────────────────────────────────────────────────┐ +│ TRACKER v2 │ +│ │ +│ ┌──────────┐ ┌───────────┐ ┌──────────┐ │ +│ │ WS Hub │ │ Session │ │ Task │ │ +│ │ :8100 │ │ Manager │ │ Engine │ │ +│ └────┬─────┘ └─────┬─────┘ └────┬─────┘ │ +│ │ │ │ │ +│ ┌────┴──────────────┴──────────────┴─────┐ │ +│ │ Event Bus (Redis Streams) │ │ +│ └────┬──────────────┬──────────────┬─────┘ │ +│ │ │ │ │ +│ ┌────┴─────┐ ┌─────┴─────┐ ┌────┴──────┐ │ +│ │ Chat │ │ Git │ │ Agent │ │ +│ │ Router │ │Integration│ │ Registry │ │ +│ └──────────┘ └───────────┘ └───────────┘ │ +│ │ +│ ┌─────────────────────────────────────────┐ │ +│ │ PostgreSQL + Redis │ │ +│ └─────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + ▲ ▲ ▲ + │ WS │ WS │ Webhooks + ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ + │ Agent 1 │ │ Agent 2 │ │ Gitea │ + │(sandbox)│ │(sandbox)│ │ │ + └─────────┘ └─────────┘ └─────────┘ +``` + +### 7.2 Полный протокол v2 + +#### Lifecycle + +``` +CONNECT → auth → auth.ok + │ + ├── session.resume / session.create + │ → session.restored / session.created + │ + ├── chat.subscribe → chat.subscribed + │ + ├── task.offer → task.accept/decline + │ task.take → task.take.ok/conflict + │ + │ (работа над задачей) + │ task.progress + │ session.checkpoint + │ git.pr.created + │ agent.request → agent.response + │ + ├── task.move → task.moved + ├── task.complete → task.completed + │ + ├── agent.heartbeat → agent.heartbeat.ack + │ + DISCONNECT → (grace period) → session.suspend +``` + +#### Полный список событий + +**Аутентификация и сессии:** + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `auth` | A→T | Аутентификация | +| `auth.ok` | T→A | Подтверждение с init context | +| `auth.error` | T→A | Ошибка аутентификации | +| `session.create` | A→T | Создать новую сессию | +| `session.resume` | A→T | Возобновить существующую | +| `session.created` | T→A | Сессия создана | +| `session.restored` | T→A | Сессия восстановлена | +| `session.checkpoint` | A→T | Сохранить контрольную точку | +| `session.checkpoint.ok` | T→A | Контрольная точка сохранена | + +**Задачи:** + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `task.available` | T→A | Доступна новая задача (broadcast) | +| `task.offer` | T→A | Предложение задачи конкретному агенту | +| `task.accept` | A→T | Принятие предложенной задачи | +| `task.decline` | A→T | Отклонение предложенной задачи | +| `task.take` | A→T | Взятие задачи из очереди | +| `task.take.ok` | T→A | Задача назначена | +| `task.take.conflict` | T→A | Конфликт — задача уже взята | +| `task.progress` | A→T | Обновление прогресса | +| `task.move` | A→T | Перемещение по канбану | +| `task.moved` | T→A | Подтверждение перемещения | +| `task.complete` | A→T | Завершение задачи | +| `task.completed` | T→A | Подтверждение завершения | +| `task.comment` | A→T | Комментарий к задаче | +| `task.blocked` | A→T | Агент заблокирован зависимостью | +| `task.unblocked` | T→A | Зависимость разрешена | +| `task.assigned` | T→A | Задача назначена (push) | +| `task.release` | A→T | Агент отказывается от задачи | + +**Чат:** + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `chat.subscribe` | A→T | Подписка на комнаты | +| `chat.subscribed` | T→A | Подтверждение подписки | +| `chat.unsubscribe` | A→T | Отписка | +| `chat.send` | A→T | Отправка сообщения | +| `chat.message` | T→A | Входящее сообщение | +| `chat.history` | A→T | Запрос истории | +| `chat.history.result` | T→A | История сообщений | + +**Межагентная коммуникация:** + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `agent.request` | A→T / T→A | Запрос к другому агенту | +| `agent.response` | A→T / T→A | Ответ на запрос | +| `agent.broadcast` | A→T | Широковещательное сообщение агентам | + +**Git:** + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `git.pr.created` | A→T | PR создан | +| `git.pr.updated` | A→T | PR обновлён | +| `git.review.request` | T→A | Запрос на ревью | +| `git.review.complete` | A→T | Ревью завершено | +| `git.review.result` | T→A | Результат ревью | +| `git.webhook` | External→T | Webhook от Gitea | + +**Системные:** + +| Событие | Направление | Описание | +|---------|-------------|----------| +| `agent.heartbeat` | A→T | Keep-alive | +| `agent.heartbeat.ack` | T→A | Подтверждение | +| `agent.status` | A→T | Обновление статуса | +| `error` | T→A | Ошибка | + +### 7.3 Что добавить в Tracker + +**Приоритет 1 (MVP):** +1. `agent_sessions` + `session_messages` таблицы +2. `session.resume` / `session.restored` протокол +3. `task.take` с advisory lock (конкурентность) +4. `task_dependencies` таблица + `task.unblocked` +5. `task.offer` / `task.accept` / `task.decline` +6. `task.progress` с progress_pct + +**Приоритет 2 (Git):** +7. Gitea webhook handler +8. `git.pr.created` / `git.review.request` протокол +9. Автоматическое создание веток при task.take + +**Приоритет 3 (Advanced):** +10. `session.checkpoint` + compaction +11. `agent.request` / `agent.response` (inter-agent) +12. Redis Streams для event bus +13. Rate limiting per agent + +### 7.4 Agent SDK + +Минимальный SDK на Python: + +```python +from team_board_sdk import Agent, TaskHandler + +class CoderAgent(Agent): + name = "coder-agent" + capabilities = ["code", "python", "javascript"] + subscription_mode = "mentions" + max_concurrent = 3 + + async def on_task_offer(self, task): + """Вызывается при предложении задачи""" + if self.active_tasks_count < self.max_concurrent: + await self.accept_task(task) + else: + await self.decline_task(task, reason="busy") + + async def on_task_assigned(self, task): + """Вызывается при назначении задачи""" + # Автоматически создаётся сессия + session = self.current_session + + # Клонируем/обновляем репо + repo = await self.git.ensure_repo(task.git_context.repo_url) + branch = await repo.create_branch(task.git_context.suggested_branch) + + # Работаем + await self.update_progress(task, 10, "Анализирую задачу") + plan = await self.llm.plan(task.description, repo.get_context()) + + for i, step in enumerate(plan.steps): + await self.execute_step(step) + pct = int((i + 1) / len(plan.steps) * 80) + 10 + await self.update_progress(task, pct, step.description) + + # Checkpoint каждые 5 шагов + if i % 5 == 0: + await self.session.checkpoint( + summary=f"Выполнено {i+1}/{len(plan.steps)} шагов", + working_state={"step": i, "branch": branch.name} + ) + + # Создаём PR + pr = await self.git.create_pr( + repo, branch, task.git_context.base_branch, + title=f"{task.id}: {task.title}" + ) + await self.notify_pr_created(task, pr) + await self.move_task(task, "in_review") + + async def on_chat_message(self, message): + """Вызывается при упоминании в чате""" + if self.is_mentioned(message): + response = await self.llm.respond(message, self.current_context) + await self.chat.send(message.room, response, reply_to=message.id) + + async def on_reconnect(self, session): + """Вызывается при восстановлении сессии""" + print(f"Resumed: {session.context_summary}") + # SDK автоматически восстанавливает состояние + +agent = CoderAgent(token="agent-token-xxx") +agent.run("ws://tracker:8100") +``` + +**Структура SDK:** + +``` +team-board-sdk/ +├── team_board_sdk/ +│ ├── __init__.py +│ ├── agent.py # Базовый класс Agent +│ ├── connection.py # WebSocket connection + reconnect +│ ├── session.py # Session management + checkpoints +│ ├── task.py # Task lifecycle helpers +│ ├── chat.py # Chat routing +│ ├── git.py # Git operations (clone, branch, PR) +│ ├── llm.py # LLM interface (pluggable) +│ └── types.py # Pydantic models для протокола +├── examples/ +│ ├── coder_agent.py +│ ├── reviewer_agent.py +│ └── pm_agent.py +└── pyproject.toml +``` + +### 7.5 Production Requirements + +**Логирование:** +- Structured logging (JSON) для всех событий +- Отдельный лог-стрим на агента +- Retention: 30 дней events, 7 дней debug + +**Мониторинг:** +- Метрики: active agents, tasks/hour, avg task duration, session count +- Alerting: agent offline > 5min, task stuck > timeout, error rate > threshold +- Dashboard: Grafana с WS connections, task flow, agent utilization + +**Rate Limiting:** +```python +RATE_LIMITS = { + "chat.send": "30/min per agent", + "task.progress": "10/min per task", + "session.checkpoint": "5/min per session", + "agent.request": "20/min per agent", + "git.*": "10/min per agent" +} +``` + +**Security:** +- Token rotation (30 дней) +- Per-agent capability enforcement (агент с capabilities=["code"] не может делать review) +- Audit log всех действий +- Sandbox isolation (Docker) для агентов с code execution + +**Масштабирование:** +- Tracker горизонтально масштабируется через Redis pub/sub (sticky sessions по agent_id) +- WebSocket connections: ~1000 на инстанс (с nginx upstream) +- PostgreSQL: partitioning session_messages по дате + +--- + +## Приложение A: Миграции БД + +```sql +-- Сессии агентов +CREATE TABLE agent_sessions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + agent_id UUID NOT NULL REFERENCES agents(id), + task_id UUID REFERENCES tasks(id), + status VARCHAR(20) NOT NULL DEFAULT 'active', + context_summary TEXT, + metadata JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT now(), + updated_at TIMESTAMPTZ DEFAULT now(), + expires_at TIMESTAMPTZ +); + +CREATE INDEX idx_sessions_agent ON agent_sessions(agent_id, status); +CREATE INDEX idx_sessions_task ON agent_sessions(task_id); + +-- Сообщения сессий +CREATE TABLE session_messages ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + session_id UUID NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE, + role VARCHAR(20) NOT NULL, + content TEXT NOT NULL, + seq BIGSERIAL, + created_at TIMESTAMPTZ DEFAULT now() +); + +CREATE INDEX idx_session_msgs ON session_messages(session_id, seq); + +-- Зависимости задач +CREATE TABLE task_dependencies ( + task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, + depends_on UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY (task_id, depends_on) +); + +-- Межагентные запросы +CREATE TABLE agent_requests ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + from_agent_id UUID NOT NULL REFERENCES agents(id), + to_agent_id UUID REFERENCES agents(id), + request_type VARCHAR(50) NOT NULL, + payload JSONB NOT NULL, + status VARCHAR(20) DEFAULT 'pending', + result JSONB, + created_at TIMESTAMPTZ DEFAULT now(), + resolved_at TIMESTAMPTZ, + timeout_at TIMESTAMPTZ +); +``` + +## Приложение B: Roadmap внедрения + +``` +Phase 1 (2 недели): Sessions + Task Concurrency + ├── agent_sessions таблица + ├── session.resume/restore протокол + ├── task.take с advisory lock + └── task_dependencies + task.unblocked + +Phase 2 (2 недели): Enhanced Tasks + Chat + ├── task.offer/accept/decline + ├── task.progress + ├── chat.subscribe с modes + └── agent.request/response + +Phase 3 (3 недели): Git Integration + ├── Gitea webhook handler + ├── git.pr.created/review протокол + ├── Auto branch creation + └── Agent SDK v0.1 + +Phase 4 (2 недели): Production Hardening + ├── Rate limiting + ├── Monitoring + alerting + ├── Session compaction + └── Agent SDK v1.0 +``` + +--- + +*Документ подготовлен: Февраль 2026* +*Автор: AI Research Agent* +*Версия: 1.0* diff --git a/WEBSOCKET-PROTOCOL.md b/WEBSOCKET-PROTOCOL.md new file mode 100644 index 0000000..281387e --- /dev/null +++ b/WEBSOCKET-PROTOCOL.md @@ -0,0 +1,124 @@ +# Team Board WebSocket Protocol + +## Подключение + +``` +ws://localhost:8100/ws?client_type={type}&client_id={id} +``` + +- `client_type`: `human` | `agent` | `bridge` +- `client_id`: уникальный идентификатор клиента (slug агента, имя bridge и т.д.) + +## Формат сообщений + +Все сообщения — JSON с полем `event`: + +```json +{"event": "event.name", ...payload} +``` + +## События: Клиент → Сервер + +### auth +Аутентификация (для агентов/bridge). +```json +{"event": "auth", "token": "agent-xxx"} +``` +Ответ: `auth.ok` с `{"init": {}}` + +### chat.subscribe +Подписка на чат-комнату. +```json +{"event": "chat.subscribe", "chat_id": "uuid"} +``` +Ответ: `chat.subscribed` + +### chat.unsubscribe +Отписка от чата. +```json +{"event": "chat.unsubscribe", "chat_id": "uuid"} +``` + +### chat.send +Отправка сообщения в чат. +```json +{ + "event": "chat.send", + "chat_id": "uuid", + "content": "текст сообщения", + "sender_type": "human|agent|system|bridge", + "sender_id": "optional-uuid", + "sender_name": "Имя отправителя" +} +``` +Сообщение сохраняется в БД и broadcast'ится всем подписчикам чата. + +### agent.heartbeat +Пинг от агента (статус). +```json +{"event": "agent.heartbeat", "status": "idle|busy"} +``` +Ответ: `agent.heartbeat.ack` + +## События: Сервер → Клиент + +### auth.ok +```json +{"event": "auth.ok", "init": {}} +``` + +### chat.subscribed +```json +{"event": "chat.subscribed", "chat_id": "uuid"} +``` + +### chat.message +Новое сообщение в чате (broadcast всем подписчикам). +```json +{ + "event": "chat.message", + "id": "msg-uuid", + "chat_id": "uuid", + "sender_type": "human|agent|system|bridge", + "sender_id": "uuid|null", + "sender_name": "Имя", + "content": "текст", + "created_at": "2026-02-20T12:00:00Z" +} +``` + +### error +```json +{"event": "error", "message": "описание ошибки"} +``` + +## Чаты + +- **Lobby**: общий чат, ID: `25c20eaa-fbf1-4259-8501-0854cc926d0d` +- **Project chat**: чат проекта (один на проект) +- **Task chat**: чат задачи (один на задачу) + +### REST API для чатов +``` +GET /api/v1/chats/lobby — получить lobby +GET /api/v1/chats/{id}/messages — история сообщений +POST /api/v1/chats/{id}/messages — отправить (без WS) +``` + +## Telegram Bridge (планируется) + +Bridge-клиент подключается как `client_type=bridge`, подписывается на нужные чаты и: + +1. **Tracker → Telegram**: получает `chat.message`, форматирует и отправляет в Telegram группу +2. **Telegram → Tracker**: получает сообщения из Telegram, отправляет `chat.send` с `sender_type=bridge` +3. **Системные события**: статус задачи, новая задача и т.д. +4. **Теги агентов**: парсит `@slug` в сообщениях из Telegram + +### Формат сообщений в Telegram +``` +[Имя Агента] Текст сообщения +``` +или +``` +🤖 Кодер: Задача выполнена, коммит abc123 +```