282 lines
12 KiB
Markdown
282 lines
12 KiB
Markdown
# 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
|