Высокодоступное планирование: Ротация, фиксированные учётные записи, липкие сессии и повторные попытки при сбоях
После использования Antigravity Tools в качестве локального AI-шлюза некоторое время вы неизбежно столкнётесь с одной и той же проблемой: чем меньше учётных записей, тем чаще 429/401/invalid_grant, чем больше учётных записей, тем менее понятно «какая учётная запись работает», а коэффициент попадания в кеш падает.
В этом уроке будет чётко объяснено планирование: как оно выбирает учётную запись, что такое «липкая сессия», когда происходит принудительная ротация, и как с помощью «режима фиксированной учётной записи» сделать планирование управляемым.
Что вы сможете сделать после изучения
- Понять, что на самом деле делают три режима планирования Antigravity Tools в реальных запросах
- Знать, как генерируется «отпечаток сессии (session_id)» и как он влияет на липкое планирование
- Включить/выключить «режим фиксированной учётной записи» в GUI и понять, какие логики планирования он перекрывает
- При встрече 429/5xx/invalid_grant знать, как система отметит ограничение частоты, как повторит попытку и когда ротирует
Ваша текущая проблема
- Claude Code или OpenAI SDK внезапно дают 429 при работе, а при повторной попытке меняют учётную запись, коэффициент попадания в кеш падает
- Несколько клиентов параллельно выполняют задачи, часто «попираются» друг на друга статусы учётных записей
- Вы хотите устранять неполадки, но не знаете, какая учётная запись обслуживает текущий запрос
- Вы хотите использовать только «самую стабильную учётную запись», но система постоянно ротирует
Когда использовать этот подход
- Вам нужно сбалансировать «стабильность (меньше ошибок)» и «попадание в кеш (одна учётная запись)»
- Вы хотите, чтобы один и тот же диалог как можно дольше использовал одну и ту же учётную запись (уменьшая колебания Prompt Caching)
- Вы хотите выполнить тестирование цвета/устранение неполадок, зафиксировав все запросы на одной учётной записи
🎒 Подготовка перед началом
- Подготовьте минимум 2 доступные учётные записи (чем меньше пул учётных записей, тем меньше пространства ротации)
- Служба обратного прокси запущена (на странице «API Proxy» виден статус Running)
- Вы знаете, где находится файл конфигурации (если вам нужно вручную изменить конфигурацию)
Сначала пройдите урок о конфигурации
Если вы ещё не знакомы с gui_config.json и какие конфигурации поддерживают горячее обновление, сначала посмотрите Конфигурация полностью: AppConfig/ProxyConfig, место сохранения на диск и семантика горячего обновления.
Основная идея: через какие «уровни планирования» пройдёт один запрос
Планирование — это не «одиночный переключатель», а несколько механизмов, наложенных друг на друга:
- SessionManager сначала даёт запросу отпечаток сессии (session_id)
- При каждой повторной попытке Handlers требуют, чтобы TokenManager принудительно ротировал (
attempt > 0) - TokenManager затем выбирает учётную запись по: фиксированная учётная запись → липкая сессия → окно 60s → ротация
- При встрече 429/5xx будет записана информация об ограничении частоты, при последующем выборе учётной записи такие учётные записи будут активно пропущены
Что такое «отпечаток сессии (session_id)»?
Отпечаток сессии — это «по возможности стабильный Session Key», используемый для привязки нескольких запросов одного и того же диалога к одной и той же учётной записи.
В запросах Claude приоритет:
metadata.user_id(явно передано клиентом и не пусто и не содержит префикса"session-")- Первое «достаточно длинное» пользовательское сообщение выполняет хеширование SHA256, затем усекается до
sid-xxxxxxxxxxxxxxxx
Соответствующая реализация: src-tauri/src/proxy/session_manager.rs (у Claude/OpenAI/Gemini есть своя логика извлечения).
Маленькая деталь: почему смотреть только на первое пользовательское сообщение?
В исходном коде явно написано «хешировать только содержимое первого пользовательского сообщения, не смешивая имя модели или временной метки», цель — заставить несколько запросов одного диалога по возможности генерировать один и тот же session_id, тем самым повышая коэффициент попадания в кеш.
Приоритет выбора учётной записи TokenManager
Основной вход TokenManager:
TokenManager::get_token(quota_group, force_rotate, session_id, target_model)
Что он делает можно понять по приоритету:
- Режим фиксированной учётной записи (Fixed Account): если в GUI включён «режим фиксированной учётной записи» (настройка рантайма), и эта учётная запись не ограничена частотой, не защищена квотой, будет использована напрямую.
- Липкая сессия (Session Binding): если есть
session_idи режим планирования неPerformanceFirst, будет с приоритетом повторно использоваться учётная запись, привязанная к этой сессии. - Повторное использование глобального окна 60s: если
session_idне передано (или привязка ещё не успешна), в неPerformanceFirstбудет как можно дольше повторно использоваться «последняя использованная учётная запись» в течение 60 секунд. - Ротация (Round-robin): если ни одно из вышеперечисленных не применимо, учётная запись выбирается по глобальному самовозрастающему индексу ротации.
Кроме того, есть два «невидимых правила», которые сильно влияют на ощущения:
- Учётные записи сначала сортируются: ULTRA > PRO > FREE, в одном уровне приоритет — учётные записи с более высоким остатком квоты.
- Сбой или ограничение частоты будут пропущены: учётные записи, которые уже пытались и неуспешно, войдут в коллекцию
attempted; помеченные как ограниченные частотой учётные записи будут пропущены.
В чём разница между тремя режимами планирования
В конфигурации вы увидите: CacheFirst / Balance / PerformanceFirst.
Взяв за основу «реальную ветку TokenManager на стороне бэкенда», их ключевая разница только в одной: включена ли липкая сессия + повторное использование окна 60s.
PerformanceFirst: пропускает липкую сессию и повторное использование окна 60s, напрямую идёт по ротации (и продолжает пропускать ограниченные частотой/защищённые квотой учётные записи).CacheFirst/Balance: оба включают липкую сессию и повторное использование окна 60s.
О max_wait_seconds
Во фронтенде/структуре конфигурации есть max_wait_seconds, и в UI он позволяет регулировать только в CacheFirst. Но текущая логика планирования на стороне бэкенда только ветвится по mode, не читает max_wait_seconds.
Как повторные попытки и «принудительная ротация» связываются
В обработчиках OpenAI/Gemini/Claude используется похожий режим обработки повторных попыток:
- Первая попытка:
force_rotate = false - Вторая и последующие:
force_rotate = true(attempt > 0), TokenManager пропустит повторное использование липкости, напрямую найдёт следующую доступную учётную запись
При встрече ошибок 429/529/503/500 и т. д.:
- обработчик вызовет
token_manager.mark_rate_limited(...), чтобы записать эту учётную запись как «ограниченную частотой/перегруженную», при последующем планировании будут активно пропускать её. - В пути совместимости с OpenAI также будет пытаться разобрать
RetryInfo.retryDelayилиquotaResetDelayиз JSON ошибки, подождать небольшое время, затем продолжить повторную попытку.
Следуйте за мной: настройте планирование на «управляемый»
Шаг 1: Сначала подтвердите, что у вас действительно есть «пул учётных записей»
Почему Независимо от того, насколько продвинуто планирование, если в пуле только 1 учётная запись, выбирать не из чего. Многие проблемы «ротация не работает/липкая сессия не ощущается» имеют корнем слишком мало учётных записей.
Действия Откройте страницу «Accounts», подтвердите, что как минимум 2 учётные записи находятся в доступном состоянии (не disabled / proxy disabled).
Вы должны увидеть: минимум 2 учётные записи могут нормально обновлять квоту, а после запуска обратного прокси active_accounts не равно 0.
Шаг 2: Выберите режим планирования в GUI
Почему Режим планирования определяет, будет ли один и тот же диалог как можно дольше использовать одну и ту же учётную запись, или каждый раз ротацию.
Действия Перейдите на страницу «API Proxy», найдите карточку «Планирование и ротация учётных записей», выберите один из режимов:
Balance: значение по умолчанию, рекомендуется. В большинстве случаев стабильнее (липкая сессия + ротация при сбоях).PerformanceFirst: высокий параллелизм, короткие задачи, вам важнее пропускная способность, чем попадание в кеш, выбирайте этот.CacheFirst: если вы хотите, чтобы диалог фиксировал учётную запись, можно выбрать его (текущее поведение на стороне бэкенда мало отличается отBalance).
Если вы хотите вручную изменить конфигурацию, соответствующий фрагмент:
{
"proxy": {
"scheduling": {
"mode": "Balance",
"max_wait_seconds": 60
}
}
}Вы должны увидеть: после переключения режима сразу записывается в gui_config.json, при работающей службе обратного прокси вступает в силу немедленно (без перезапуска).
Шаг 3: Включите «режим фиксированной учётной записи» (отключите ротацию)
Почему При устранении неполадок/тестировании цвета/или вы хотите «прибить» определённую учётную запись к определённому клиенту, режим фиксированной учётной записи — самый прямой способ.
Действия В той же карточке откройте «Режим фиксированной учётной записи», затем выберите учётную запись в выпадающем списке.
Не забудьте: этот переключатель доступен только когда служба обратного прокси Running.
Вы должны увидеть: последующие запросы будут с приоритетом использовать эту учётную запись; если она ограничена частотой или защищена квотой, откатится к ротации.
Фиксированная учётная запись — это настройка рантайма
Режим фиксированной учётной записи — это состояние рантайма (динамически устанавливается через GUI или API), не сохраняется в gui_config.json. После перезапуска службы обратного прокси фиксированная учётная запись вернётся в пустое (вернётся в режим ротации).
Шаг 4: При необходимости очистите «привязку сессии»
Почему Липкая сессия записывает session_id -> account_id в памяти. Если вы на одной машине выполняете разные эксперименты (например, переключение пулов учётных записей, переключение режимов), старая привязка может мешать вам наблюдать.
Действия В карточке «Планирование и ротация учётных записей» нажмите «Очистить привязки» в правом верхнем углу.
Вы должны увидеть: старые сессии будут снова распределены учётные записи (следующий запрос снова привяжется).
Шаг 5: Используйте заголовок ответа, чтобы подтвердить «какая учётная запись обслуживает»
Почему Вы хотите подтвердить, что планирование соответствует ожиданиям, самый надёжный способ — получить «идентификатор текущей учётной записи» с сервера.
Действия Отправьте не потоковый запрос на совместимую с OpenAI конечную точку, затем наблюдайте за заголовком X-Account-Email в ответе.
# Пример: минимальный запрос OpenAI Chat Completions
# Обратите внимание: model должен быть доступен/маршрутизирован в вашей текущей конфигурации
curl -i "http://127.0.0.1:8045/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-REPLACE_ME" \
-d '{
"model": "gemini-3-pro-high",
"stream": false,
"messages": [{"role": "user", "content": "hello"}]
}'Вы должны увидеть: в заголовках ответа появляется следующее содержимое (пример):
X-Account-Email: [email protected]
X-Mapped-Model: gemini-3-pro-highКонтрольная точка ✅
- Вы можете чётко сказать, какой из трёх механизмов: «фиксированная учётная запись», «липкая сессия», «ротация» перекрывает другой
- Вы знаете, как получается
session_id(приоритетmetadata.user_id, иначе хеш первого пользовательского сообщения) - При встрече 429/5xx вы можете ожидать: система сначала запишет ограничение частоты, затем сменит учётную запись и повторит попытку
- Вы можете использовать
X-Account-Emailдля подтверждения, какая учётная запись обслуживает текущий запрос
Напоминания о возможных ошибках
Если в пуле только 1 учётная запись, не ожидайте, что «ротация спасёт вас» Ротация — это «выбрать другую учётную запись», если в пуле нет второй учётной записи, 429/invalid_grant будет возникать чаще.
CacheFirst— это не «ждать всегда, пока не станет доступным» Текущая логика планирования на стороне бэкенда при ограничении частоты склоняется к развязыванию и переключению учётной записи, а не к долгосрочному блокирующему ожиданию.Фиксированная учётная запись не является абсолютно принудительной Если фиксированная учётная запись отмечена как ограниченная частотой или защищена квотой, система откатится к ротации.
Краткое содержание урока
- Цепочка планирования: обработчик извлекает
session_id→TokenManager::get_tokenвыбирает учётную запись → при ошибкеattempt > 0принудительная ротация - Два ваших самых часто используемых переключателя: режим планирования (включать ли липкую сессию/повторное использование 60s) + режим фиксированной учётной записи (напрямую указать учётную запись)
- 429/5xx будут записаны как «состояние ограничения частоты», при последующем планировании эта учётная запись будет пропущена, пока не истечёт время блокировки
Предварительный просмотр следующего урока
Следующий урок мы посмотрим Маршрутизацию моделей: когда вы хотите предоставить «стабильное внешнее множество моделей», а также использовать подстановочные знаки/предустановленные стратегии, как настроить и устранять неполадки.
Приложение: Ссылка на исходный код
Нажмите, чтобы развернуть и просмотреть местоположение исходного кода
Дата обновления: 2026-01-23
| Функция | Путь к файлу | Номер строки |
|---|---|---|
| Режим планирования и структура конфигурации (StickySessionConfig) | src-tauri/src/proxy/sticky_config.rs | 1-36 |
| Генерация отпечатка сессии (Claude/OpenAI/Gemini) | src-tauri/src/proxy/session_manager.rs | 1-159 |
| TokenManager: поля режима фиксированной учётной записи и инициализация | src-tauri/src/proxy/token_manager.rs | 27-50 |
| TokenManager: основная логика выбора учётной записи (фиксированная учётная запись/липкая сессия/окно 60s/ротация/защита квоты) | src-tauri/src/proxy/token_manager.rs | 470-940 |
| TokenManager: invalid_grrant автоматически отключает и удаляет из пула | src-tauri/src/proxy/token_manager.rs | 868-878 |
| TokenManager: запись ограничения частоты и очистка успешного API | src-tauri/src/proxy/token_manager.rs | 1087-1147 |
| TokenManager: обновление конфигурации планирования / очистка привязок сессии / сеттер фиксированной учётной записи | src-tauri/src/proxy/token_manager.rs | 1419-1461 |
| ProxyConfig: определение полей планирования и значений по умолчанию | src-tauri/src/proxy/config.rs | 174-257 |
| При запуске прокси синхронизируется конфигурация планирования | src-tauri/src/commands/proxy.rs | 70-100 |
| Команды Tauri, связанные с планированием (get/update/clear bindings/fixed account) | src-tauri/src/commands/proxy.rs | 478-551 |
| Обработчик OpenAI: session_id + принудительная ротация при повторной попытке | src-tauri/src/proxy/handlers/openai.rs | 160-182 |
| Обработчик OpenAI: запись ограничения частоты 429/5xx + разбор повторной задержки | src-tauri/src/proxy/handlers/openai.rs | 349-367 |
| Обработчик Gemini: session_id + принудительная ротация при повторной попытке | src-tauri/src/proxy/handlers/gemini.rs | 62-88 |
| Обработчик Gemini: запись ограничения частоты 429/5xx и ротация | src-tauri/src/proxy/handlers/gemini.rs | 279-299 |
| Обработчик Claude: извлечение session_id и передача в TokenManager | src-tauri/src/proxy/handlers/claude.rs | 517-524 |
| Разбор повторной задержки 429 (RetryInfo.retryDelay / quotaResetDelay) | src-tauri/src/proxy/upstream/retry.rs | 37-66 |
| Идентификация причин ограничения частоты и экспоненциальный откат (RateLimitTracker) | src-tauri/src/proxy/rate_limit.rs | 154-279 |
Ключевая структура:
StickySessionConfig: структура конфигурации планирования (mode,max_wait_seconds)TokenManager: пул учётных записей, привязка сессий, режим фиксированной учётной записи, трекер ограничения частотыSessionManager: извлечениеsession_idиз запроса