docs: Domain Model, State Machines, Connector API, Handoff, Operations, Security, ADR, Roadmap (+15 улучшений ТЗ)

This commit is contained in:
ochenstarik-ui 2026-07-20 18:54:01 +07:00
parent 37f058938c
commit 3f9a925f24
15 changed files with 3125 additions and 2 deletions

View file

@ -15,11 +15,35 @@
## Документы ## Документы
### Нормативные
- [Каноническое ТЗ](docs/SPECIFICATION.md) - [Каноническое ТЗ](docs/SPECIFICATION.md)
- [Архитектура](docs/ARCHITECTURE.md) - [Архитектура](docs/ARCHITECTURE.md)
- [Исследование интеграций](docs/RESEARCH.md) - [Domain Model](docs/DOMAIN_MODEL.md)
- [State Machines](docs/STATE_MACHINES.md)
- [Connector API](docs/CONNECTOR_API.md)
- [Handoff Contract](docs/HANDOFF_CONTRACT.md)
- [Матрица трассируемости](docs/TRACEABILITY.md) - [Матрица трассируемости](docs/TRACEABILITY.md)
- [Открытые решения](docs/OPEN-QUESTIONS.md) - [Открытые решения](docs/OPEN-QUESTIONS.md)
### Исследования и адаптеры
- [Исследование интеграций](docs/RESEARCH.md)
- [Hermes Agent Adapter](docs/adapters/HERMES.md)
### Паттерны
- [Worker Orchestration](docs/patterns/WORKER_ORCHESTRATION.md)
- [Credential Pools](docs/patterns/CREDENTIAL_POOLS.md)
### Операции
- [Scheduler, Events, Errors, SLO](docs/OPERATIONS.md)
- [Security & Performance](docs/SECURITY_AND_PERFORMANCE.md)
- [Monitoring & Health-check](docs/operations/MONITORING.md)
- [Backup & Recovery](docs/operations/BACKUP.md)
### Проектирование
- [ADR и Sequence Diagrams](docs/ADR_AND_SEQUENCES.md)
- [Roadmap](docs/ROADMAP.md)
### Участие
- [Правила участия](CONTRIBUTING.md) - [Правила участия](CONTRIBUTING.md)
## Ключевой принцип ## Ключевой принцип

128
docs/ADR_AND_SEQUENCES.md Normal file
View file

@ -0,0 +1,128 @@
# Architecture Decision Records
**Версия:** 0.1.0-draft | **Статус:** Draft
## Шаблон ADR
```markdown
# ADR-NNN: Краткое название
**Статус:** proposed | accepted | deprecated | superseded
**Дата:** YYYY-MM-DD
**Владельцы:** @username
**Связано:** OQ-XXX, FR-XXX, NFR-XXX
## Контекст
Описание проблемы, требующей архитектурного решения. Какие силы действуют?
Какие ограничения?
## Варианты и анализ
### Вариант A: Название
- **Плюсы:** ...
- **Минусы:** ...
- **Риски:** ...
### Вариант B: Название
- **Плюсы:** ...
- **Минусы:** ...
## Решение
Выбран **Вариант X** потому что ...
## Последствия
### Положительные
- ...
### Отрицательные
- ...
### Безопасность и приватность
- ...
### Операционные
- ...
## Миграция и откат
Как перейти от текущего состояния к новому? Как откатиться?
## Валидация
Как проверить, что решение работает?
```
## Каталог ADR
| Номер | Название | Статус | Дата |
|---|---|---|---|
| ADR-001 | Client stack: React + Tauri + Capacitor | proposed | — |
| ADR-002 | Auth: OIDC + local bootstrap | proposed | — |
| ADR-003 | Persistence: PostgreSQL + Object Storage | proposed | — |
| ADR-004 | WSS transport for Connector | proposed | — |
| ADR-005 | Adapter pattern: versioned, capability-based | proposed | — |
| ADR-006 | Handoff format: JSON Schema v1 | proposed | — |
| ADR-007 | Event Bus: PostgreSQL NOTIFY/LISTEN vs Redis | proposed | — |
| ADR-008 | Memory embeddings: pgvector vs external | proposed | — |
| ADR-009 | Connector sandbox: container vs systemd | proposed | — |
| ADR-010 | API versioning: URL-based (/v1, /v2) | proposed | — |
## Sequence Diagrams (текстовые)
### Запуск проекта (happy path)
```
User → API: POST /projects (name, workspace)
API → DB: INSERT project
API → User: 201 { project_id }
User → API: POST /tasks (project_id, title)
API → Scheduler: enqueue(task)
Scheduler → Connector: create_run(agent, goal)
Connector → Agent: run(goal)
Agent → Connector: stream_events(...)
Connector → API: stream_events(...)
API → User: WSS push events
Agent → Connector: run_completed
Connector → API: run_completed
API → User: notification
```
### Handoff
```
Agent_A → Connector_A: handoff(agent_B, bundle)
Connector_A → API: handoff_request
API → Scheduler: enqueue(agent_B, bundle)
Scheduler → Connector_B: create_run(agent_B, bundle)
Agent_B → Connector_B: run_accepted
Connector_B → API: handoff_ack
API → Connector_A: handoff_ack
```
### Approval
```
Agent → Connector: approval_request(action="rm -rf /build", risk=high)
Connector → API: approval_request
API → EventBus: approval.requested
EventBus → User: push notification
User → API: POST /approvals/{id}/approve
API → Connector: approval_granted
Connector → Agent: continue
Agent → Connector: action_executed
```
### Восстановление после reconnect
```
Connector → API: register (reconnect)
API → Connector: register_ack + pending_commands[]
Connector → Agent: resume(run_id)
Agent → Connector: stream_events (с последнего checkpoint)
Connector → API: stream_events
API → User: events replayed
```

228
docs/CONNECTOR_API.md Normal file
View file

@ -0,0 +1,228 @@
# Connector API v1
**Версия:** 0.1.0-draft | **Статус:** Draft
## Transport
- **Протокол:** WSS (WebSocket Secure) + HTTPS fallback
- **Аутентификация:** mTLS (клиентский сертификат) + JWT
- **Формат:** JSON (все payloads)
- **Сжатие:** per-message deflate (опционально)
- **Keepalive:** ping/pong каждые 30 секунд
## Endpoints
### 1. register
Регистрация коннектора при первом подключении.
```json
// → Request
{
"type": "register",
"connector_id": "uuid",
"version": "1.0.0",
"host": "server01.example.com",
"adapters": ["hermes", "openclaw"]
}
// ← Response
{
"type": "register_ack",
"connector_id": "uuid",
"server_time": "2026-07-20T12:00:00Z",
"config": { ... }
}
```
### 2. heartbeat
Периодический health-check.
```json
// → Request (каждые 30s)
{
"type": "heartbeat",
"connector_id": "uuid",
"timestamp": "2026-07-20T12:00:30Z",
"metrics": {
"cpu_pct": 45.2,
"memory_mb": 512,
"active_runs": 2,
"queued_runs": 1
}
}
// ← Response
{ "type": "heartbeat_ack" }
```
### 3. capabilities
Объявление возможностей коннектора и его агентов.
```json
// → Request
{
"type": "capabilities",
"connector_id": "uuid",
"agents": [{
"agent_id": "uuid",
"runtime": "hermes",
"model": "opencode-go/kimi-k2.7-code",
"tools": ["terminal", "browser", "file", "delegation"],
"max_turns": 60,
"supports": ["streaming", "cancellation", "handoff"]
}]
}
```
### 4. create_run
Запуск задачи на агенте.
```json
// → Request
{
"type": "create_run",
"run_id": "uuid",
"agent_id": "uuid",
"goal": "Research GRPO papers",
"context_bundle": {
"objective": "...",
"acceptance_criteria": ["..."],
"memory_keys": ["key1", "key2"],
"artifacts": ["artifact-id-1"]
},
"constraints": {
"max_tokens": 100000,
"timeout_seconds": 600,
"tools": ["terminal", "browser"]
}
}
// ← Response
{
"type": "run_accepted",
"run_id": "uuid",
"status": "queued"
}
```
### 5. stream_events
Поток событий от агента (server → client push).
```json
{
"type": "run_event",
"run_id": "uuid",
"event": "tool_call",
"timestamp": "2026-07-20T12:01:00Z",
"payload": {
"tool": "terminal",
"command": "ls -la",
"output": "total 48\n..."
}
}
```
Типы событий: `thinking`, `tool_call`, `tool_result`, `progress`, `warning`, `error`, `completion`.
### 6. cancel_run
Отмена запущенной задачи.
```json
// → Request
{ "type": "cancel_run", "run_id": "uuid" }
// ← Response
{ "type": "run_cancelled", "run_id": "uuid" }
```
### 7. handoff
Передача контекста между агентами.
```json
// → Request
{
"type": "handoff",
"from_run_id": "uuid",
"to_agent_id": "uuid",
"bundle": {
"objective": "...",
"progress": "...",
"decisions": ["..."],
"artifacts": ["id1"],
"open_questions": ["..."]
}
}
```
### 8. upload_artifact / download_artifact
Загрузка/выгрузка артефактов через Object Storage.
```json
// → upload_artifact
{
"type": "upload_artifact",
"run_id": "uuid",
"name": "results.csv",
"content_type": "text/csv",
"size_bytes": 1024
}
// ← Response: { "upload_url": "https://...", "artifact_id": "uuid" }
// → download_artifact
{ "type": "download_artifact", "artifact_id": "uuid" }
// ← Response: { "download_url": "https://..." }
```
### 9. approve
Запрос подтверждения опасного действия.
```json
// → Request (connector → server)
{
"type": "approval_request",
"run_id": "uuid",
"action": "rm -rf /tmp/build",
"risk_level": "high"
}
// ← Response (server → connector, after human approval)
{
"type": "approval_granted",
"approval_id": "uuid",
"approved_by": "user@example.com"
}
```
## Коды ошибок
| Код | Описание |
|---|---|
| 4001 | Invalid request format |
| 4002 | Unknown message type |
| 4003 | Agent not found |
| 4004 | Run not found |
| 4005 | Agent busy (max concurrent runs) |
| 4006 | Quota exceeded |
| 4007 | Unauthorized action |
| 4008 | Approval denied |
| 5001 | Connector internal error |
| 5002 | Adapter error |
## Retry Policy
| Ошибка | Стратегия |
|---|---|
| Network timeout | Exponential backoff: 1s, 2s, 4s, 8s, 16s, затем каждые 30s |
| 5001/5002 | Мгновенный retry × 3, затем fail |
| 4005 | Отложить run в очередь, retry через 60s |
| 4006 | Остановить run, уведомить operator |
## Таймауты
| Операция | Таймаут |
|---|---|
| register | 10s |
| heartbeat response | 5s |
| create_run accept | 30s |
| stream_event доставка | 60s (затем reconnect) |
| cancel_run подтверждение | 15s |
| upload_artifact URL | 300s |

203
docs/DOMAIN_MODEL.md Normal file
View file

@ -0,0 +1,203 @@
# Domain Model — Agent Control Center
**Версия:** 0.1.0-draft
**Статус:** Draft
**Нормативность:** supporting; при конфликте приоритет у SPECIFICATION.md
## Сущности
### Organization
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | Первичный ключ |
| name | string | Название организации |
| slug | string | Уникальный идентификатор для URL |
| billing_email | string | Email для биллинга |
| created_at | timestamptz | Дата создания |
**Связи:** 1 → N Workspace
### Workspace
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| organization_id | UUID | FK → Organization |
| name | string | Название рабочего пространства |
| owner_account_id | UUID | FK → Account (владелец) |
| ai_lockout | boolean | Блокировка AI-действий |
| created_at | timestamptz | |
**Связи:** 1 → N Project, 1 → N Member
### Project
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| workspace_id | UUID | FK → Workspace |
| name | string | |
| description | text | |
| status | enum | active, archived, deleted |
| created_at | timestamptz | |
**Связи:** 1 → N Task, 1 → N Artifact
### Task
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| project_id | UUID | FK → Project |
| title | string | |
| description | text | |
| priority | enum | low, medium, high, critical |
| status | enum | backlog, todo, in_progress, review, done |
| assignee_id | UUID | FK → Account (опционально) |
| created_at | timestamptz | |
**Связи:** 1 → N Run
### Run
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| task_id | UUID | FK → Task |
| agent_id | UUID | FK → Agent |
| connector_id | UUID | FK → Connector |
| status | enum | см. State Machine |
| goal | text | Цель запуска |
| context_bundle | jsonb | Bounded context |
| started_at | timestamptz | |
| completed_at | timestamptz | |
| error_message | text | |
### Agent
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| connector_id | UUID | FK → Connector |
| name | string | |
| runtime | enum | hermes, openclaw, claude, codex, gemini, generic |
| model | string | Идентификатор модели |
| tools | jsonb | Доступные инструменты |
| capabilities | jsonb | FK → Capability Registry |
| config_ref | string | Ссылка на конфигурацию |
### Connector
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| name | string | |
| host | string | Адрес сервера |
| status | enum | online, offline, degraded |
| version | string | Версия коннектора |
| last_heartbeat | timestamptz | |
| capabilities | jsonb | |
### Artifact
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| project_id | UUID | FK → Project |
| run_id | UUID | FK → Run (опционально) |
| name | string | |
| type | string | MIME-тип |
| size_bytes | bigint | |
| storage_key | string | Ключ в Object Storage |
| checksum | string | SHA-256 |
| created_at | timestamptz | |
### Memory
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| scope | string | user, project, workspace |
| scope_id | UUID | ID области видимости |
| key | string | Ключ записи |
| value | text | Содержимое |
| provenance | string | Источник (agent_id, user_id) |
| version | int | Версия записи |
| created_at | timestamptz | |
### Skill
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| name | string | Уникальное имя |
| version | string | Семантическая версия |
| content | text | SKILL.md |
| signature | text | Подпись (опционально) |
| review_status | enum | draft, reviewed, approved, deprecated |
| created_by | string | agent_id или user_id |
| created_at | timestamptz | |
### WikiPage
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| workspace_id | UUID | FK → Workspace |
| path | string | Путь страницы |
| title | string | |
| content | text | Markdown |
| version | int | |
| updated_at | timestamptz | |
### Approval
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| run_id | UUID | FK → Run |
| action | string | Описание действия |
| risk_level | enum | low, medium, high, critical |
| requested_by | UUID | Кто запросил |
| status | enum | pending, approved, denied, expired |
| decided_by | UUID | Кто принял решение |
| created_at | timestamptz | |
| decided_at | timestamptz | |
### AuditEvent
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| event_type | string | Тип события |
| actor_id | UUID | Кто совершил |
| target_type | string | Тип цели |
| target_id | UUID | ID цели |
| payload | jsonb | Детали события |
| created_at | timestamptz | |
### User / Account
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| email | string | Уникальный |
| display_name | string | |
| password_hash | string | |
| mfa_enabled | boolean | |
| created_at | timestamptz | |
### Role
| Поле | Тип | Описание |
|---|---|---|
| id | UUID | PK |
| name | string | owner, admin, lead, operator, contributor, viewer |
| permissions | jsonb | Список разрешений |
**Связи:** User N ↔ M Role (через user_roles)
## Индексы
```sql
-- Поиск задач по проекту и статусу
CREATE INDEX idx_task_project_status ON task(project_id, status);
-- Поиск запусков по агенту и статусу
CREATE INDEX idx_run_agent_status ON run(agent_id, status);
-- Поиск артефактов по проекту
CREATE INDEX idx_artifact_project ON artifact(project_id);
-- Аудит по времени
CREATE INDEX idx_audit_created ON audit_event(created_at DESC);
-- Поиск memory по scope
CREATE INDEX idx_memory_scope ON memory(scope, scope_id, key);
```

211
docs/HANDOFF_CONTRACT.md Normal file
View file

@ -0,0 +1,211 @@
# Handoff Contract v1
**Версия:** 0.1.0-draft | **Статус:** Draft
## Назначение
Контракт определяет формат передачи контекста между агентами при handoff.
Гарантирует, что принимающий агент получает минимально достаточный bounded context
для продолжения работы без повторного исследования.
## JSON Schema
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://agent-control-center/schemas/handoff-v1.json",
"type": "object",
"required": ["handoff_id", "from_agent", "to_agent", "objective", "bundle"],
"properties": {
"handoff_id": { "type": "string", "format": "uuid" },
"version": { "const": "1.0" },
"timestamp": { "type": "string", "format": "date-time" },
"from_agent": {
"type": "object",
"required": ["agent_id", "runtime"],
"properties": {
"agent_id": { "type": "string" },
"runtime": { "enum": ["hermes", "openclaw", "claude", "codex", "gemini"] },
"model": { "type": "string" },
"run_id": { "type": "string" }
}
},
"to_agent": {
"type": "object",
"required": ["agent_id"],
"properties": {
"agent_id": { "type": "string" },
"expected_capabilities": {
"type": "array",
"items": { "type": "string" }
}
}
},
"objective": {
"type": "object",
"required": ["goal"],
"properties": {
"goal": { "type": "string", "maxLength": 500 },
"acceptance_criteria": {
"type": "array",
"items": { "type": "string" },
"maxItems": 10
}
}
},
"bundle": {
"type": "object",
"properties": {
"progress": {
"type": "object",
"properties": {
"summary": { "type": "string", "maxLength": 1000 },
"completed_steps": {
"type": "array",
"items": { "type": "string" },
"maxItems": 20
},
"current_step": { "type": "string" }
}
},
"decisions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"decision": { "type": "string" },
"rationale": { "type": "string" },
"alternatives_considered": {
"type": "array",
"items": { "type": "string" }
}
}
},
"maxItems": 15
},
"artifacts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"artifact_id": { "type": "string" },
"name": { "type": "string" },
"type": { "type": "string" },
"relevance": { "type": "string", "maxLength": 200 }
}
},
"maxItems": 20
},
"memory": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": { "type": "string" },
"summary": { "type": "string", "maxLength": 300 },
"provenance": { "type": "string" }
}
},
"maxItems": 10
},
"open_questions": {
"type": "array",
"items": { "type": "string" },
"maxItems": 10
},
"constraints": {
"type": "object",
"properties": {
"must_not_change": {
"type": "array",
"items": { "type": "string" }
},
"budget_remaining": { "type": "number" },
"deadline": { "type": "string", "format": "date-time" }
}
}
}
}
}
}
```
## Ограничения размера
| Поле | Макс. размер |
|---|---|
| handoff целиком | 64 KB |
| objective.goal | 500 символов |
| progress.summary | 1000 символов |
| decisions | 15 записей |
| artifacts | 20 ссылок |
| memory keys | 10 ключей |
## Пример
```json
{
"handoff_id": "a1b2c3d4-...",
"version": "1.0",
"timestamp": "2026-07-20T12:00:00Z",
"from_agent": {
"agent_id": "worker-research",
"runtime": "hermes",
"model": "opencode-go/kimi-k2.7-code",
"run_id": "run-123"
},
"to_agent": {
"agent_id": "worker-code",
"expected_capabilities": ["terminal", "file", "python"]
},
"objective": {
"goal": "Реализовать EMA-кроссовер стратегию",
"acceptance_criteria": [
"Код проходит тесты",
"Бэктест на Brent > 0% return",
"Документация в docstring"
]
},
"bundle": {
"progress": {
"summary": "Проанализированы 3 стратегии EMA-кроссовера. Выбран вариант с period=(3,10).",
"completed_steps": ["Собраны данные Brent", "Протестированы параметры"],
"current_step": "Реализация на Python"
},
"decisions": [{
"decision": "EMA(3,10) вместо EMA(5,20)",
"rationale": "Лучше Sharpe на исторических данных",
"alternatives_considered": ["EMA(5,20)", "SMA(10,30)"]
}],
"artifacts": [{
"artifact_id": "art-001",
"name": "brent_2024.csv",
"type": "csv",
"relevance": "Рыночные данные для бэктеста"
}],
"memory": [{
"key": "brent_seasonality",
"summary": "Brent показывает сезонный рост Q2-Q3",
"provenance": "worker-research, run-123"
}],
"open_questions": [
"Учитывать ли спред при расчёте комиссии?"
],
"constraints": {
"must_not_change": ["сигнатура run_single_pass()"],
"budget_remaining": 50000
}
}
}
```
## Валидация
- schema_version MUST быть "1.0"
- handoff_id MUST быть уникальным
- bundle НЕ должен содержать provider secrets или raw chain-of-thought
- Принимающий агент SHOULD подтвердить получение в течение 30s

124
docs/OPERATIONS.md Normal file
View file

@ -0,0 +1,124 @@
# Operations: Scheduler, Events, Errors, SLO
**Версия:** 0.1.0-draft | **Статус:** Draft
## 1. Scheduler
Приоритетная очередь запусков с поддержкой fair scheduling.
### Очереди
| Очередь | Приоритет | Описание |
|---|---|---|
| Priority Queue | 0 (высший) | Критические задачи, ручной запуск |
| Fair Queue | 1 | Обычные задачи, round-robin по workspace |
| Retry Queue | 2 | Задачи после transient failure |
| Delayed Queue | 3 | Отложенные задачи (cron, schedule) |
| Approval Queue | — | Задачи, ожидающие approval (блокируют слот агента) |
### Параметры планировщика
| Параметр | Значение |
|---|---|
| Макс. concurrent runs на connector | 5 |
| Макс. concurrent runs на agent | 1 |
| Preemption | Выключена в MVP |
| Fair share | Минимум 1 слот на workspace |
| Retry backoff | 1min, 5min, 15min, 1h, затем fail |
| Max queued per workspace | 100 |
## 2. Event Bus
Durable event bus с гарантией at-least-once доставки.
### События
| Событие | Payload | Частота |
|---|---|---|
| `run.created` | run_id, task_id, agent_id, goal | ~1/min |
| `run.started` | run_id, connector_id, timestamp | ~1/min |
| `run.completed` | run_id, duration, tokens, cost | ~1/min |
| `run.failed` | run_id, error_code, error_message | ~0.1/min |
| `run.cancelled` | run_id, cancelled_by | ~0.05/min |
| `approval.requested` | approval_id, run_id, action, risk | ~2/day |
| `approval.granted` | approval_id, approved_by | ~2/day |
| `approval.denied` | approval_id, denied_by, reason | ~0.5/day |
| `connector.online` | connector_id, version, agents | ~0.1/day |
| `connector.offline` | connector_id, last_heartbeat | ~0.1/day |
| `connector.degraded` | connector_id, missed_heartbeats | ~0.2/day |
| `artifact.created` | artifact_id, run_id, name, size | ~5/min |
| `memory.updated` | memory_id, key, scope, version | ~1/min |
| `skill.registered` | skill_id, name, version | ~0.1/day |
### Гарантии доставки
| Свойство | Значение |
|---|---|
| Доставка | At-least-once |
| Порядок | Per-run (внутри одного run — строгий порядок) |
| Дедупликация | По event_id (idempotency key) |
| Хранение | 30 дней |
| Версионирование | Все схемы эволюционируют additive-only |
## 3. Error Catalog
Единый каталог ошибок с кодами по подсистемам.
| Код | Подсистема | Описание | HTTP |
|---|---|---|---|
| AUTH-001 | Auth | Invalid credentials | 401 |
| AUTH-002 | Auth | Token expired | 401 |
| AUTH-003 | Auth | Insufficient permissions | 403 |
| CONN-001 | Connector | Registration failed | 400 |
| CONN-002 | Connector | Heartbeat timeout | 408 |
| CONN-003 | Connector | Version incompatible | 409 |
| RUN-001 | Run | Agent busy | 409 |
| RUN-002 | Run | Quota exceeded | 429 |
| RUN-003 | Run | Invalid goal format | 400 |
| RUN-004 | Run | Run not found | 404 |
| APPROVAL-001 | Approval | Self-approval denied | 403 |
| APPROVAL-002 | Approval | Approval expired | 410 |
| ARTIFACT-001 | Artifact | Upload failed | 500 |
| ARTIFACT-002 | Artifact | Size limit exceeded | 413 |
| MEM-001 | Memory | Key conflict | 409 |
| SKILL-001 | Skill | Signature invalid | 403 |
| WIKI-001 | Wiki | Page locked | 423 |
## 4. SLO / SLA
Целевые показатели качества обслуживания.
### SLO (Service Level Objectives)
| Метрика | Цель | Окно |
|---|---|---|
| API availability | 99.5% | monthly |
| p95 latency (REST read) | < 200ms | rolling 1h |
| p95 latency (REST write) | < 500ms | rolling 1h |
| Event delivery (p95) | < 2s | rolling 1h |
| Run dispatch (p99) | < 10s | rolling 1h |
| Search latency (p95) | < 500ms | rolling 1h |
| Artifact upload (p95) | < 30s per MB | rolling 1h |
### SLA (Service Level Agreements) — целевые, не контрактные в MVP
| Метрика | Цель |
|---|---|
| Time to detect connector offline | < 90s |
| Time to recover connector | < 5min (ручной) |
| Max data loss (events) | < 5min окно |
| RPO (артефакты) | < 1h |
| RTO (полное восстановление) | < 4h |
## 5. API Versioning
| Версия | Статус | Дата |
|---|---|---|
| /api/v1 | Draft | Q3 2026 |
| /api/v2 | Planned | Q1 2027 (breaking changes) |
**Правила:**
- MAJOR version в URL (/api/v1, /api/v2)
- MINOR changes — additive (новые поля, эндпоинты)
- Deprecation: минимум 3 месяца уведомления через Sunset header
- Старые версии: 6 месяцев поддержки после выхода новой MAJOR

110
docs/ROADMAP.md Normal file
View file

@ -0,0 +1,110 @@
# Roadmap — Agent Control Center
**Версия:** 0.1.0-draft | **Статус:** Draft
## Фазы
### M0 — Foundations (4 недели)
**Цель:** спецификация утверждена, репозиторий готов к реализации.
| Задача | Статус |
|---|---|
| Закрыть блокирующие OQ-001..OQ-005 | ⏳ |
| Утвердить SPECIFICATION.md (G1 PASS) | ⏳ |
| Утвердить Domain Model | ⏳ |
| Утвердить State Machines | ⏳ |
| Настроить CI + линтеры | ⏳ |
| Развернуть dev-окружение | ⏳ |
### M1 — Core (8 недель)
**Цель:** минимальный Control Plane: workspace, project, task, agent registry.
| Компонент | Что входит |
|---|---|
| Identity & Policy | OIDC, local bootstrap admin |
| Project Service | CRUD projects, tasks |
| Connector Gateway | WSS, register, heartbeat |
| Agent Registry | Adapters: Hermes, OpenClaw |
| Run Orchestrator | create_run, stream_events, cancel |
| Approval Service | Basic approve/deny для destructive actions |
| Audit Service | Immutable audit log |
### M2 — Context (6 недель)
**Цель:** общий контекст между агентами.
| Компонент | Что входит |
|---|---|
| Context Broker | Bounded context bundle, redaction |
| Memory Service | Scoped memory, versions, search |
| Skill Registry | Skill upload, versioning, signing |
| Wiki Service | Markdown pages, links, search |
| Handoff | Полный handoff contract |
| Artifact Storage | Upload/download, checksums |
### M3 — Operations (6 недель)
**Цель:** observability, quotas, мониторинг.
| Компонент | Что входит |
|---|---|
| Usage Service | Quota tracking, budget alerts |
| Scheduler | Priority + Fair Queue |
| Event Bus | Durable delivery, replay |
| Monitoring | Health dashboard, SLO метрики |
| Backup/Restore | Git-based config backup |
| Error Catalog | Единые коды ошибок |
### M4 — Clients (8 недель)
**Цель:** три клиента с общим UI-ядром.
| Клиент | Платформа |
|---|---|
| Web / PWA | React + TypeScript |
| Desktop | Tauri (Windows, macOS, Linux) |
| Android | Capacitor wrapper |
**Функциональность клиентов:**
- Workspace/project/task management
- Agent dashboard + live run monitor
- Approval UI (push notifications)
- Memory/Wiki browser
- Skill browser + install
- Kanban board
### M5 — Enterprise (8 недель)
**Цель:** production-ready для нескольких tenants.
| Компонент | Что входит |
|---|---|
| Multi-tenancy | Организации, SSO/SAML |
| Connector sandbox | Container-based (Docker/podman) |
| Advanced approval | Dual-control, custom policies |
| Notifications | Email, FCM, webhook |
| SLA monitoring | SLO панель, инциденты |
| Capacity | 50 users, 20 connectors, 100 concurrent runs |
## Временная шкала
```
M0 ──── M1 ──────── M2 ────── M3 ────── M4 ──────── M5 ──────
│ │ │ │ │ │ │
0 4 12 18 24 32 40 недель
```
## Зависимости между фазами
```
M0 (Spec) ──► M1 (Core) ──► M2 (Context) ──► M3 (Ops)
│ │
└──────► M4 (Clients) ◄┘
└──► M5 (Enterprise)
```
## Критерии готовности MVP (M1+M2)
- [ ] 1 workspace, 1 project, 3 агента (Hermes) — create/run/monitor
- [ ] Handoff между 2 агентами — контекст не потерян
- [ ] Approval для destructive action — работает через UI
- [ ] Memory/Wiki — запись и поиск
- [ ] Web-клиент — полный цикл управления
- [ ] 5 последовательных ручных прогонов без ошибок

View file

@ -0,0 +1,95 @@
# Security & Performance
**Версия:** 0.1.0-draft | **Статус:** Draft
## 1. Security
### 1.1 Ротация секретов
| Секрет | Период ротации | Автоматически |
|---|---|---|
| Connector mTLS cert | 90 дней | ✅ (ACME/внутренний CA) |
| API signing keys | 30 дней | ✅ |
| OAuth refresh tokens | По истечению | ✅ (провайдер) |
| User sessions | 24 часа | ✅ (JWT expiry) |
| API keys (provider) | Вручную | ❌ |
### 1.2 Управление ключами
- **Иерархия:** Root CA → Intermediate CA → Connector cert
- **Хранение:** Vault/HSM для production; зашифрованный файл для dev
- **Доступ:** Только Connector Gateway (machine identity)
- **Отзыв:** CRL + OCSP, задержка распространения ≤ 5 минут
### 1.3 Шифрование
| Уровень | Метод |
|---|---|
| Transport | TLS 1.3 (min) |
| Data at rest | AES-256-GCM (Object Storage, DB) |
| Secrets | Vault transit engine |
| Backups | GPG (асимметричное, offline-ключ) |
### 1.4 Аудит
- Все события → append-only audit log
- Защита от удаления/модификации: WORM storage (30 дней min)
- Экспорт: CSV/JSON, фильтрация по actor, target, time range
- Критические события: real-time alert (connector offline, mass approval deny)
### 1.5 Отзыв коннекторов
- **Немедленный:** Revoke cert → disconnect active WSS → block re-registration
- **Плановый:** Deprecation notice (7 дней) → revoke → cleanup
- **Аварийный:** One-click kill switch (Operator/Admin role)
### 1.6 Резервное копирование
- Конфигурация: Git-репо (hermes-config-backup паттерн)
- Базы данных: ежедневно, retention 30 дней
- Проверка восстановления: ежемесячно (automated restore test)
## 2. Performance Targets
### 2.1 Нагрузка MVP
| Параметр | Значение |
|---|---|
| Simultaneous users | 5 (MVP), 50 (M5) |
| Active connectors | 3 (MVP), 20 (M5) |
| Concurrent runs | 10 (MVP), 100 (M5) |
| Events/sec | 5 (MVP), 50 (M5) |
| Total artifacts | 1 000 (MVP), 100 000 (M5) |
| Wiki pages | 100 (MVP), 10 000 (M5) |
| Memory entries | 1 000 (MVP), 100 000 (M5) |
### 2.2 Latency Budget
| Компонент | Бюджет (p95) |
|---|---|
| API Gateway | 50ms |
| Auth (JWT verify) | 10ms |
| DB query (read) | 20ms |
| DB query (write) | 50ms |
| Object Storage | 100ms |
| Event Bus publish | 30ms |
| **Total REST read** | **≤ 200ms** |
| **Total REST write** | **≤ 500ms** |
### 2.3 Throughput
| Операция | Цель |
|---|---|
| REST API req/sec | 100 (однопоточный) |
| WSS connections | 50 одновременных |
| Event delivery | 100 events/sec |
| Search queries | 20 QPS |
### 2.4 Capacity Planning
| Ресурс | Min (MVP) | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 4 GB | 8 GB |
| Disk | 20 GB SSD | 100 GB SSD |
| Network | 100 Mbps | 1 Gbps |

View file

@ -7,7 +7,7 @@
| Статус | **Draft — implementation blocked** | | Статус | **Draft — implementation blocked** |
| Владелец решения | Product owner / operator | | Владелец решения | Product owner / operator |
| Нормативный документ | Этот файл | | Нормативный документ | Этот файл |
| Поддерживающие документы | `ARCHITECTURE.md`, `RESEARCH.md`, `OPEN-QUESTIONS.md` | | Поддерживающие документы | `ARCHITECTURE.md`, `RESEARCH.md`, `OPEN-QUESTIONS.md`, `adapters/HERMES.md`, `patterns/WORKER_ORCHESTRATION.md`, `patterns/CREDENTIAL_POOLS.md`, `operations/MONITORING.md`, `operations/BACKUP.md` |
> Разработка продукта не начинается до закрытия блокирующих решений, G1 PASS и явного утверждения оператором. Формулировки MUST/SHALL обязательны; SHOULD требуют обоснования отклонения. > Разработка продукта не начинается до закрытия блокирующих решений, G1 PASS и явного утверждения оператором. Формулировки MUST/SHALL обязательны; SHOULD требуют обоснования отклонения.

118
docs/STATE_MACHINES.md Normal file
View file

@ -0,0 +1,118 @@
# State Machines — Agent Control Center
**Версия:** 0.1.0-draft | **Статус:** Draft
## Run State Machine
```
┌─────────┐
│ QUEUED │
└────┬────┘
│ dispatch
┌────▼────┐
│ PENDING │
└────┬────┘
│ connector accepts
┌────▼────┐
│ RUNNING │◄──────────────┐
└────┬────┘ │
┌──────────────┼──────────────┐ │
▼ ▼ ▼ │
┌──────────┐ ┌───────────┐ ┌──────────┐ │
│COMPLETED │ │ FAILED │ │CANCELLED │ │
└──────────┘ └───────────┘ └──────────┘ │
┌──────────┐ │
│ PAUSED │───────────────────────────────┘
└──────────┘ resume
```
**Переходы:**
| Из | В | Условие |
|---|---|---|
| QUEUED | PENDING | Scheduler dispatch |
| PENDING | RUNNING | Connector accept |
| PENDING | FAILED | Timeout / connector offline |
| RUNNING | COMPLETED | Agent finished successfully |
| RUNNING | FAILED | Error / timeout |
| RUNNING | CANCELLED | User cancel / policy |
| RUNNING | PAUSED | User pause / preemption |
| PAUSED | RUNNING | User resume |
| PAUSED | CANCELLED | User cancel while paused |
**Инварианты:**
- COMPLETED/FAILED/CANCELLED — терминальные, не могут переходить
- Только один Run активен на Agent одновременно (MUST)
- CANCELLED → terminal; cleanup resources within 30s (SHOULD)
## Task State Machine
```
┌─────────┐ ┌──────────┐ ┌───────────┐
│ BACKLOG │────►│ TODO │────►│ IN_PROGRESS│
└─────────┘ └──────────┘ └─────┬─────┘
▲ │
│ ┌────────────┼────────────┐
│ ▼ ▼ ▼
│ ┌─────────┐ ┌─────────┐ ┌──────┐
└──────────────│ DONE │ │ BLOCKED │ │ REVIEW│
└─────────┘ └─────────┘ └──┬───┘
┌─────────┐
│ DONE │
└─────────┘
```
## Project State Machine
```
┌─────────┐ ┌────────┐ ┌──────────┐
│ DRAFT │────►│ ACTIVE │────►│ ARCHIVED │
└─────────┘ └────────┘ └──────────┘
│ ▲
└───────────────┘
reactivate
```
## Connector State Machine
```
┌──────────┐ ┌────────┐ ┌───────────┐
│REGISTERING│───►│ ONLINE │────►│ DEGRADED │
└──────────┘ └───┬────┘ └─────┬─────┘
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│ OFFLINE │◄───│ OFFLINE │
└─────────┘ └──────────┘
```
**Переходы:**
| Событие | Переход |
|---|---|
| Heartbeat OK (каждые 30s) | → ONLINE |
| Heartbeat missed × 3 | ONLINE → DEGRADED |
| Heartbeat missed × 10 | DEGRADED → OFFLINE |
| Reconnect | OFFLINE → ONLINE |
## Approval State Machine
```
┌─────────┐ ┌───────────┐
│ PENDING │────►│ APPROVED │
└────┬────┘ └───────────┘
├──────────►┌──────────┐
│ │ DENIED │
│ └──────────┘
└──────────►┌──────────┐
│ EXPIRED │ (timeout 5 min)
└──────────┘
```
**Правила:**
- PENDING → APPROVED/DENIED: только authorized approver (не self-approval для high/critical)
- PENDING → EXPIRED: автоматически через 5 минут
- EXPIRE → новый Approval (MUST)

349
docs/adapters/HERMES.md Normal file
View file

@ -0,0 +1,349 @@
# Hermes Agent Adapter Specification
**Версия:** 0.1.0-draft
**Дата:** 2026-07-20
**Статус:** Draft — implementation blocked
**Зависит от:** `SPECIFICATION.md` (§6.2 Connectors and agents), `ARCHITECTURE.md` (§4 Connector), `RESEARCH.md`
> Этот документ детализирует адаптер Hermes Agent в рамках Agent Adapter Protocol (AAP) согласно `SPECIFICATION.md`. Реализация запрещена до закрытия G0/G1 и явного утверждения оператором. Формулировки MUST/SHALL обязательны; SHOULD требуют обоснования отклонения.
## 1. Обзор
Hermes Agent — open-source фреймворк ИИ-агентов от Nous Research, работающий в терминале, desktop-приложении, мессенджерах и IDE. Адаптер ACC подключается через HTTP API Hermes (`/v1/chat/completions`, `/v1/responses`, `/v1/runs`) и использует систему профилей для изоляции рабочих нагрузок.
Hermes обеспечивает: provider-agnostic model routing, credential pools, профили, навыки (skills), память, cron, gateway (Telegram/20+ платформ), делегирование, сессионный поиск, tools (terminal, browser, file, code execution), плагины, MCP-серверы и webhook'и.
## 2. Профили
Hermes использует профили для изоляции независимых экземпляров агента. Каждый профиль имеет собственный `config.yaml`, `.env`, `skills/`, `plugins/`, `cron/` и `memories/`.
### 2.1 Базовый профиль
| Профиль | Назначение | Модель по умолчанию | Провайдер |
|---|---|---|---|
| `default` | Управляющий оркестратор | `deepseek-v4-pro` | `nvidia` |
Профиль `default` является точкой входа: принимает пользовательские запросы, маршрутизирует подзадачи worker-профилям через `delegate_task`, агрегирует результаты.
### 2.2 Worker-профили
| Профиль | Назначение | Модель | Провайдер | Типовые задачи |
|---|---|---|---|---|
| `worker-code` | Разработка и генерация кода | `kimi-k2.7-code` | `opencode-go` | Написание, рефакторинг, отладка кода; PR; code review |
| `worker-fast` | Быстрые задачи, не требующие глубокого анализа | `grok-4.5` | `fireworks` | Простые скрипты, форматирование, быстрый поиск |
| `worker-research` | Исследования и анализ | `gemini-3-flash-preview` | `gemini` | Анализ документации, исследование API, literature review |
| `worker-review` | Рецензирование и проверка качества | `deepseek-v4-pro` | `nvidia` | Code review, security audit, проверка спецификаций |
Каждый worker-профиль SHOULD иметь ограниченный набор инструментов, соответствующий его роли.
## 3. Модели и провайдеры
### 3.1 Модели
| Модель | Провайдер | Контекст (прибл.) | Назначение | Статус |
|---|---|---|---|---|
| `kimi-k2.7-code` | `opencode-go` | 128K | Генерация кода, сложный рефакторинг | Активен |
| `grok-4.5` | `fireworks` | 128K | Быстрые ответы, простые задачи | Активен |
| `gemini-3-flash-preview` | `gemini` | 1M | Исследования, анализ больших документов | Активен |
| `deepseek-v4-pro` | `nvidia` | 128K | Оркестрация, review, сложное рассуждение | Активен |
Модели резервируются: при недоступности одной моделью SHOULD срабатывать fallback-цепочка (см. `WORKER_ORCHESTRATION.md`).
### 3.2 Провайдеры
| Провайдер | Тип | Аутентификация | Env-переменная |
|---|---|---|---|
| `opencode-go` | OpenCode Go API | API key | `OPENCODE_GO_API_KEY` |
| `fireworks` | OpenAI-совместимый кастомный | API key | `FIREWORKS_API_KEY` |
| `gemini` | Google Gemini API | API key | `GOOGLE_API_KEY` или `GEMINI_API_KEY` |
| `nvidia` | NVIDIA AI API | API key | `NVIDIA_API_KEY` |
| `ollama` | Локальный | Без аутентификации | — |
**Custom OpenAI-совместимый провайдер (`fireworks`):** настраивается через `model.base_url` и `model.api_key` в `config.yaml`. Должен реализовывать `/v1/chat/completions` с поддержкой tool calling. Конфигурация:
```yaml
model:
provider: fireworks
base_url: "https://api.fireworks.ai/inference/v1"
api_key: "${FIREWORKS_API_KEY}"
model: grok-4.5
```
## 4. Инструменты
Hermes предоставляет следующие инструментальные наборы (toolsets), релевантные для адаптера:
| Инструмент | Назначение | Доступность в профилях |
|---|---|---|
| `terminal` | Выполнение shell-команд и управление процессами | Все профили |
| `browser` | Браузерная автоматизация (Chromium) | `default`, `worker-research` |
| `file` | Чтение/запись/поиск/редактирование файлов | Все профили |
| `delegation` | Делегирование подзадач sub-agent'ам | `default` (оркестратор) |
| `cronjob` | Управление запланированными задачами | `default` |
| `memory` | Персистентная память между сессиями | `default` |
| `session_search` | Поиск по истории сессий | `default` |
| `skills` | Просмотр и установка навыков | `default` |
| `gateway` | Управление gateway-сообщениями | `default` |
| `web` | Веб-поиск и извлечение контента | `default`, `worker-research` |
| `code_execution` | Песочница для выполнения Python | `worker-code` |
Worker-профили SHOULD иметь минимально необходимый набор инструментов для выполнения своей роли.
### 4.1 Делегирование (`delegate_task`)
`delegate_task` порождает sub-agent с изолированным контекстом и терминальной сессией.
**Параметры:**
- `goal` (обязательный) — цель подзадачи
- `context` — контекстная информация
- `role``leaf` (по умолчанию; не может ре-делегировать) или `orchestrator` (может порождать своих worker'ов)
- `background` — если `true`, возвращает handle немедленно
- `tasks` — массив задач для параллельного выполнения (fan-out)
**Ограничения:**
- `max_spawn_depth``delegation` секции `config.yaml`) ограничивает глубину вложенности оркестраторов
- `max_concurrent_children` (по умолчанию 3) — максимум параллельных детей
- Максимальное количество итераций: `delegation.max_iterations` (по умолчанию 50)
**Важно:** делегирование не durable — дочерний процесс живёт в рамках родительского процесса. Для задач, которые должны пережить перезапуск, использовать `cronjob`.
## 5. Gateway
Hermes Gateway обеспечивает подключение к 20+ мессенджинг-платформам.
### 5.1 Telegram
| Параметр | Значение |
|---|---|
| Режим | Polling (long polling) |
| Fallback IPs | Поддерживаются альтернативные IP-адреса для обхода блокировок |
| Формат сообщений | MarkdownV2 / HTML |
| Доставка уведомлений | Через gateway adapter |
| Команды | `/approve`, `/deny`, `/restart`, `/sethome`, `/status`, `/agents` |
Настройка Telegram polling:
```yaml
gateway:
platforms:
telegram:
token: "${TELEGRAM_BOT_TOKEN}"
polling: true
fallback_ips:
- "149.154.167.220"
- "149.154.167.50"
```
### 5.2 Поддерживаемые платформы
Discord, Slack, WhatsApp (Baileys + Business Cloud API), iMessage (Photon), Signal, Email, SMS, Matrix, Mattermost, Microsoft Teams, LINE, SimpleX, ntfy, Google Chat, Home Assistant, DingTalk, Feishu, WeCom, Weixin (WeChat), Raft, API Server, Webhooks.
Каждая платформа конфигурируется в секции `gateway.platforms.<name>`.
## 6. Cron
Планировщик Hermes поддерживает durable scheduled jobs.
### 6.1 Синтаксис расписаний
| Формат | Пример | Описание |
|---|---|---|
| Длительность | `"30m"`, `"2h"`, `"90s"` | Интервал между запусками |
| "Every"-фраза | `"every monday 9am"`, `"every 6 hours"` | Человеко-читаемое расписание |
| 5-полевой cron | `"0 9 * * *"` | Стандартный cron-синтаксис |
| ISO timestamp | `"2026-07-20T15:00:00Z"` | Однократный запуск |
### 6.2 Параметры задания
| Параметр | Описание |
|---|---|
| `skills` | Список навыков для загрузки |
| `model`/`provider` | Переопределение модели/провайдера |
| `script` | Pre-run скрипт сбора данных; `no_agent=True` делает скрипт единственным действием |
| `context_from` | Цепочка: вывод задания A → контекст задания B |
| `workdir` | Рабочая директория (загружает `AGENTS.md`/`CLAUDE.md` из неё при наличии) |
| Доставка | Multi-platform: Telegram, Email, Webhooks |
### 6.3 Инварианты
- 3-минутный hard interrupt на каждый run
- `.tick.lock` предотвращает дублирование тиков между процессами
- Cron-сессии передают `skip_memory=True` по умолчанию
- Доставка обрамляется header/footer, не зеркалируется в gateway-сессию
### 6.4 Watch patterns
При запуске через `terminal(background=True, watch_patterns=[...])` Hermes отслеживает вывод фонового процесса на совпадение с шаблонами. Rate limit: не более 1 уведомления в 15 секунд; после 3 последовательных окон с dropped matches отключается.
## 7. Skills (навыки)
Skills — переиспользуемые процедуры, сохраняемые агентом.
### 7.1 Жизненный цикл
```
create → use → idle → stale → archive
↑ ↓
└── restore ←──────┘
```
- Только навыки с `created_by: "agent"` подлежат автоматическому curator'у
- Bundled + hub-установленные навыки неприкосновенны
- Максимальное деструктивное действие — archive (никогда не delete)
- Pinned навыки исключены из всех авто-переходов
### 7.2 Curator
Фоновый процесс обслуживания навыков:
```bash
hermes curator status # статус
hermes curator run # ручной запуск
hermes curator pin NAME # защита от авто-архивации
hermes curator archive NAME # ручная архивация
hermes curator restore NAME # восстановление
hermes curator backup # создание снапшота
```
Consolidation (объединение пересекающихся навыков) **выключен по умолчанию** (`curator.consolidate: false`), требует aux-модель и явного включения.
### 7.3 Категории навыков
- **Встроенные** — поставляются с Hermes (code review, planning, TDD, debugging, и др.)
- **Пользовательские** — создаются агентом в процессе работы
- **Hub** — устанавливаются из реестра `hermes skills install`
## 8. Память
Hermes ведёт два файла памяти:
| Файл | Назначение |
|---|---|
| `MEMORY.md` | Факты, решения, уроки, выученные в процессе работы |
| `USER.md` | Предпочтения пользователя, окружение, постоянные параметры |
Память опционально может использовать внешние провайдеры (Honcho, Mem0) через `hermes memory setup`.
## 9. Конфигурация
### 9.1 config.yaml
Основной конфигурационный файл (`~/.hermes/config.yaml` или `$HERMES_HOME/config.yaml`). Ключевые секции для адаптера:
```yaml
model:
default: deepseek-v4-pro
provider: nvidia
base_url: "https://integrate.api.nvidia.com/v1"
agent:
max_turns: 90
tool_use_enforcement: true
terminal:
backend: local
timeout: 180
delegation:
model: kimi-k2.7-code
provider: opencode-go
max_iterations: 50
max_spawn_depth: 2
max_concurrent_children: 3
gateway:
platforms:
telegram:
token: "${TELEGRAM_BOT_TOKEN}"
polling: true
curator:
enabled: true
consolidate: false
interval_hours: 24
stale_after_days: 30
```
### 9.2 .env
Секреты и API-ключи:
```bash
OPENCODE_GO_API_KEY=sk-...
FIREWORKS_API_KEY=fw-...
GOOGLE_API_KEY=AIza...
NVIDIA_API_KEY=nvapi-...
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
```
### 9.3 Профили
Каждый профиль — изолированная директория `~/.hermes/profiles/<name>/` с собственными `config.yaml`, `.env`, `skills/`, `plugins/`, `cron/`, `memories/`.
Создание профиля:
```bash
hermes profile create worker-code
hermes profile create worker-fast
hermes profile create worker-research
hermes profile create worker-review
```
## 10. Возможности адаптера (capabilities)
При handshake с ACC адаптер Hermes публикует следующий набор capabilities:
| Capability | Значение | Примечание |
|---|---|---|
| `stream` | `true` | HTTP SSE / JSON Lines |
| `cancel` | `true` | Через `cancel` в run API |
| `checkpoint` | `true` | Observable checkpoint schema |
| `approvals` | `true` | Built-in approval flow |
| `tools` | `["terminal", "browser", "file", "web", "code_execution", ...]` | Зависит от профиля |
| `mcp` | `true` | MCP client support |
| `artifacts` | `true` | Файловая система + object refs |
| `usage_exact` | `false` | Hermes не предоставляет authoritative usage API |
| `usage_estimated` | `true` | Token usage из LLM-ответов |
| `filesystem_scope` | `["cwd", "home"]` | Ограничивается рабочей директорией |
| `structured_output` | `true` | JSON mode в моделях |
| `profiles` | `["default", "worker-code", "worker-fast", "worker-research", "worker-review"]` | Доступные профили |
| `resume` | `true` | Session resume через session ID |
## 11. Соответствие AAP
Адаптер Hermes реализует Agent Adapter Protocol (`ARCHITECTURE.md` §4.2):
| AAP-метод | Реализация в Hermes |
|---|---|
| `handshake()` | `GET /v1/status` → identity, version, capabilities |
| `health()` | `GET /v1/health` → status, latency, active_runs |
| `start(run_spec, context)` | `POST /v1/runs` → native_run_ref |
| `stream(run_ref, cursor)` | `GET /v1/runs/{id}/events?cursor=` → SSE |
| `checkpoint(run_ref, reason)` | `POST /v1/runs/{id}/checkpoint` → checkpoint bundle |
| `cancel(run_ref, mode)` | `POST /v1/runs/{id}/cancel` → outcome |
| `resume(run_ref)` | `POST /v1/runs/{id}/resume` → outcome |
| `usage(scope)` | `GET /v1/usage` → measured/estimated/unknown signal |
| `artifacts(run_ref)` | `GET /v1/runs/{id}/artifacts` → metadata + refs |
## 12. Ограничения и риски
| Ограничение | Влияние | Мера |
|---|---|---|
| Hermes не предоставляет authoritative usage API | Бюджетирование только estimated | Явная маркировка confidence; ручной budget policy |
| Делегирование не durable | Потеря задачи при падении родителя | Критичные задачи → cronjob |
| Worker-профили требуют отдельных процессов | Накладные расходы на запуск | Keep-warm пул worker'ов в production |
| Adapter версионирование отстаёт от Hermes | Incompatible handshake | Pinned version + contract tests + N-1 policy |
| Gateway polling подвержен сетевым сбоям | Задержка доставки | Fallback IPs, метрики polling latency |
## 13. Ссылки
- [Hermes Agent Documentation](https://hermes-agent.nousresearch.com/docs/)
- [Hermes API Server](https://hermes-agent.nousresearch.com/docs/user-guide/features/api-server)
- [Hermes Profiles](https://hermes-agent.nousresearch.com/docs/user-guide/profiles)
- [SPECIFICATION.md](../SPECIFICATION.md) — каноническое ТЗ ACC
- [ARCHITECTURE.md](../ARCHITECTURE.md) — архитектура ACC и AAP
- [RESEARCH.md](../RESEARCH.md) — исследование интеграций
- [WORKER_ORCHESTRATION.md](../patterns/WORKER_ORCHESTRATION.md) — паттерны оркестрации worker-профилей
- [CREDENTIAL_POOLS.md](../patterns/CREDENTIAL_POOLS.md) — паттерны credential pools
- [MONITORING.md](../operations/MONITORING.md) — мониторинг
- [BACKUP.md](../operations/BACKUP.md) — бэкап конфигурации

704
docs/operations/BACKUP.md Normal file
View file

@ -0,0 +1,704 @@
# Backup & Recovery
**Версия:** 0.1.0-draft
**Дата:** 2026-07-20
**Статус:** Draft
**Зависит от:** `SPECIFICATION.md` (§10 Data lifecycle, §7.2 Reliability), `docs/adapters/HERMES.md`
> Этот документ описывает процедуры бэкапа и восстановления конфигурации Hermes Agent в составе Agent Control Center. Реализация запрещена до G0/G1 и утверждения оператором.
## 1. Обзор
Система бэкапа ACC охватывает:
- Конфигурацию Hermes Agent (config.yaml, .env, профили)
- Пользовательские навыки (skills)
- Память (MEMORY.md, USER.md)
- Сессионные данные (опционально)
Бэкап НЕ включает:
- Секреты (API-ключи) — управляются отдельно через secret manager
- Бинарные артефакты и кэши (audio_cache, сессионные файлы)
- Логи (сохраняются согласно retention policy)
## 2. Структура конфигурации Hermes
### 2.1 Основные компоненты
```
~/.hermes/
├── config.yaml # Основная конфигурация
├── .env # Переменные окружения (API-ключи, секреты)
├── auth.json # OAuth-токены и credential pools
├── state.db # SQLite база сессий (state store)
├── profiles/ # Профили
│ ├── default/
│ │ ├── config.yaml
│ │ ├── .env
│ │ ├── skills/
│ │ └── memories/
│ ├── worker-code/
│ │ └── ...
│ ├── worker-fast/
│ │ └── ...
│ ├── worker-research/
│ │ └── ...
│ └── worker-review/
│ └── ...
├── skills/ # Пользовательские навыки
│ ├── *.md # SKILL.md файлы
│ └── .usage.json # Метаданные curator'а
├── sessions/ # Сессионные файлы (опционально)
├── logs/ # Логи (НЕ бэкапируются)
├── audio_cache/ # Аудиокэш (НЕ бэкапируется)
└── backups/ # Локальные снапшоты
├── latest/ # Последний снапшот
│ ├── config.yaml
│ ├── config.yaml.sig
│ └── manifest.json
└── archive/ # Архив снапшотов
└── 2026-07-20T14-00-00Z.tar.gz
```
### 2.2 Приоритеты бэкапа
| Компонент | Приоритет | RPO | Без секретов |
|---|---|---|---|
| `config.yaml` (все профили) | CRITICAL | 1 час | Да |
| `skills/` | HIGH | 6 часов | Да |
| `memories/` (MEMORY.md, USER.md) | HIGH | 6 часов | Да |
| `profiles/*/config.yaml` | HIGH | 1 час | Да |
| `cron/` конфигурации | MEDIUM | 24 часа | Да |
| `sessions/` | LOW | 24 часа | Нет (секреты в сессиях) |
| `state.db` | LOW | 24 часа | Нет |
## 3. Процедура снапшота
### 3.1 Скрипт: `scripts/update_backup.py`
```python
#!/usr/bin/env python3
"""
update_backup.py — создание снапшота конфигурации Hermes Agent.
Использование:
python scripts/update_backup.py [--output DIR] [--exclude-secrets] [--dry-run]
Снапшот включает:
- config.yaml всех профилей
- .env (с redacted секретами при --exclude-secrets)
- skills/ (только SKILL.md файлы)
- memories/ (MEMORY.md, USER.md)
- cron/ конфигурации
Исключает:
- sessions/
- logs/
- audio_cache/
- state.db (опционально)
- auth.json (секреты)
"""
import os
import json
import shutil
import tarfile
import hashlib
from datetime import datetime, timezone
from pathlib import Path
HERMES_HOME = Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes"))
BACKUP_DIR = HERMES_HOME / "backups"
LATEST_DIR = BACKUP_DIR / "latest"
ARCHIVE_DIR = BACKUP_DIR / "archive"
# Директории для бэкапа
INCLUDE_DIRS = [
HERMES_HOME / "skills",
HERMES_HOME / "profiles",
]
# Файлы для бэкапа
INCLUDE_FILES = [
HERMES_HOME / "config.yaml",
HERMES_HOME / ".env",
]
# Директории для исключения
EXCLUDE_PATTERNS = [
"**/sessions/",
"**/logs/",
"**/audio_cache/",
"**/state.db",
"**/auth.json",
"**/__pycache__/",
"**/*.pyc",
]
def create_snapshot(output_dir: Path, exclude_secrets: bool = False):
"""Создать снапшот конфигурации."""
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H-%M-%SZ")
snapshot_dir = output_dir / timestamp
snapshot_dir.mkdir(parents=True, exist_ok=True)
manifest = {
"version": "0.1.0",
"created_at": timestamp,
"hermes_home": str(HERMES_HOME),
"files": [],
"exclude_secrets": exclude_secrets,
}
# Копирование конфигурационных файлов
for src in INCLUDE_FILES:
if src.exists():
dst = snapshot_dir / src.relative_to(HERMES_HOME)
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
# Redact секреты в .env если нужно
if exclude_secrets and dst.name == ".env":
redact_env_secrets(dst)
file_hash = hash_file(dst)
manifest["files"].append({
"path": str(src.relative_to(HERMES_HOME)),
"hash": file_hash,
"size": dst.stat().st_size,
})
# Копирование директорий с исключениями
for src_dir in INCLUDE_DIRS:
if src_dir.exists() and src_dir.is_dir():
_copy_with_exclusions(
src_dir,
snapshot_dir / src_dir.relative_to(HERMES_HOME),
manifest,
)
# Сохранение манифеста
manifest_path = snapshot_dir / "manifest.json"
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False))
# Обновление симлинка latest
if LATEST_DIR.exists():
if LATEST_DIR.is_symlink():
LATEST_DIR.unlink()
else:
shutil.rmtree(LATEST_DIR)
os.symlink(snapshot_dir, LATEST_DIR, target_is_directory=True)
# Создание архива
archive_path = ARCHIVE_DIR / f"{timestamp}.tar.gz"
archive_path.parent.mkdir(parents=True, exist_ok=True)
with tarfile.open(archive_path, "w:gz") as tar:
tar.add(snapshot_dir, arcname=timestamp)
print(f"Snapshot created: {snapshot_dir}")
print(f"Archive: {archive_path}")
print(f"Manifest: {len(manifest['files'])} files")
# Очистка старых архивов (> 30 дней)
cleanup_old_archives(days=30)
def redact_env_secrets(env_path: Path):
"""Заменить значения секретов на REDACTED."""
content = env_path.read_text()
redacted_lines = []
for line in content.split("\n"):
if "=" in line and not line.startswith("#"):
key, _ = line.split("=", 1)
if any(s in key.upper() for s in ["KEY", "TOKEN", "SECRET", "PASSWORD"]):
redacted_lines.append(f"{key}=REDACTED")
else:
redacted_lines.append(line)
else:
redacted_lines.append(line)
env_path.write_text("\n".join(redacted_lines))
def hash_file(path: Path) -> str:
"""SHA-256 хеш файла."""
sha = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha.update(chunk)
return sha.hexdigest()
def cleanup_old_archives(days: int = 30):
"""Удалить архивы старше N дней."""
cutoff = datetime.now(timezone.utc).timestamp() - days * 86400
for archive in ARCHIVE_DIR.glob("*.tar.gz"):
if archive.stat().st_mtime < cutoff:
archive.unlink()
print(f"Removed old archive: {archive.name}")
def _copy_with_exclusions(src: Path, dst: Path, manifest: dict):
"""Копировать директорию с исключениями."""
from fnmatch import fnmatch
for root, dirs, files in os.walk(src):
root_path = Path(root)
# Пропустить исключённые директории
dirs[:] = [
d for d in dirs
if not any(fnmatch(str(root_path / d), pat) for pat in EXCLUDE_PATTERNS)
]
for fname in files:
fpath = root_path / fname
if any(fnmatch(str(fpath), pat) for pat in EXCLUDE_PATTERNS):
continue
rel = fpath.relative_to(HERMES_HOME)
dest = dst / rel.relative_to(src)
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(fpath, dest)
file_hash = hash_file(dest)
manifest["files"].append({
"path": str(rel),
"hash": file_hash,
"size": dest.stat().st_size,
})
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Hermes config snapshot")
parser.add_argument("--output", type=Path, default=BACKUP_DIR,
help="Output directory")
parser.add_argument("--exclude-secrets", action="store_true",
help="Redact secrets in .env files")
parser.add_argument("--dry-run", action="store_true",
help="Show what would be backed up, without copying")
args = parser.parse_args()
if args.dry_run:
print("Files to backup:")
for f in INCLUDE_FILES:
if f.exists():
print(f" {f}")
for d in INCLUDE_DIRS:
if d.exists():
print(f" {d}/")
else:
create_snapshot(args.output, exclude_secrets=args.exclude_secrets)
```
### 3.2 Ручной снапшот
```bash
# Стандартный снапшот
python scripts/update_backup.py
# Снапшот с redacted секретами (для хранения в Git)
python scripts/update_backup.py --exclude-secrets
# Предварительный просмотр
python scripts/update_backup.py --dry-run
# В указанную директорию
python scripts/update_backup.py --output /mnt/backups/hermes/
```
### 3.3 Автоматизация через cron
```bash
# Добавить задание в crontab
# Ежечасный снапшот с исключением секретов
0 * * * * cd ~/acc-local && python scripts/update_backup.py --exclude-secrets --output ~/.hermes/backups/
# Ежедневный полный снапшот
0 2 * * * cd ~/acc-local && python scripts/update_backup.py --output ~/.hermes/backups/
```
## 4. Процедура восстановления
### 4.1 Скрипт: `scripts/restore_backup.py`
```python
#!/usr/bin/env python3
"""
restore_backup.py — восстановление конфигурации Hermes Agent из снапшота.
Использование:
python scripts/restore_backup.py --snapshot DIR
python scripts/restore_backup.py --latest
python scripts/restore_backup.py --list
python scripts/restore_backup.py --verify DIR
"""
import os
import json
import shutil
import hashlib
from pathlib import Path
from datetime import datetime, timezone
HERMES_HOME = Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes"))
BACKUP_DIR = HERMES_HOME / "backups"
LATEST_DIR = BACKUP_DIR / "latest"
ARCHIVE_DIR = BACKUP_DIR / "archive"
RESTORE_DIR = HERMES_HOME / "restore_tmp"
def list_snapshots():
"""Показать доступные снапшоты."""
if not ARCHIVE_DIR.exists():
print("No archives found.")
return
archives = sorted(ARCHIVE_DIR.glob("*.tar.gz"), reverse=True)
for i, arc in enumerate(archives):
size_mb = arc.stat().st_size / (1024 * 1024)
mtime = datetime.fromtimestamp(arc.stat().st_mtime)
print(f" [{i}] {arc.name} {size_mb:.1f} MB {mtime.isoformat()}")
def verify_snapshot(snapshot_dir: Path):
"""Проверить целостность снапшота."""
manifest_path = snapshot_dir / "manifest.json"
if not manifest_path.exists():
print("ERROR: manifest.json not found")
return False
manifest = json.loads(manifest_path.read_text())
all_ok = True
for fspec in manifest.get("files", []):
fpath = snapshot_dir / fspec["path"]
if not fpath.exists():
print(f" MISSING: {fspec['path']}")
all_ok = False
continue
actual_hash = hashlib.sha256(fpath.read_bytes()).hexdigest()
if actual_hash != fspec["hash"]:
print(f" HASH MISMATCH: {fspec['path']}")
all_ok = False
if all_ok:
print(f"VERIFIED: {len(manifest['files'])} files OK")
return all_ok
def restore_snapshot(snapshot_dir: Path, dry_run: bool = False):
"""Восстановить конфигурацию из снапшота."""
manifest_path = snapshot_dir / "manifest.json"
manifest = json.loads(manifest_path.read_text())
print(f"Restoring from: {snapshot_dir}")
print(f"Created: {manifest.get('created_at', 'unknown')}")
print(f"Files: {len(manifest['files'])}")
print(f"{'[DRY RUN] ' if dry_run else ''}")
conflicts = []
for fspec in manifest.get("files", []):
src = snapshot_dir / fspec["path"]
dst = HERMES_HOME / fspec["path"]
if not src.exists():
print(f" SKIP (missing): {fspec['path']}")
continue
# Проверить конфликт
if dst.exists() and not dry_run:
dst_hash = hashlib.sha256(dst.read_bytes()).hexdigest()
if dst_hash != fspec["hash"]:
conflicts.append(fspec["path"])
if dry_run:
action = "CONFLICT" if dst.exists() else "CREATE"
print(f" [{action}] {fspec['path']}")
else:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
print(f" RESTORED: {fspec['path']}")
if conflicts:
print(f"\nWARNING: {len(conflicts)} files had local modifications (overwritten):")
for p in conflicts:
print(f" - {p}")
if dry_run:
print(f"\nDry run complete. {len(manifest['files'])} files would be restored.")
print("Restore complete.")
print("NOTE: .env files were backed up with redacted secrets.")
print(" Manually restore API keys and secrets after restore.")
def restore_from_archive(archive_path: Path):
"""Восстановить из tar.gz архива."""
import tarfile
extract_dir = RESTORE_DIR / archive_path.stem
extract_dir.mkdir(parents=True, exist_ok=True)
with tarfile.open(archive_path, "r:gz") as tar:
tar.extractall(extract_dir)
# Найти директорию снапшота внутри архива
snapshots = list(extract_dir.glob("*/manifest.json"))
if not snapshots:
print("ERROR: No manifest.json found in archive")
return
snapshot_dir = snapshots[0].parent
restore_snapshot(snapshot_dir)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Hermes config restore")
parser.add_argument("--snapshot", type=Path,
help="Snapshot directory to restore from")
parser.add_argument("--latest", action="store_true",
help="Restore from latest snapshot")
parser.add_argument("--list", action="store_true",
help="List available snapshots")
parser.add_argument("--verify", type=Path,
help="Verify snapshot integrity")
parser.add_argument("--dry-run", action="store_true",
help="Show what would be restored")
args = parser.parse_args()
if args.list:
list_snapshots()
elif args.verify:
verify_snapshot(Path(args.verify))
elif args.latest:
if not LATEST_DIR.exists():
print("ERROR: No latest snapshot found")
exit(1)
if args.dry_run:
restore_snapshot(LATEST_DIR, dry_run=True)
else:
verify_snapshot(LATEST_DIR)
restore_snapshot(LATEST_DIR)
elif args.snapshot:
snapshot = Path(args.snapshot)
if snapshot.suffix == ".gz":
restore_from_archive(snapshot)
else:
if args.dry_run:
restore_snapshot(snapshot, dry_run=True)
else:
verify_snapshot(snapshot)
restore_snapshot(snapshot)
else:
parser.print_help()
```
### 4.2 Ручное восстановление
```bash
# Список доступных снапшотов
python scripts/restore_backup.py --list
# Восстановить последний снапшот
python scripts/restore_backup.py --latest
# Восстановить конкретный снапшот
python scripts/restore_backup.py --snapshot ~/.hermes/backups/2026-07-20T14-00-00Z
# Восстановить из архива
python scripts/restore_backup.py --snapshot ~/.hermes/backups/archive/2026-07-20T14-00-00Z.tar.gz
# Проверить целостность без восстановления
python scripts/restore_backup.py --verify ~/.hermes/backups/latest
# Предварительный просмотр
python scripts/restore_backup.py --latest --dry-run
```
## 5. Git-репозиторий как хранилище
### 5.1 Структура репозитория `ochenstarik-ui/hermes-config-backup`
```
hermes-config-backup/
├── README.md
├── config/
│ ├── default.yaml # Без секретов
│ ├── worker-code.yaml
│ ├── worker-fast.yaml
│ ├── worker-research.yaml
│ └── worker-review.yaml
├── skills/ # Пользовательские навыки
│ └── *.md
├── memories/ # Память (без чувствительных данных)
│ ├── MEMORY.md
│ └── USER.md
├── scripts/
│ ├── update_backup.py
│ ├── restore_backup.py
│ └── sync_to_git.sh
└── .gitignore
```
### 5.2 `.gitignore`
```gitignore
# Исключить файлы с секретами
.env
*.env
auth.json
credentials/
secrets/
# Исключить чувствительные данные сессий
sessions/
state.db
# Исключить временные файлы
*.pyc
__pycache__/
backups/archive/
backups/latest/
```
### 5.3 Синхронизация: `scripts/sync_to_git.sh`
```bash
#!/bin/bash
# sync_to_git.sh — синхронизация снапшота в Git-репозиторий
set -euo pipefail
REPO_DIR="$HOME/hermes-config-backup"
SNAPSHOT_DIR="$HOME/.hermes/backups/latest"
cd "$REPO_DIR"
# Копировать файлы из снапшота (без секретов)
if [ -d "$SNAPSHOT_DIR" ]; then
# config.yaml профилей
cp "$SNAPSHOT_DIR/config.yaml" config/default.yaml 2>/dev/null || true
cp "$SNAPSHOT_DIR/profiles/worker-code/config.yaml" config/worker-code.yaml 2>/dev/null || true
cp "$SNAPSHOT_DIR/profiles/worker-fast/config.yaml" config/worker-fast.yaml 2>/dev/null || true
cp "$SNAPSHOT_DIR/profiles/worker-research/config.yaml" config/worker-research.yaml 2>/dev/null || true
cp "$SNAPSHOT_DIR/profiles/worker-review/config.yaml" config/worker-review.yaml 2>/dev/null || true
# Навыки
rm -rf skills/
cp -r "$SNAPSHOT_DIR/skills/" skills/ 2>/dev/null || true
# Память
mkdir -p memories/
cp "$SNAPSHOT_DIR/profiles/default/memories/MEMORY.md" memories/ 2>/dev/null || true
cp "$SNAPSHOT_DIR/profiles/default/memories/USER.md" memories/ 2>/dev/null || true
fi
# Commit и push
git add -A
if git diff --cached --quiet; then
echo "No changes to commit."
else
git commit -m "backup: $(date -u +'%Y-%m-%dT%H:%M:%SZ')"
git push origin main
echo "Backup synced to GitHub."
fi
```
## 6. Исключение секретов
| Компонент | Статус секретов | Действие при бэкапе |
|---|---|---|
| `config.yaml` | Может содержать `api_key` references | Проверить на прямые ключи; использовать `${ENV_VAR}` синтаксис |
| `.env` | Содержит API-ключи | REDACT при `--exclude-secrets`; NEVER commit в Git |
| `auth.json` | OAuth-токены | Исключён из бэкапа полностью |
| `state.db` | Может содержать ключи в сессиях | Исключён из бэкапа |
| `skills/` | Обычно без секретов | Бэкапируется; проверить на embedded ключи |
| `memories/` | Может содержать косвенные ссылки | Бэкапируется; проверить на чувствительные данные |
### 6.1 Проверка на секреты перед commit
```bash
# Проверить снапшот на наличие паттернов секретов
grep -rE "(sk-|api_key|token|secret|password|-----BEGIN)" ~/.hermes/backups/latest/ \
--exclude-dir=sessions --exclude="*.pyc" && echo "WARNING: Potential secrets found!"
```
## 7. Процедуры
### 7.1 Плановый бэкап (ежедневно)
```bash
#!/bin/bash
# daily_backup.sh
echo "=== Daily backup $(date) ==="
# 1. Создать снапшот
python scripts/update_backup.py --exclude-secrets
echo "Snapshot created."
# 2. Проверить целостность
python scripts/restore_backup.py --verify ~/.hermes/backups/latest
echo "Integrity verified."
# 3. Синхронизировать в Git
bash scripts/sync_to_git.sh
echo "Git sync complete."
# 4. Очистить старые архивы (> 30 дней)
find ~/.hermes/backups/archive/ -name "*.tar.gz" -mtime +30 -delete
echo "Old archives cleaned."
```
### 7.2 Восстановление после сбоя
```bash
#!/bin/bash
# disaster_recovery.sh
echo "=== Disaster Recovery $(date) ==="
echo "WARNING: This will overwrite current Hermes configuration!"
read -p "Continue? (yes/no): " confirm
[[ "$confirm" != "yes" ]] && exit 0
# 1. Создать аварийный снапшот текущего состояния
python scripts/update_backup.py --output ~/.hermes/backups/pre_recovery
echo "Pre-recovery snapshot created."
# 2. Восстановить из последнего снапшота
python scripts/restore_backup.py --latest
echo "Config restored."
# 3. Проверить конфигурацию
hermes config check
echo "Config check complete."
# 4. Проверить health
hermes doctor
echo "Health check complete."
# 5. Перезапустить gateway
hermes gateway restart
echo "Gateway restarted."
echo "=== Recovery complete ==="
echo "NOTE: API keys (.env) were redacted in backup."
echo " Manually restore API keys if needed."
```
## 8. RPO и RTO
| Сценарий | RPO | RTO | Процедура |
|---|---|---|---|
| Повреждение `config.yaml` | ≤ 1 час | ≤ 5 мин | `restore_backup.py --latest` |
| Потеря навыков | ≤ 6 часов | ≤ 10 мин | `restore_backup.py --latest` |
| Полная потеря `~/.hermes/` | ≤ 1 час (config) / ≤ 24 часа (sessions) | ≤ 30 мин | `disaster_recovery.sh` |
| Миграция на новый сервер | ≤ 1 час | ≤ 1 час | Клонировать Git-репозиторий + восстановить секреты |
## 9. Ссылки
- [HERMES.md](../adapters/HERMES.md) — спецификация Hermes-адаптера (конфигурация)
- [MONITORING.md](MONITORING.md) — мониторинг и алерты (включая ALT-BKP-001)
- [CREDENTIAL_POOLS.md](../patterns/CREDENTIAL_POOLS.md) — управление ключами
- [SPECIFICATION.md](../SPECIFICATION.md) §10 — Data lifecycle
- [SPECIFICATION.md](../SPECIFICATION.md) §7.2 — Reliability, consistency and recovery

View file

@ -0,0 +1,340 @@
# Monitoring & Health-check
**Версия:** 0.1.0-draft
**Дата:** 2026-07-20
**Статус:** Draft
**Зависит от:** `SPECIFICATION.md` (§7.5 Operations and observability), `docs/adapters/HERMES.md`, `docs/patterns/WORKER_ORCHESTRATION.md`
> Этот документ описывает систему мониторинга и health-check'ов для Hermes Agent в составе Agent Control Center. Реализация запрещена до G0/G1 и утверждения оператором.
## 1. Обзор
Система мониторинга ACC охватывает три слоя:
1. **Инфраструктурный** — Connector, Control Plane, базы данных, сеть
2. **Агентский** — Hermes профили, провайдеры, credential pools
3. **Интеграционный** — Gateway (Telegram polling), 9Router, внешние API
## 2. Health-check компонентов
### 2.1 Матрица health-check
| Компонент | Метод проверки | Периодичность | Критичность |
|---|---|---|---|
| **Hermes default профиль** | `hermes --profile default doctor` | 5 мин | CRITICAL |
| **Worker-code** | `hermes --profile worker-code doctor` | 5 мин | HIGH |
| **Worker-fast** | `hermes --profile worker-fast doctor` | 5 мин | HIGH |
| **Worker-research** | `hermes --profile worker-research doctor` | 5 мин | MEDIUM |
| **Worker-review** | `hermes --profile worker-review doctor` | 5 мин | MEDIUM |
| **Gateway (Telegram)** | `hermes gateway status` | 1 мин | CRITICAL |
| **9Router** | `curl https://9router.example.com/health` | 1 мин | HIGH |
| **OpenCode Go API** | `curl -I https://api.opencode.ai/health` | 5 мин | HIGH |
| **Fireworks API** | `curl -I https://api.fireworks.ai/health` | 5 мин | HIGH |
| **Gemini API** | `curl -I https://generativelanguage.googleapis.com` | 5 мин | HIGH |
| **NVIDIA API** | `curl -I https://integrate.api.nvidia.com/v1` | 5 мин | HIGH |
| **Дисковое пространство** | `df -h` | 15 мин | MEDIUM |
| **Память** | `free -m` | 15 мин | MEDIUM |
| **CPU** | `top -bn1` | 15 мин | LOW |
### 2.2 Hermes Health-check
```bash
# Комплексная проверка
hermes doctor --fix
# Проверка конфигурации
hermes config check
# Статус компонентов
hermes status --all
# Проверка конкретного профиля
hermes --profile worker-code doctor
# Проверка gateway
hermes gateway status
# Проверка cron
hermes cron status
```
### 2.3 Интерпретация статусов
| Статус | Описание | SLA Impact |
|---|---|---|
| `healthy` | Все проверки пройдены | — |
| `degraded` | Часть функций работает с ограничениями | WARNING, частичная доступность |
| `unhealthy` | Критическая функция недоступна | CRITICAL, требуется intervention |
| `unknown` | Health-check не выполнен / нет данных | WARNING, проверка мониторинга |
## 3. Ключевые метрики
### 3.1 Latency
| Метрика | Цель | WARNING | CRITICAL |
|---|---|---|---|
| `hermes_response_time_p95` | ≤ 5s | > 10s | > 30s |
| `provider_latency_p95` (openrouter) | ≤ 3s | > 8s | > 20s |
| `delegate_task_startup_time_p95` | ≤ 10s | > 20s | > 60s |
| `telegram_polling_latency_p95` | ≤ 2s | > 5s | > 15s |
| `9router_proxy_latency_p95` | ≤ 500ms | > 2s | > 5s |
### 3.2 Token usage
| Метрика | Обновление | WARNING | CRITICAL |
|---|---|---|---|
| `tokens_used_daily` | Каждый запрос | > 80% дневной квоты | > 95% |
| `tokens_used_monthly` | Каждый запрос | > 80% месячной квоты | > 95% |
| `tokens_per_request_avg` | Скользящее окно 1 час | > 100K в среднем (аномалия) | > 500K (возможна утечка) |
| `cost_estimate_daily` | Ежечасно | > бюджета | > бюджета × 1.5 |
### 3.3 Error rates
| Метрика | Окно | WARNING | CRITICAL |
|---|---|---|---|
| `provider_4xx_rate` | 5 мин | > 5% | > 15% |
| `provider_5xx_rate` | 5 мин | > 2% | > 10% |
| `delegation_failure_rate` | 15 мин | > 10% | > 25% |
| `gateway_message_delivery_failure` | 15 мин | > 5% | > 15% |
| `telegram_polling_errors` | 5 мин | > 3 ошибок | > 10 ошибок |
### 3.4 Gateway метрики
| Метрика | Описание | WARNING | CRITICAL |
|---|---|---|---|
| `telegram_polling_loop_healthy` | polling loop активен | false в течение 2 мин | false в течение 5 мин |
| `gateway_websocket_connected` | mTLS WSS к Control Plane | disconnected > 30s | disconnected > 5 мин |
| `active_user_sessions` | Количество активных пользователей | = 0 (нет активности) | — |
| `message_queue_depth` | Глубина очереди недоставленных сообщений | > 100 | > 1000 |
## 4. Алерты
### 4.1 Классификация
| Severity | Описание | Время реакции | Эскалация |
|---|---|---|---|
| **CRITICAL** | Сервис недоступен, пользователи затронуты | 5 мин | Немедленно → Operator |
| **WARNING** | Деградация, возможны проблемы | 30 мин | В течение часа |
| **INFO** | Информационное событие | — | В рабочее время |
### 4.2 Перечень алертов
| Alert ID | Название | Severity | Условие | Runbook |
|---|---|---|---|---|
| `ALT-GTW-001` | Gateway down | CRITICAL | `hermes gateway status` != running | [§6.1](#61-gateway-down) |
| `ALT-GTW-002` | Telegram polling lag | WARNING | Polling latency > 5s за 5 мин | [§6.2](#62-telegram-polling-lag) |
| `ALT-PRV-001` | Provider quota exhausted | CRITICAL | Все ключи провайдера exhausted | [§6.3](#63-provider-quota-exhausted) |
| `ALT-PRV-002` | Provider unavailable | CRITICAL | Все провайдеры для профиля unhealthy | [§6.4](#64-provider-unavailable) |
| `ALT-PRV-003` | Quota warning | WARNING | > 80% квоты использовано | [§6.3](#63-provider-quota-exhausted) |
| `ALT-WRK-001` | Worker profile offline | WARNING | Профиль unhealthy > 10 мин | [§6.5](#65-worker-profile-offline) |
| `ALT-WRK-002` | All workers offline | CRITICAL | Все worker-профили unhealthy | [§6.5](#65-worker-profile-offline) |
| `ALT-CFG-001` | Config invalid | WARNING | `hermes config check` failed | [§6.6](#66-config-invalid) |
| `ALT-BKP-001` | Backup failed | WARNING | Последний снапшот старше 24ч | [BACKUP.md](BACKUP.md) |
| `ALT-DSK-001` | Low disk space | CRITICAL | < 5 GB свободно | [§6.7](#67-low-disk-space) |
| `ALT-9RT-001` | 9Router unreachable | WARNING | Health-check 9Router failed > 5 мин | [§6.8](#68-9router-unreachable) |
| `ALT-CRN-001` | Cron scheduler stalled | WARNING | Последний tick > 2× интервала | `hermes cron status` |
## 5. Мониторинговые инструменты
### 5.1 Встроенные
| Инструмент | Команда | Назначение |
|---|---|---|
| `hermes doctor` | `hermes doctor [--fix]` | Проверка зависимостей и конфигурации |
| `hermes status` | `hermes status --all` | Статус компонентов |
| `hermes config check` | `hermes config check` | Проверка конфигурации |
| `hermes gateway status` | `hermes gateway status` | Статус gateway |
| `hermes cron status` | `hermes cron status` | Статус планировщика |
| `hermes insights` | `hermes insights --days 7` | Аналитика использования |
| `hermes debug` | `hermes debug` (или `/debug`) | Отчёт для отладки |
### 5.2 Внешние (рекомендуемые)
- **Prometheus + Grafana**сбор и визуализация метрик
- **Healthchecks.io / Uptime Kuma** — внешний мониторинг доступности
- **Sentry / Datadog** — отслеживание ошибок
- **W&B** — логирование ML-экспериментов и usage
## 6. Процедуры восстановления
### 6.1 Gateway down
```bash
# 1. Проверить статус
hermes gateway status
# 2. Проверить логи
tail -100 ~/.hermes/logs/gateway.log | grep -i "error\|failed"
# 3. Перезапустить
hermes gateway restart
# 4. Если не помогло — проверить systemd (Linux)
systemctl --user status hermes-gateway
systemctl --user reset-failed hermes-gateway
systemctl --user restart hermes-gateway
# 5. Если crash loop — проверить конфигурацию платформ
hermes config check
# Убедиться, что токены платформ валидны
```
### 6.2 Telegram polling lag
```bash
# 1. Проверить статус polling
hermes gateway status | grep telegram
# 2. Переключить fallback IP (если блокировка)
# В config.yaml:
# gateway.platforms.telegram.fallback_ips добавить/изменить IP
# 3. Перезапустить gateway
hermes gateway restart
```
### 6.3 Provider quota exhausted
```bash
# 1. Проверить статус ключей
hermes auth list opencode-go
hermes auth list fireworks
hermes auth list gemini
hermes auth list nvidia
# 2. Если есть активные ключи — проверить стратегию
# Убедиться, что exhausted ключи корректно исключены
# 3. Если все ключи exhausted:
# a. Проверить наличие резервных ключей
# b. Добавить новый ключ: hermes auth add <provider>
# c. Или ждать сброса квоты
# 4. Для срочных задач — переключить на другого провайдера
# worker-code (opencode-go) → worker-code (nvidia / deepseek-v4-pro)
```
### 6.4 Provider unavailable
```bash
# 1. Проверить доступность API
curl -I https://api.opencode.ai/health
curl -I https://api.fireworks.ai/health
curl -I https://generativelanguage.googleapis.com
# 2. Если API недоступен — ждать восстановления
# 3. Переключить профили на альтернативных провайдеров
# (см. WORKER_ORCHESTRATION.md §3 Fallback-цепочки)
# 4. Проверить статус 9Router
curl https://9router.example.com/health
```
### 6.5 Worker profile offline
```bash
# 1. Проверить статус профиля
hermes --profile worker-code doctor
# 2. Проверить конфигурацию
hermes --profile worker-code config check
# 3. Проверить .env профиля
hermes --profile worker-code config env-path
# Убедиться, что API ключи на месте и валидны
# 4. Проверить доступность провайдера (см. §6.4)
# 5. Если проблема в модели — переключить модель
hermes --profile worker-code config set model.default <alternative_model>
```
### 6.6 Config invalid
```bash
# 1. Проверить синтаксис
hermes config check
# 2. Сравнить с последним бэкапом
diff ~/.hermes/config.yaml ~/.hermes/backups/latest/config.yaml
# 3. Восстановить из бэкапа (см. BACKUP.md)
python scripts/restore_backup.py --latest
```
### 6.7 Low disk space
```bash
# 1. Определить, что занимает место
du -sh ~/.hermes/sessions/
du -sh ~/.hermes/logs/
du -sh ~/.hermes/audio_cache/
# 2. Очистить старые сессии
hermes sessions prune --older-than 30
# 3. Очистить старые логи
find ~/.hermes/logs/ -name "*.log" -mtime +7 -delete
# 4. Очистить audio cache
find ~/.hermes/audio_cache/ -mtime +7 -delete
```
### 6.8 9Router unreachable
```bash
# 1. Проверить доступность
curl -v https://9router.example.com/health
# 2. Переключить Hermes на прямой endpoint
# В config.yaml изменить base_url на прямой URL провайдера
hermes config set model.base_url "https://api.opencode.ai/v1"
# 3. После восстановления 9Router — вернуть конфигурацию
```
## 7. Панель мониторинга
Рекомендуемая структура дашборда:
```
┌─────────────────────────────────────────────────────────┐
│ AGENT CONTROL CENTER — MONITORING v0.1.0-draft │
├────────────┬────────────┬────────────┬──────────────────┤
│ PROFILES │ PROVIDERS │ GATEWAY │ INFRASTRUCTURE │
│ ──────── │ ──────── │ ──────── │ ────────────── │
│ default │ opencode │ telegram │ CPU: 34% │
│ ● ONLINE │ ● HEALTHY │ ● ONLINE │ RAM: 62% │
│ │ │ │ DISK: 41% │
│ worker-c │ fireworks │ discord │ │
│ ● ONLINE │ ● DEGRADED │ ○ OFFLINE │ UPTIME: 14d 3h │
│ │ │ │ │
│ worker-f │ gemini │ slack │ CONNECTOR │
│ ● ONLINE │ ● HEALTHY │ ○ OFFLINE │ ● CONNECTED │
│ │ │ │ │
│ worker-r │ nvidia │ │ BACKUP │
│ ● ONLINE │ ● HEALTHY │ │ ✓ 2h ago │
│ │ │ │ │
│ worker-rv │ │ │ CRON │
│ ● ONLINE │ │ │ ✓ running │
├────────────┴────────────┴────────────┴──────────────────┤
│ USAGE TODAY ERRORS (LAST HOUR) │
│ ───────── ────────────────── │
│ Tokens: 1.2M / 5M (24%) Total: 12 │
│ Cost: $4.20 / $25.00 (17%) 4xx: 8 | 5xx: 4 │
│ Rate: 0.3% │
├──────────────────────────────────────────────────────────┤
│ RECENT EVENTS │
│ 14:32 INFO worker-code: task completed (45s) │
│ 14:30 WARN fireworks: quota 82% │
│ 14:28 INFO telegram: message delivered │
│ 14:25 WARN worker-fast: latency spike (8.2s p95) │
└──────────────────────────────────────────────────────────┘
```
## 8. Ссылки
- [HERMES.md](../adapters/HERMES.md) — спецификация Hermes-адаптера
- [WORKER_ORCHESTRATION.md](../patterns/WORKER_ORCHESTRATION.md) — health-check worker-профилей
- [CREDENTIAL_POOLS.md](../patterns/CREDENTIAL_POOLS.md) — мониторинг квот
- [BACKUP.md](BACKUP.md) — бэкап и восстановление
- [SPECIFICATION.md](../SPECIFICATION.md) §7.5 — Operations and observability

View file

@ -0,0 +1,282 @@
# Credential Pools & Multi-Account Management
**Версия:** 0.1.0-draft
**Дата:** 2026-07-20
**Статус:** Draft
**Зависит от:** `SPECIFICATION.md`, `ARCHITECTURE.md`, `docs/adapters/HERMES.md`
> Этот документ описывает паттерны управления пулами API-ключей для провайдеров ИИ-моделей. Реализация запрещена до G0/G1 и утверждения оператором.
## 1. Обзор
Credential Pool — механизм ротации нескольких API-ключей в рамках одного провайдера, обеспечивающий:
- Автоматический обход исчерпанных квот
- Round-robin распределение нагрузки
- Приоритетное использование ключей
- Прозрачный failover без ручного вмешательства
## 2. Архитектура credential pool
```
┌─────────────────────────────────────────────┐
│ Hermes Agent │
│ ┌───────────────────────────────────────┐ │
│ │ Credential Pool Manager │ │
│ │ ┌─────────┐ ┌─────────┐ ┌───────┐ │ │
│ │ │ Key #1 │ │ Key #2 │ │ Key N │ │ │
│ │ │ (active)│ │ (active)│ │(spare)│ │ │
│ │ └────┬────┘ └────┬────┘ └───┬───┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Provider API Backend │ │ │
│ │ └──────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ 9Router (опционально) │ │
│ │ • Round-robin распределение │ │
│ │ • Quota tracking │ │
│ │ • OpenAI-формат трансляции │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
```
## 3. Hermes Auth Pools
### 3.1 Конфигурация
Hermes поддерживает несколько ключей на одного провайдера через `hermes auth`:
```bash
# Добавление ключа
hermes auth add opencode-go
hermes auth add fireworks
hermes auth add gemini
hermes auth add nvidia
# Просмотр пула
hermes auth list opencode-go
# Вывод:
# [0] sk-abc...xyz active quota: 85%
# [1] sk-def...uvw active quota: 42%
# [2] sk-ghi...rst active quota: 12%
# Ручной сброс exhausted-статуса
hermes auth reset opencode-go 2
```
### 3.2 Стратегии выбора ключа
| Стратегия | Алгоритм | Применение |
|---|---|---|
| **Round-robin** | Последовательный перебор активных ключей | Равномерное распределение нагрузки |
| **Priority-based** | Ключи с более высоким приоритетом используются первыми | primary/fallback сценарий |
| **Least-loaded** | Ключ с наименьшим процентом использованной квоты | Оптимизация использования квот |
| **Failover-only** | Второй ключ активируется только при отказе первого | Минимизация случайного использования дорогих ключей |
### 3.3 Primary/Fallback паттерн
```yaml
# ~/.hermes/auth.json (концептуально)
{
"pools": {
"opencode-go": {
"keys": [
{
"id": "primary",
"key": "sk-primary-...",
"priority": 1,
"quota_limit": 1000000,
"status": "active"
},
{
"id": "fallback",
"key": "sk-fallback-...",
"priority": 2,
"quota_limit": 500000,
"status": "active"
}
],
"strategy": "priority"
}
}
}
```
### 3.4 Статусы ключей
| Статус | Описание | Поведение |
|---|---|---|
| `active` | Ключ работает | Используется согласно стратегии |
| `exhausted` | Ключ исчерпал квоту | Автоматически исключается из ротации |
| `rate_limited` | Временный rate limit | Исключается на cooldown-период (60s) |
| `error` | Ошибка аутентификации | Исключается до ручного сброса |
| `disabled` | Ручное отключение | Никогда не используется |
### 3.5 Обработка ошибок
| HTTP-код | Интерпретация | Действие |
|---|---|---|
| 401 | Неверный ключ | `status=error`; alert |
| 403 | Доступ запрещён | `status=error`; alert |
| 429 | Rate limit | `status=rate_limited`; retry через cooldown |
| 429 + `x-ratelimit-remaining: 0` | Квота исчерпана | `status=exhausted`; переключение на следующий ключ |
| 500/502/503 | Серверная ошибка | Retry до 3 раз; затем fallback ключ |
| Timeout | Сетевой сбой | Retry 1 раз; затем fallback ключ |
## 4. Интеграция с 9Router
### 4.1 Назначение
9Router — внешний агрегатор API-ключей, обеспечивающий:
- Централизованное управление пулом ключей от разных аккаунтов
- Round-robin / weighted распределение запросов
- Отслеживание квот в реальном времени
- Трансляцию форматов запросов (не-OpenAI → OpenAI-совместимый)
- Единый endpoint для всех моделей
### 4.2 Конфигурация Hermes → 9Router
```yaml
# config.yaml
model:
provider: opencode-go
base_url: "https://9router.example.com/v1/opencode-go" # Проксируется через 9Router
api_key: "${NINEROUTER_API_KEY}"
```
```bash
# .env
NINEROUTER_API_KEY=nr-...
```
### 4.3 Паттерны использования
#### Round-robin через 9Router
```yaml
# 9Router конфигурация (на стороне 9Router)
pools:
opencode-go:
strategy: round-robin
keys:
- account: personal
key: sk-personal-...
weight: 1
- account: team
key: sk-team-...
weight: 2
- account: backup
key: sk-backup-...
weight: 1
```
#### Quota tracking
9Router отслеживает использование через:
- Response headers (`x-ratelimit-remaining`, `x-ratelimit-reset`)
- Собственный счётчик токенов из тела ответа
- Периодический опрос `/v1/usage` (если доступен)
При достижении 95% квоты ключ помечается `low`; при 100% — `exhausted` и исключается из ротации.
### 4.4 Отказоустойчивость
Если 9Router недоступен, Hermes MUST fallback на прямое подключение к провайдеру с локальным credential pool:
```
Hermes → [попытка] 9Router → [отказ]
→ [fallback] прямой endpoint провайдера + локальный пул ключей
```
## 5. Мониторинг квот
### 5.1 Метрики
| Метрика | Источник | Обновление |
|---|---|---|
| `tokens_used` | Response body `usage.total_tokens` | Каждый запрос |
| `tokens_remaining` | Response header `x-ratelimit-remaining` или локальный счётчик | Каждый запрос |
| `quota_percent` | `tokens_used / quota_limit * 100` | Каждый запрос |
| `request_count` | Локальный счётчик | Каждый запрос |
| `error_count` | HTTP status != 200 | Каждый запрос |
| `latency_p95` | Локальный замер | Скользящее окно 5 мин |
### 5.2 Пороги и алерты
| Порог | Условие | Действие |
|---|---|---|
| `WARNING` | `quota_percent > 80%` | Уведомление оператору |
| `CRITICAL` | `quota_percent > 95%` | Автоматический failover на следующий ключ + алерт |
| `EXHAUSTED` | `quota_percent >= 100%` | Исключение ключа из ротации + инцидент |
| `ERROR_BURST` | > 5 ошибок за 1 мин | Отключение ключа на 5 мин + алерт |
### 5.3 Автоматический failover
```
1. Текущий ключ получает 429 с x-ratelimit-remaining: 0
2. Pool Manager помечает ключ exhausted
3. Выбирается следующий ключ согласно стратегии
4. Запрос повторяется с новым ключом
5. Если все ключи exhausted → возврат ошибки оператору
6. Exhausted-ключи сбрасываются при наступлении reset time
```
## 6. Безопасность
| Требование | Реализация |
|---|---|
| Ключи NEVER в логах | Secret redaction (`security.redact_secrets`) включён по умолчанию |
| Ключи NEVER в коде | Хранятся только в `.env` или `auth.json` |
| Ключи NEVER в Git | `.gitignore` исключает `.env`, `auth.json` |
| Ротация без простоя | Добавление нового ключа в пул + сброс старого после grace period |
| Аудит использования | Все операции с ключами логируются через `audit` tool |
## 7. Процедуры
### 7.1 Добавление нового ключа
```bash
# 1. Добавить ключ в пул
hermes auth add opencode-go
# 2. Проверить статус
hermes auth list opencode-go
# 3. Проверить health с новым ключом
hermes --profile worker-code doctor
# 4. При необходимости настроить 9Router
# Обновить конфигурацию 9Router с новым ключом
```
### 7.2 Замена скомпрометированного ключа
```bash
# 1. Немедленно отозвать ключ на стороне провайдера
# 2. Пометить ключ как error в Hermes
hermes auth remove opencode-go 1 # индекс скомпрометированного ключа
# 3. Добавить новый ключ
hermes auth add opencode-go
# 4. Проверить, что старый ключ не используется
# Проверить логи аудита
```
### 7.3 Сброс exhausted-статуса
```bash
# После сброса квоты (начало нового billing period)
hermes auth reset opencode-go 0
hermes auth reset opencode-go 1
```
## 8. Ссылки
- [HERMES.md](../adapters/HERMES.md) — спецификация Hermes-адаптера
- [WORKER_ORCHESTRATION.md](WORKER_ORCHESTRATION.md) — оркестрация worker-профилей
- [MONITORING.md](../operations/MONITORING.md) — мониторинг квот и алерты
- [BACKUP.md](../operations/BACKUP.md) — бэкап конфигурации credential pools
- [SPECIFICATION.md](../SPECIFICATION.md) §6.8 — Usage, notifications и audit

View file

@ -0,0 +1,207 @@
# Worker Orchestration Patterns
**Версия:** 0.1.0-draft
**Дата:** 2026-07-20
**Статус:** Draft
**Зависит от:** `SPECIFICATION.md`, `ARCHITECTURE.md`, `docs/adapters/HERMES.md`
> Этот документ описывает паттерны оркестрации через worker-профили Hermes Agent. Реализация запрещена до G0/G1 и утверждения оператором.
## 1. Обзор
Worker-профили Hermes Agent (`worker-code`, `worker-fast`, `worker-research`, `worker-review`) обеспечивают изолированное выполнение специализированных задач. Оркестратор (`default` профиль) маршрутизирует подзадачи, управляет fallback'ами и агрегирует результаты.
Архитектура оркестрации:
```
Пользователь → default (оркестратор)
├──→ worker-code (opencode-go / kimi-k2.7-code)
├──→ worker-fast (fireworks / grok-4.5)
├──→ worker-research (gemini / gemini-3-flash-preview)
└──→ worker-review (nvidia / deepseek-v4-pro)
```
## 2. Маршрутизация задач
### 2.1 Матрица маршрутизации
| Тип задачи | Первичный профиль | Вторичный (fallback) | Критерий выбора |
|---|---|---|---|
| **Генерация кода** | `worker-code` | `worker-fast` | Задача содержит "напиши код", "реализуй", "создай файл" |
| **Рефакторинг** | `worker-code` | `worker-review` | Задача требует изменения существующего кода с сохранением поведения |
| **Отладка** | `worker-code` | `worker-research` | Задача содержит трассировку, ошибку, stack trace |
| **Быстрый ответ** | `worker-fast` | `worker-code` | Задача простая, не требует сложного рассуждения |
| **Исследование** | `worker-research` | `worker-code` | Задача требует анализа документации, поиска, чтения |
| **Code Review** | `worker-review` | `worker-code` | Задача проверки качества/безопасности кода |
| **Аудит безопасности** | `worker-review` | `worker-research` | Задача анализа уязвимостей, проверки политик |
| **Планирование** | `default` (in-process) | `worker-research` | Задача требует стратегического рассуждения |
| **Сложный анализ** | `worker-research` | `worker-review` | Задача с большим контекстом, требует synthesis |
### 2.2 Алгоритм маршрутизации
```
1. Классифицировать задачу по типу (NL-эвристика)
2. Определить первичный профиль из матрицы
3. Проверить health первичного профиля
4. Если health OK → delegate_task(profile=primary, ...)
5. Если health DEGRADED/OFFLINE → проверить fallback
6. Если fallback OK → delegate_task(profile=secondary, ...)
7. Если оба недоступны → выполнить задачу в default с предупреждением
```
Маршрутизация MUST учитывать capabilities профиля: если задача требует `browser`, а `worker-fast` не имеет этого инструмента, маршрутизация не должна выбирать этот профиль.
## 3. Fallback-цепочки
### 3.1 Модель fallback'а
При отказе провайдера (rate limit, timeout, model unavailable) Hermes использует внутренний механизм credential pools (см. `CREDENTIAL_POOLS.md`). На уровне оркестратора реализуется дополнительный fallback:
```
worker-code (kimi-k2.7-code) → [ОТКАЗ]
├──→ worker-fast (grok-4.5) — быстрая альтернатива для кода
├──→ worker-code (deepseek-v4-pro, nvidia) — та же роль, другой провайдер
└──→ default (deepseek-v4-pro) — выполнить in-process
```
### 3.2 Стратегии fallback'а
| Стратегия | Описание | Применение |
|---|---|---|
| **Role-preserving** | Та же роль, другой провайдер | `worker-code` + `nvidia` вместо `opencode-go` |
| **Downgrade** | Более простой профиль | `worker-code``worker-fast` |
| **Escalate** | Более мощный профиль | `worker-fast``worker-code` |
| **In-process** | Выполнение в оркестраторе | Когда все worker'ы недоступны |
### 3.3 Политика повторных попыток
| Ошибка | Действие | Макс. повторов | Задержка |
|---|---|---|---|
| Rate limit (429) | Ждать + повторить тот же профиль | 3 | Exponential: 1s, 4s, 16s |
| Timeout (504) | Fallback на вторичный профиль | 1 | — |
| Model unavailable (503) | Fallback на вторичный профиль | 0 | — |
| Auth error (401/403) | Не повторять; alert оператору | 0 | — |
| Unknown error | Fallback → in-process | 1 | 5s |
## 4. Параллельное делегирование (Fan-out)
### 4.1 Паттерн
`delegate_task(tasks=[...])` запускает дочерние задачи параллельно:
```json
{
"tasks": [
{"goal": "Реализовать API endpoint /users", "profile": "worker-code"},
{"goal": "Написать тесты для /users endpoint", "profile": "worker-code"},
{"goal": "Обновить документацию API", "profile": "worker-fast"}
]
}
```
### 4.2 Ограничения
| Параметр | Значение по умолчанию | Описание |
|---|---|---|
| `max_concurrent_children` | 3 | Максимум параллельных дочерних задач |
| `max_spawn_depth` | 2 | Максимальная глубина вложенности оркестраторов |
| `max_iterations` | 50 | Максимум итераций на дочернюю задачу |
### 4.3 Сбор результатов
Оркестратор SHOULD:
1. Дождаться завершения всех дочерних задач (или таймаута)
2. Агрегировать результаты в порядке исходного списка
3. При частичном отказе — вернуть успешные результаты + ошибки для упавших
4. Предложить пользователю retry для упавших задач
## 5. Передача контекста
### 5.1 Механизмы
| Механизм | Формат | Применение |
|---|---|---|
| `goal` + `context` | Markdown/JSON | Основной механизм: цель + структурированный контекст |
| Файловая система | Файлы в рабочей директории | Общие артефакты (спецификации, результаты) |
| `memory` tool | MEMORY.md | Долгоживущие факты/решения между сессиями |
| `session_search` | FTS5-поиск | Поиск релевантной истории |
### 5.2 Формат контекста
```json
{
"goal": "Реализовать модуль аутентификации",
"context": {
"objective": "JWT-based authentication module",
"acceptance_criteria": ["Login endpoint", "Token refresh", "Password reset"],
"constraints": {"framework": "FastAPI", "database": "PostgreSQL"},
"decisions": ["Use python-jose for JWT", "Use passlib for hashing"],
"artifact_refs": ["specs/auth-spec.md", "schemas/user.sql"],
"completed_work": ["Database schema created", "User model defined"],
"pending_work": ["Implement /login", "Implement /refresh"],
"errors": []
}
}
```
## 6. Health-check worker-профилей
### 6.1 Процедура
Health-check MUST выполняться:
- Перед каждой делегацией задачи
- Периодически (раз в 5 минут) для фонового мониторинга
```bash
# Проверка конкретного профиля
hermes --profile worker-code chat -q "respond with 'OK'" --quiet
# Проверка статуса провайдера
hermes --profile worker-code doctor
```
### 6.2 Состояния
| Состояние | Критерий | Действие |
|---|---|---|
| `ONLINE` | Успешный ping < 5s | Нормальная маршрутизация |
| `DEGRADED` | Успешный ping, но > 5s или retries | Маршрутизация с предупреждением, приоритет fallback |
| `OFFLINE` | Ping failed / timeout 30s | Исключить из маршрутизации, алерт оператору |
| `INCOMPATIBLE` | Adapter version mismatch | Заблокировать запуск, требуется upgrade |
### 6.3 Метрики health-check
| Метрика | Порог WARNING | Порог CRITICAL |
|---|---|---|
| Ping latency | > 5s | > 30s (timeout) |
| Error rate (5 min) | > 10% | > 25% |
| Token usage rate | > 80% квоты | > 95% квоты |
| Active runs | > 5 | > 10 |
## 7. Паттерны обработки ошибок
### 7.1 Классификация ошибок делегирования
| Код | Ошибка | Действие |
|---|---|---|
| `DELEGATION_TIMEOUT` | Дочерняя задача превысила `max_iterations` | Возврат частичного результата; предложить разбить задачу |
| `DELEGATION_PROFILE_OFFLINE` | Профиль недоступен | Fallback на альтернативный профиль |
| `DELEGATION_RATE_LIMITED` | Исчерпана квота провайдера | Fallback на профиль с другим провайдером |
| `DELEGATION_TOOL_ERROR` | Ошибка инструмента в дочерней задаче | Повтор с уточнённым контекстом |
| `DELEGATION_DEPTH_EXCEEDED` | Превышена `max_spawn_depth` | Выполнить задачу in-process |
### 7.2 Таймауты
| Стадия | Таймаут по умолчанию | Описание |
|---|---|---|
| Запуск профиля | 10s | Время на инициализацию Hermes |
| Ответ на goal | 60s | Время до первого осмысленного ответа |
| Полное выполнение | 300s (5 min) | Общее время на задачу |
| Сбор результатов | 30s | Таймаут после завершения задачи |
## 8. Ссылки
- [HERMES.md](../adapters/HERMES.md) — спецификация Hermes-адаптера
- [SPECIFICATION.md](../SPECIFICATION.md) §6.4 — Runs, approvals и handoff
- [ARCHITECTURE.md](../ARCHITECTURE.md) §5 — Run и handoff state machine
- [CREDENTIAL_POOLS.md](CREDENTIAL_POOLS.md) — управление пулами ключей
- [MONITORING.md](../operations/MONITORING.md) — мониторинг health-check'ов