From 3f9a925f244e769efbf4fe0fcef2b83c79f10e2a Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Mon, 20 Jul 2026 18:54:01 +0700 Subject: [PATCH] =?UTF-8?q?docs:=20Domain=20Model,=20State=20Machines,=20C?= =?UTF-8?q?onnector=20API,=20Handoff,=20Operations,=20Security,=20ADR,=20R?= =?UTF-8?q?oadmap=20(+15=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=A2=D0=97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 26 +- docs/ADR_AND_SEQUENCES.md | 128 +++++ docs/CONNECTOR_API.md | 228 +++++++++ docs/DOMAIN_MODEL.md | 203 ++++++++ docs/HANDOFF_CONTRACT.md | 211 ++++++++ docs/OPERATIONS.md | 124 +++++ docs/ROADMAP.md | 110 ++++ docs/SECURITY_AND_PERFORMANCE.md | 95 ++++ docs/SPECIFICATION.md | 2 +- docs/STATE_MACHINES.md | 118 +++++ docs/adapters/HERMES.md | 349 +++++++++++++ docs/operations/BACKUP.md | 704 ++++++++++++++++++++++++++ docs/operations/MONITORING.md | 340 +++++++++++++ docs/patterns/CREDENTIAL_POOLS.md | 282 +++++++++++ docs/patterns/WORKER_ORCHESTRATION.md | 207 ++++++++ 15 files changed, 3125 insertions(+), 2 deletions(-) create mode 100644 docs/ADR_AND_SEQUENCES.md create mode 100644 docs/CONNECTOR_API.md create mode 100644 docs/DOMAIN_MODEL.md create mode 100644 docs/HANDOFF_CONTRACT.md create mode 100644 docs/OPERATIONS.md create mode 100644 docs/ROADMAP.md create mode 100644 docs/SECURITY_AND_PERFORMANCE.md create mode 100644 docs/STATE_MACHINES.md create mode 100644 docs/adapters/HERMES.md create mode 100644 docs/operations/BACKUP.md create mode 100644 docs/operations/MONITORING.md create mode 100644 docs/patterns/CREDENTIAL_POOLS.md create mode 100644 docs/patterns/WORKER_ORCHESTRATION.md diff --git a/README.md b/README.md index 1687599..bf3496f 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,35 @@ ## Документы +### Нормативные - [Каноническое ТЗ](docs/SPECIFICATION.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/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) ## Ключевой принцип diff --git a/docs/ADR_AND_SEQUENCES.md b/docs/ADR_AND_SEQUENCES.md new file mode 100644 index 0000000..2f9616c --- /dev/null +++ b/docs/ADR_AND_SEQUENCES.md @@ -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 +``` diff --git a/docs/CONNECTOR_API.md b/docs/CONNECTOR_API.md new file mode 100644 index 0000000..8211147 --- /dev/null +++ b/docs/CONNECTOR_API.md @@ -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 | diff --git a/docs/DOMAIN_MODEL.md b/docs/DOMAIN_MODEL.md new file mode 100644 index 0000000..29a2d49 --- /dev/null +++ b/docs/DOMAIN_MODEL.md @@ -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); +``` diff --git a/docs/HANDOFF_CONTRACT.md b/docs/HANDOFF_CONTRACT.md new file mode 100644 index 0000000..7d6c899 --- /dev/null +++ b/docs/HANDOFF_CONTRACT.md @@ -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 diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md new file mode 100644 index 0000000..eda0edc --- /dev/null +++ b/docs/OPERATIONS.md @@ -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 diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..dad2eda --- /dev/null +++ b/docs/ROADMAP.md @@ -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 последовательных ручных прогонов без ошибок diff --git a/docs/SECURITY_AND_PERFORMANCE.md b/docs/SECURITY_AND_PERFORMANCE.md new file mode 100644 index 0000000..ab98d21 --- /dev/null +++ b/docs/SECURITY_AND_PERFORMANCE.md @@ -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 | diff --git a/docs/SPECIFICATION.md b/docs/SPECIFICATION.md index 4c7bb50..46b759f 100644 --- a/docs/SPECIFICATION.md +++ b/docs/SPECIFICATION.md @@ -7,7 +7,7 @@ | Статус | **Draft — implementation blocked** | | Владелец решения | 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 требуют обоснования отклонения. diff --git a/docs/STATE_MACHINES.md b/docs/STATE_MACHINES.md new file mode 100644 index 0000000..5976f73 --- /dev/null +++ b/docs/STATE_MACHINES.md @@ -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) diff --git a/docs/adapters/HERMES.md b/docs/adapters/HERMES.md new file mode 100644 index 0000000..7e77737 --- /dev/null +++ b/docs/adapters/HERMES.md @@ -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.`. + +## 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//` с собственными `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) — бэкап конфигурации diff --git a/docs/operations/BACKUP.md b/docs/operations/BACKUP.md new file mode 100644 index 0000000..5d33224 --- /dev/null +++ b/docs/operations/BACKUP.md @@ -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 diff --git a/docs/operations/MONITORING.md b/docs/operations/MONITORING.md new file mode 100644 index 0000000..540411b --- /dev/null +++ b/docs/operations/MONITORING.md @@ -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 +# 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 +``` + +### 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 diff --git a/docs/patterns/CREDENTIAL_POOLS.md b/docs/patterns/CREDENTIAL_POOLS.md new file mode 100644 index 0000000..57a30fb --- /dev/null +++ b/docs/patterns/CREDENTIAL_POOLS.md @@ -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 diff --git a/docs/patterns/WORKER_ORCHESTRATION.md b/docs/patterns/WORKER_ORCHESTRATION.md new file mode 100644 index 0000000..860e684 --- /dev/null +++ b/docs/patterns/WORKER_ORCHESTRATION.md @@ -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'ов