docs: обновлён WebSocket протокол, убраны адаптеры, capabilities на агентах

This commit is contained in:
Markov 2026-02-15 22:45:21 +01:00
parent 5061c60b9a
commit 7849a1fddb

374
IDEAS.md
View File

@ -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
Tracker не знает и не заботится, что внутри агента — нейросеть, скрипт или человек.
# Конфиг — 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"]
```
**Важно:** 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
---