Представим ситуацию: вы — руководитель средней IT‑команды. В наше время у каждого сотрудника, от технического писателя до архитектора, есть платная подписка на AI‑инструменты. Так как это производственная необходимость, компания компенсирует затраты на личные подписки каждому члену команды.
Возьмём типичные ситуации для разных должностей:
Василий, технический писатель. Долгое время ему хватало подписки за 20$. Но то ли вендор лимиты втихую урезал, толи эффективность команды повысилась, но хватать ему 20$ подписки перестало. Хочет купить подписку за 100$, но понимает, что большая часть квоты у него всегда будет неиспользованной
Артём, QA тестировщик. У него подписка за 100$, но распределение потребления неравномерное. Когда существовали пятичасовые лимиты — было особенно тяжко. Перед релизом всё съедается в ноль, а в остальное время он едва ли 50% использует
Аня, Senior backend‑разработчица. Обожает оставлять агентов на ночь в режиме цели (goal), работает много и активно. Подписки за 200$ перестало хватать. Пришлось покупать вторую. Но на практике она не используется большую часть времени. Расход квоты всегда в районе 25%

Кто‑то может возразить: «Но ведь есть же официальные Business/Enterprise‑тарифы! Зачем изобретать велосипед?». Проблема особенно актуальна для IT в России, когда внезапно оказывается, что велосипеды не продают российским юрлицам, а даже если продали, в любой момент могут превратить велосипед в тыкву.
В идеале наша архитектура должна выглядеть следующим образом: объединяем все подписки в единый пул, ставим перед ним балансировщик, выдаём ключи доступа каждому разработчику и начинаем расходовать пул подписок по мере необходимости. С каждого по способностям, каждому по потребностям, прямо как завещали в прекрасном светлом прошлом!
Оказывается, мы далеко не первые, кто додумался до такой архитектуры, и умные люди уже давно наклепали опенсорс‑проектов похожей направленности. Одним из популярных решений на данный момент является codex‑lb, который набрал уже почти 3к звёзд на гитхабе.
Однако кроличья нора оказывается глубже, чем кажется на первый взгляд. Если присмотреться, то у такой архитектурной идеи есть множество нюансов, которые нужно учесть. Попробуем разобрать, что предлагает нам рассматриваемый выше проект.
Developer A ─┐ Developer B ─┤ Developer C ─┤ ... ├──► codex-lb ───► ChatGPT account #1 ($20) Developer O ─┘ │ ├─► ChatGPT account #2 ($100) │ ├─► ChatGPT account #3 ($200) │ ├─► ... │ └─► ChatGPT account #15 │ ├── routing ├── quotas ├── sticky sessions ├── API keys └── usage accounting
Для разработчика архитектура превращается в blackbox. У разработчика есть только ключ: он не выбирает чью подписку использовать, он не знает ничего про лимиты, текущее потребление и нагруженность. Оркестратор сам за него выбирает на какой аккаунт послать запрос, у кого осталось больше квоты, когда у какого аккаунт произойдет reset и какие модели доступны.
Для начала администратору необходимо добавить аккаунты в пул подписок. Делает он это через OAuth‑инфраструктуру Codex/OpenAI и хранит access/refresh/id токены аккаунтов. Токены шифруются; в коде есть отдельный механизм обновления refresh‑токенов и даже механизмы для горизонтального масштабирования.
Разработчики не получают прямого доступа к OAuth‑аккаунтам, они имеют только внутренние ключи оркестратора. При этом, настройки достаточно гибкие, и можно внутри системы ограничивать доступные модели, лимиты по токенам, задать собственные дневные/недельные/месячные лимиты под каждого.
Хорошо, с сетапом разобрались. Но теперь возникает вопрос — а как внутри системы балансируются запросы между подписками? Механизм оказывается сложнее базового round robin. Нам предлагается на выбор несколько стратегий:
Capacity weighted
Account A 20% remaining Account B 70% remaining Account C 90% remaining ↓ больше traffic │ ┌──────┴──────┐ B C
При данном режиме выбор падает на тот аккаунт, где больше всего лимитов. При этом учитываются также и ограничения на ключ — выбираются именно те аккаунты, которые могут обслужить текущий запрос по доступности определенной модели/reasoning
Relative availability
Здесь мы пытаемся поймать баланс между доступностью и остатком квоты. Для каждого аккаунта считаем величину, равную:
остаток квоты / количество секунд до reset
В таком формате больший приоритет получают аккаунты, которые уже подходят к reset, но всё ещё имеют достаточный лимит:
Account A осталось 40% reset через 2 дня Account B осталось 40% reset через 6 дней
Аккаунт A получает более высокий приоритет: его оставшуюся квоту нужно быстрее использовать, иначе она пропадёт при reset.
Для команды, где люди работают в разное время суток, это уже довольно умно.
Reset drain
Тут мы уже пытаемся выжать подписку на максимум. На вероятность выбора теперь влияют только секунды до reset, без учёта оставшейся квоты:
A: 46% left → reset tomorrow B: 79% left → reset in 4 days C: 65% left → reset in 6 days traffic ↓ A ↓ после него B/C
Но это может периодически приводить к неприятным ситуациям, когда лимиты часто заканчиваются прямо в процессе работы.
А теперь перейдём к самому проблемному моменту всей архитектуры.
Пользователь работал через аккаунт А, разрабатывал какую‑то фичу. Квота аккаунта А закончилась, фича еще не доделана. Как нам безопасно и эффективно продолжить на аккаунте Б и можно ли в принципе это сделать?
Для этого вводим два понятия: Sticky routing и Hard continuation.
Это не настройки‑переключатели, а состояния, которые оркестратор определяет автоматически и от которых зависит, можем ли мы продолжить текущую сессию в другой подписке, или нет?
Возьмём для примера стандартный флоу работы с агентом
THREAD Весь чат на несколько часов │ ├── TURN 1 │ User: "Исследуй auth и исправь баг" │ │ │ ├── inference #1 │ ├── tool call │ ├── inference #2 │ ├── tool call │ ├── inference #3 │ └── Assistant: "Готово" │ ├── TURN 2 │ User: "Теперь добавь тесты" │ ├── inference #1 │ ├── ... │ └── Assistant: "Готово" │ └── TURN 3 User: "А теперь отрефактори"
Особенности кодекса состоят в том, что между вызовами внутри одного turn он не передаёт каждый раз полную историю, вместо повторной передачи всего контекста используется продолжение через previous_response_id. Этот идентификатор ссылается на состояние, доступное в рамках соответствующего аккаунта. Между turns агента же, отправляется полная дельта текущего контекстного окна.
Зачем всё это было нужно? Чтобы понять простую вещь: если внутри одного turn закончился лимит, например после inference #2, то мы не сможем безопасно продолжить работу через другой аккаунт, так как находимся в процессе исполнения. То есть для типичного сценария: закончилась подписка А → перенаправляем на подписку Б, такой кейс не подходит. И нам приходится либо создавать новую сессию, либо ждать, пока сбросятся лимиты. Это и есть Hard continuation.
Если же квота аккаунта A закончилась уже после завершения TURN 1., то мы спокойно передаём контекст через аккаунт Б и продолжаем работу там. Это как раз механизм Sticky routing
Подводя итог, можно отметить, что reset drain стратегия хоть и является наиболее экономически выгодной, тем не менее сильно уступает в удобстве для рядового разработчика.

Но тут все замечают слона в комнате. Официальной политикой OpenAI запрещена передача доступа к аккаунту третьим лицам.
Данная статья не является рекомендацией по обходу официальных правил или нарушению принятых Terms of Use и Account sharing policy.
Все персонажи и события вымышлены, любые совпадения с реальными людьми или событиями случайны

