docs: add OpenClaw integration architecture

- OpenClawClient Python class
- Agent model with status tracking
- Task assignment flow
- Callback endpoint
This commit is contained in:
Markov 2026-02-15 15:41:37 +01:00
parent 438a8aaa88
commit bbbc65bc48

198
README.md
View File

@ -9,7 +9,7 @@ API сервисы для Team Board. Python + FastAPI, микросервисн
| gateway | 8000 | API Gateway, аутентификация |
| projects | 8001 | Проекты, Git интеграция |
| tasks | 8002 | Задачи, канбан, подзадачи |
| agents | 8003 | AI агенты |
| agents | 8003 | AI агенты, OpenClaw интеграция |
| chat | 8004 | Чаты проектов |
## Структура
@ -22,10 +22,196 @@ backend/
│ ├── tasks/
│ ├── agents/
│ └── chat/
├── shared/
│ └── openclaw_client.py
├── docker-compose.yml
└── README.md
```
## OpenClaw Integration
### Архитектура
```
Team Board (agents service)
│ HTTP POST /hooks/agent
OpenClaw Gateway (localhost:18789)
│ sessions_spawn
Субагенты (изолированные сессии)
│ результат
Callback → Team Board
```
### Конфигурация
```env
# OpenClaw
OPENCLAW_URL=http://localhost:18789
OPENCLAW_TOKEN=team-board-secret-token
```
### OpenClaw Client
```python
# shared/openclaw_client.py
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") -> dict:
"""Разбудить основную сессию"""
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
) -> dict:
"""Запустить задачу в изолированной сессии"""
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()
async def get_session_history(self, session_key: str) -> dict:
"""Получить историю сессии"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"{self.base_url}/api/sessions/{session_key}/history",
headers=self.headers
)
return response.json()
```
## Agents Service
### Модели
```python
# services/agents/models.py
from sqlalchemy import Column, String, Integer, Text, Boolean
from sqlalchemy.dialects.postgresql import UUID, TIMESTAMPTZ
from shared.database import Base
import uuid
class Agent(Base):
__tablename__ = "agents"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(100), nullable=False)
slug = Column(String(50), unique=True, nullable=False)
# Настройки
model = Column(String(100), default="claude-opus-4-5")
system_prompt = Column(Text)
max_concurrent = Column(Integer, default=1)
# Статус
status = Column(String(20), default="idle")
current_tasks = Column(Integer, default=0)
is_enabled = Column(Boolean, default=True)
# Timestamps
created_at = Column(TIMESTAMPTZ, server_default="now()")
updated_at = Column(TIMESTAMPTZ, server_default="now()", onupdate="now()")
```
### API Endpoints
```python
# services/agents/routes.py
from fastapi import APIRouter, Depends, HTTPException
from .models import Agent
from .schemas import AgentCreate, AgentUpdate, TaskAssignment
from shared.openclaw_client import OpenClawClient
router = APIRouter(prefix="/agents", tags=["agents"])
@router.get("/")
async def list_agents():
"""Список всех агентов"""
pass
@router.post("/")
async def create_agent(data: AgentCreate):
"""Создать нового агента"""
pass
@router.put("/{agent_id}")
async def update_agent(agent_id: str, data: AgentUpdate):
"""Обновить агента"""
pass
@router.post("/{agent_id}/assign")
async def assign_task(agent_id: str, data: TaskAssignment):
"""Назначить задачу агенту"""
agent = await Agent.get(agent_id)
if not agent.is_enabled:
raise HTTPException(400, "Agent is disabled")
if agent.current_tasks >= agent.max_concurrent:
raise HTTPException(400, "Agent is busy")
# Запускаем через OpenClaw
client = OpenClawClient(settings.OPENCLAW_URL, settings.OPENCLAW_TOKEN)
result = await client.run_agent(
message=data.prompt,
session_key=f"task:{data.task_id}",
agent_id=agent.slug
)
# Обновляем статус
agent.current_tasks += 1
agent.status = "working"
await agent.save()
return result
@router.post("/callback")
async def agent_callback(data: dict):
"""Callback от OpenClaw после выполнения задачи"""
session_key = data.get("sessionKey")
# Обновить задачу, уменьшить current_tasks агента
pass
```
## Запуск
```bash
@ -45,12 +231,22 @@ docker-compose up -d
- PostgreSQL
- Redis
- SQLAlchemy
- httpx (async HTTP client)
## Переменные окружения
```env
# Database
DATABASE_URL=postgresql://team_board:password@localhost:5432/team_board
# Redis
REDIS_URL=redis://localhost:6379
# Auth
AUTHENTIK_CLIENT_ID=...
AUTHENTIK_CLIENT_SECRET=...
# OpenClaw
OPENCLAW_URL=http://localhost:18789
OPENCLAW_TOKEN=team-board-secret-token
```