docs/archive/RESEARCH.md

21 KiB
Raw Blame History

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 — таймаут
  • cleanupdelete|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:

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

Конфиг:

{
  "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
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()
  1. ACP Translator
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)

Схема контекста:

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)

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 (БД)

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

Конфигурация

{
  "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 — Разбудить основную сессию

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) — описание события
  • modenow (сразу) или next-heartbeat (при след. проверке)

POST /hooks/agent — Запустить изолированную сессию

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)

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

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

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

# 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

# Gateway endpoint
http://localhost:18789

# Текущий токен
cat ~/.openclaw/openclaw.json | jq '.gateway.auth.token'

# Webhooks пока не настроены — нужно включить

Для активации webhooks:

// ~/.openclaw/openclaw.json
{
  "hooks": {
    "enabled": true,
    "token": "team-board-secret-token",
    "path": "/hooks"
  }
}

Документ обновляется по мере исследования.