docs/RESEARCH.md
Markov 4209bc26fd docs: add Agent Management and OpenClaw Webhooks integration
- Agent Registry schema
- OpenClaw Webhooks API documentation
- Team Board → OpenClaw architecture
- Python client example
- Task execution flow
2026-02-15 15:41:37 +01:00

667 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Team Board — Research: Agent Integration
Исследование возможностей подключения AI-агентов к Team Board.
---
## Текущие возможности OpenClaw
### 1. sessions_spawn — спавн изолированных субагентов
```
Task → Spawn → Isolated Session → Result → Announce back
```
**Параметры:**
- `task` (required) — задача для агента
- `label` — метка для логов
- `agentId` — ID агента (если разрешён в allowlist)
- `model` — переопределение модели
- `runTimeoutSeconds` — таймаут
- `cleanup``delete|keep`
**Поведение:**
- Создаёт новую сессию `agent:<agentId>:subagent:<uuid>`
- Субагенты имеют полный набор инструментов минус session tools
- Не блокирует: возвращает `{ status: "accepted", runId, childSessionKey }`
- После завершения — announce в исходный чат
---
### 2. sessions_send — отправка между сессиями
```
Agent A → sessions_send → Agent B → Response
```
**Параметры:**
- `sessionKey` (required)
- `message` (required)
- `timeoutSeconds` — 0 = fire-and-forget
**Поведение:**
- Поддерживает ping-pong между агентами (до 5 итераций)
- Контекст сохраняется
- Провenance маркируется как `inter_session`
---
### 3. sessions_list / sessions_history
- Список активных сессий
- История сообщений сессии
- Фильтры по типу, времени, каналу
---
### 4. Multi-Agent Routing
Несколько агентов в одном gateway:
```yaml
agents:
list:
- id: main
workspace: ~/.openclaw/workspace
- id: coder
workspace: ~/projects/coder-workspace
- id: reviewer
workspace: ~/projects/reviewer-workspace
```
**Каждый агент имеет:**
- Свой workspace (файлы, AGENTS.md, SOUL.md)
- Свой state directory
- Своё хранилище сессий
- Свои auth profiles
---
### 5. ACP (Agent Client Protocol)
Стандартный протокол для подключения внешних агентов.
```
External Agent ↔ ACP ↔ OpenClaw Gateway
```
**Реализация в OpenClaw:**
- `src/acp/server.ts` — ACP сервер
- `src/acp/client.ts` — ACP клиент
- Использует `@agentclientprotocol/sdk`
**Dangerous tools требуют approval:**
- exec, spawn, shell
- sessions_spawn, sessions_send
- gateway
- fs_write, fs_delete, fs_move
---
## Варианты интеграции для Team Board
### Вариант 1: OpenClaw Multi-Agent (рекомендуется)
```
Team Board API
OpenClaw Gateway
┌─────────────────────────────────┐
│ Agent: main (Lead) │
│ Agent: coder (Developer) │
│ Agent: reviewer (QA) │
│ Agent: analyst (Research) │
└─────────────────────────────────┘
```
**Плюсы:**
- Готовая инфраструктура
- Сохранение контекста через сессии
- Единый API
- Безопасность (sandbox, permissions)
- Инструменты (exec, browser, files)
**Реализация:**
1. Добавить агентов в `openclaw.json`
2. Team Board вызывает `sessions_spawn` / `sessions_send` через Gateway API
3. Результаты через webhook или polling
**Конфиг:**
```json
{
"agents": {
"list": [
{
"id": "main",
"workspace": "~/.openclaw/workspace"
},
{
"id": "coder",
"workspace": "~/workspaces/coder",
"model": "anthropic/claude-sonnet-4-20250514"
}
],
"defaults": {
"subagents": {
"allowAgents": ["main", "coder", "reviewer"]
}
}
}
}
```
---
### Вариант 2: ACP Bridge для CLI агентов
Для подключения Claude Code CLI без траты API токенов.
```
Claude Code CLI (Max subscription)
ACP Client Wrapper
OpenClaw Gateway
Team Board
```
**Компоненты:**
1. **PTY Session Manager**
```python
import pexpect
class CliSession:
def __init__(self, command="claude"):
self.child = pexpect.spawn(command)
def send(self, message: str) -> str:
self.child.sendline(message)
self.child.expect(r'\n>') # wait for prompt
return self.child.before.decode()
```
2. **ACP Translator**
```python
class AcpCliBridge:
def handle_request(self, request):
response = self.cli_session.send(request.message)
return AcpResponse(content=response)
```
**Сложности:**
- CLI интерактивный, не headless
- Нужно парсить вывод
- Контекст внутри CLI сессии
---
### Вариант 3: Direct API + Context Store
Прямые вызовы API с хранением контекста в БД.
```
Team Board
Agent Service
┌──────────────────────────────────┐
│ Claude API │ Codex │ Gemini │
└──────────────────────────────────┘
Context Store (PostgreSQL)
```
**Схема контекста:**
```sql
CREATE TABLE agent_contexts (
id UUID PRIMARY KEY,
agent_id UUID REFERENCES agents(id),
session_id UUID REFERENCES sessions(id),
messages JSONB, -- история сообщений
metadata JSONB, -- system prompt, tools, etc.
created_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ
);
```
**При каждом запросе:**
1. Загрузить контекст из БД
2. Добавить новое сообщение
3. Вызвать API с полным контекстом
4. Сохранить ответ в БД
---
## Web Terminal Interface
### Архитектура
```
Browser (xterm.js)
↓ WebSocket
Team Board Frontend
↓ WebSocket
Team Board Backend (Session Manager)
↓ PTY / API
Agent (OpenClaw / CLI / API)
```
### Компоненты
**Frontend:**
- xterm.js — терминальный эмулятор
- WebSocket клиент
- Session selector UI
**Backend:**
- WebSocket сервер
- Session Manager
- PTY spawner (для CLI агентов)
- OpenClaw Gateway client (для OpenClaw агентов)
### Пример кода (Backend)
```python
from fastapi import WebSocket
import asyncio
class AgentSessionManager:
def __init__(self):
self.sessions = {}
async def create_session(self, agent_type: str, config: dict):
if agent_type == "openclaw":
return OpenClawSession(config)
elif agent_type == "cli":
return CliSession(config)
elif agent_type == "api":
return ApiSession(config)
async def handle_websocket(self, ws: WebSocket, session_id: str):
session = self.sessions[session_id]
async for message in ws.iter_text():
response = await session.send(message)
await ws.send_text(response)
```
---
## Сравнение вариантов
| Критерий | OpenClaw Multi-Agent | ACP CLI Bridge | Direct API |
|----------|---------------------|----------------|------------|
| Сложность | Низкая | Высокая | Средняя |
| Контекст | ✓ Автоматический | ✓ В CLI | Ручной |
| Инструменты | ✓ Полный набор | ✓ CLI tools | ✗ Нет |
| API токены | Нужны | Не нужны (Max) | Нужны |
| Готовность | Сейчас | Требует разработки | Требует разработки |
---
## Рекомендуемый план
### Фаза 1: OpenClaw Multi-Agent (быстрый старт)
1. Добавить агентов в конфиг OpenClaw
2. Team Board API вызывает Gateway
3. Использовать `sessions_spawn` для задач
4. Использовать `sessions_send` для коммуникации
**Результат:** Рабочая система с несколькими агентами
### Фаза 2: Web Terminal UI
1. xterm.js на фронте
2. WebSocket сервер
3. Прямое взаимодействие с агентами
**Результат:** Terminal-like опыт в браузере
### Фаза 3: ACP CLI Bridge (опционально)
1. PTY wrapper для Claude Code CLI
2. ACP транслятор
3. Интеграция с Gateway
**Результат:** CLI агенты без API токенов
---
## Открытые вопросы
1. **Headless mode в Claude Code CLI?**
- Нужно проверить `claude --help`
- Возможно есть `--message` или `--non-interactive`
2. **Как шарить файлы между агентами?**
- Общий workspace?
- Git как source of truth?
- Копирование через API?
3. **Rate limiting для API агентов?**
- Очередь задач
- Приоритеты
- Backoff при ошибках
4. **Мониторинг агентов?**
- Статус (idle/working/error)
- Использование токенов
- Время выполнения
---
## Agent Management System
### Agent Registry (БД)
```sql
CREATE TABLE agents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL, -- "Coder", "Analyst"
slug TEXT UNIQUE NOT NULL, -- "coder", "analyst"
-- Настройки
model TEXT, -- "claude-opus-4-5", "claude-sonnet"
system_prompt TEXT, -- роль/персона
max_concurrent INT DEFAULT 1, -- макс параллельных задач
-- Статус
status TEXT DEFAULT 'idle', -- idle, working, error, disabled
current_tasks INT DEFAULT 0,
-- Мета
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
```
### Web UI: Agent Config
```
┌─────────────────────────────────────────────┐
│ Agents [+ Add] │
├─────────────────────────────────────────────┤
│ 🟢 Coder claude-opus idle [⚙] │
│ 🟢 Reviewer claude-sonnet idle [⚙] │
│ 🟡 Analyst claude-opus working[⚙] │
│ ⚫ Tester claude-sonnet disabled[⚙] │
└─────────────────────────────────────────────┘
```
### Task Assignment Flow
```
1. User создаёт задачу в Web UI
2. Назначает агента (или Auto → Lead решает)
3. Team Board → POST /hooks/agent → OpenClaw
4. OpenClaw проверяет лимиты, спавнит субагента
5. Субагент работает, результат → callback
6. Team Board обновляет задачу в БД
```
---
## OpenClaw Webhooks API
### Конфигурация
```json
{
"hooks": {
"enabled": true,
"token": "${OPENCLAW_HOOKS_TOKEN}",
"path": "/hooks",
"defaultSessionKey": "hook:team-board",
"allowRequestSessionKey": true,
"allowedSessionKeyPrefixes": ["hook:", "task:"],
"allowedAgentIds": ["main", "coder", "reviewer"]
}
}
```
### Эндпоинты
#### POST /hooks/wake — Разбудить основную сессию
```bash
curl -X POST http://localhost:18789/hooks/wake \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "Новая задача в Team Board", "mode": "now"}'
```
**Параметры:**
- `text` (required) — описание события
- `mode``now` (сразу) или `next-heartbeat` (при след. проверке)
#### POST /hooks/agent — Запустить изолированную сессию
```bash
curl -X POST http://localhost:18789/hooks/agent \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "Напиши функцию сортировки на Python",
"name": "TeamBoard",
"agentId": "main",
"sessionKey": "task:abc123",
"deliver": true,
"channel": "telegram",
"timeoutSeconds": 300
}'
```
**Параметры:**
- `message` (required) — промпт для агента
- `name` — название хука для логов
- `agentId` — ID агента (main, coder, etc.)
- `sessionKey` — ключ сессии для отслеживания
- `deliver` — отправить ответ в канал
- `channel` — канал для ответа (telegram, discord, etc.)
- `model` — переопределение модели
- `thinking` — уровень размышлений (low, medium, high)
- `timeoutSeconds` — таймаут выполнения
### Аутентификация
```
Authorization: Bearer <token>
# или
x-openclaw-token: <token>
```
⚠️ Query-string токены (`?token=...`) запрещены.
---
## Team Board → OpenClaw Integration
### Архитектура
```
┌─────────────────────────────────────────────────────────────┐
│ Team Board │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web UI │ │ Tasks │ │ Agents │ │
│ │ (Next.js) │ │ Service │ │ Service │ │
│ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ OpenClaw │ │
│ │ Client │ │
│ └────────┬────────┘ │
└───────────────────────────────────┼─────────────────────────┘
│ HTTP
┌───────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ localhost:18789 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ /hooks/wake │ │/hooks/agent │ │ Sessions │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Agent: main (Марков) │ │
│ │ - sessions_spawn → субагенты │ │
│ │ - sessions_send → коммуникация │ │
│ │ - sessions_history → история │ │
│ └─────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
```
### OpenClaw Client (Python)
```python
import httpx
from typing import Optional
class OpenClawClient:
def __init__(self, base_url: str, token: str):
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
async def wake(self, text: str, mode: str = "now"):
"""Разбудить основную сессию"""
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.base_url}/hooks/wake",
headers=self.headers,
json={"text": text, "mode": mode}
)
return response.json()
async def run_agent(
self,
message: str,
session_key: str,
agent_id: str = "main",
deliver: bool = False,
timeout: int = 300
):
"""Запустить задачу в изолированной сессии"""
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.base_url}/hooks/agent",
headers=self.headers,
json={
"message": message,
"name": "TeamBoard",
"agentId": agent_id,
"sessionKey": session_key,
"deliver": deliver,
"timeoutSeconds": timeout
}
)
return response.json()
```
### Task Execution Flow
```python
async def execute_task(task: Task, agent: Agent):
client = OpenClawClient(
base_url="http://localhost:18789",
token=settings.OPENCLAW_TOKEN
)
# Формируем промпт
prompt = f"""
Задача: {task.title}
Описание: {task.description}
Контекст проекта: {task.project.brief}
Выполни задачу и верни результат.
"""
# Запускаем агента
result = await client.run_agent(
message=prompt,
session_key=f"task:{task.id}",
agent_id=agent.slug,
timeout=600
)
# Обновляем статус
task.status = "in_progress"
task.openclaw_session = result.get("sessionKey")
await task.save()
return result
```
### Получение результатов
**Вариант 1: Polling**
```python
async def poll_task_result(task: Task):
client = OpenClawClient(...)
# Получаем историю сессии
history = await client.get_session_history(task.openclaw_session)
# Парсим последний ответ
last_message = history["messages"][-1]
if last_message["role"] == "assistant":
task.result = last_message["content"]
task.status = "completed"
await task.save()
```
**Вариант 2: Callback Webhook**
```python
# Team Board получает callback от OpenClaw
@app.post("/api/callbacks/openclaw")
async def openclaw_callback(data: dict):
session_key = data.get("sessionKey")
result = data.get("result")
# Находим задачу по session_key
task = await Task.find_by_session(session_key)
task.result = result
task.status = "completed"
await task.save()
```
---
## Текущий статус OpenClaw
```bash
# Gateway endpoint
http://localhost:18789
# Текущий токен
cat ~/.openclaw/openclaw.json | jq '.gateway.auth.token'
# Webhooks пока не настроены — нужно включить
```
### Для активации webhooks:
```json
// ~/.openclaw/openclaw.json
{
"hooks": {
"enabled": true,
"token": "team-board-secret-token",
"path": "/hooks"
}
}
```
---
*Документ обновляется по мере исследования.*