Ключі моделей
Набір ключів постачальників, якими інсталяція може відповідати, і екран, на якому оператор їх додає, вимикає та оживляє. До нього продукт мав один ключ, він жив в оточенні api, і заміна згорілого була деплоєм. З'явилося в AGNT2-366; з AGNT2-367 ключ може ще й сказати, які рівні моделі він обслуговує.
Хто це бачить: адміністратор платформи. Екран — /model-keys в адмінці.
Адмінка англійською
Перекладу вона не має, тому назви стовпців і станів нижче подано українською для читання, а на екрані ви побачите англійські: Vendor, Name, Levels, Ends in, State, Last used, Calls, Tokens; стани — Working, Resting, Needs attention, Switched off.
Порожній набір нічого не змінює
Це додає джерело. Коли в наборі нічим обслужити виклик, обидва постачальники повертаються до ключа з оточення, яким користувалися завжди: ANTHROPIC_API_KEY для відповідей, OPENAI_API_KEY для ембедингів. Отже:
- інсталяція, що не додала жодного ключа, поводиться рівно як раніше;
- увімкнення набору — це рядок на екрані, а не реліз;
- а режим відмови всієї цієї можливості — «як було».

Вище — інсталяція, у наборі якої немає нічого: стан, з якого починається будь-яка інсталяція. Api відповідає ключем з оточення, і екран каже про це над таблицею — бо таблиця з восьми ключів на відпочинку і таблиця без ключів описують той самий працюючий продукт. (Знімок зроблено до появи стовпця Рівні.)
Що каже рядок
| стовпець | |
|---|---|
| Постачальник | anthropic або openai |
| Назва | як ви його назвали — робоча область або акаунт, звідки він |
| Рівні | які рівні моделі цей ключ обслуговує; «all levels», якщо не названо жодного |
| Закінчується на | останні чотири символи: саме за ними ви звіряєте його з консоллю постачальника |
| Стан | один із чотирьох нижче |
| Останнє використання, Виклики, Токени | що через нього реально пройшло |
Один ключ — це один рядок LlmCredential, і звичайне читання цієї таблиці показує рівно те саме, що показує екран:
SELECT vendor, label, tiers, last4, "restingUntil", "attentionAt", "disabledAt", "requestCount"
FROM "LlmCredential";Значення в цьому переліку немає, бо воно й не читається: воно лежить конвертом AES-256-GCM, запечатаним на ідентифікатор самого рядка, — тож блоб, скопійований в інший рядок, не відкриється. Шифр той самий EnvelopeCipher, що зберігає власні секрети агента: AGNT2-366 спустив його в setup/core саме для того, щоб другого способу шифрувати не з'явилося.
Значення ключа не показують ніколи. Адміністраторові платформи — теж. У відповіді немає поля, куди його можна було б покласти, тому екран не зміг би його намалювати, навіть якби захотів, — а справжні тіла відповідей читає тест, який це доводить, з контролем, що показує: постачальник і назва в тих самих тілах знаходяться.
Які рівні обслуговує ключ
Продукт працює на трьох рівнях моделі — simple, smart, genius, — описаних на сторінці Рівні моделі та кеш початку запиту. Ключ може назвати рівні, які він обслуговує, і в набору питають ключ на постачальника і рівень.
- Не назвати жодного — означає обслуговувати всі. Так зберігається кожен ключ, доданий до появи рівнів, тому інсталяція, що ніколи не прив'язувала ключ до рівня, поводиться рівно як раніше — те саме правило, що й для порожнього набору.
- Один ключ на кілька рівнів — звичайний випадок:
simpleіsmartв одного постачальника зазвичай сидять на одному ключі. - Рівень може обслуговуватися ключами іншого постачальника, ніж рівень нижче. Сьогодні відповіді дає лише Anthropic, тож дорога відкрита в даних, але ще не пройдена.
Рівні — єдине, що в ключі змінюють після додавання: позначте їх просто в рядку, і зміна набуває чинності з наступного виклику моделі в кожній репліці api, бо читаються вони з бази на кожен виклик. Ключ з оточення обслуговує всі рівні.
Чотири стани
| стан | що означає | повернеться |
|---|---|---|
| Працює | у наборі, через нього йдуть виклики | — |
| Відпочиває | постачальник відповів 429 — квота вичерпана | сам, у момент, названий у retry-after постачальника, — або через LLM_CREDENTIAL_REST_MS (добу), якщо той нічого не назвав |
| Потребує уваги | постачальник відповів 401 / 403 — сам ключ відхилено: відкликаний, друкарська помилка, не з того акаунта | ніколи сам. Лише людина |
| Вимкнений | хтось вивів його з ладу. Історію збережено | коли хтось поверне |
Середні два намальовані по-різному навмисно. Ключу, що відпочиває, ніхто не потрібен; відхиленому потрібні ви. Пофарбувати обидва бурштиновим — означає відвести ваш погляд на рядок, якому нічого не потрібно. Злити їх — той самий дефект, який варто назвати: відкликаний ключ, що «одужує» за добу, ламає кожну відповідь увесь час, поки існує, і робить це мовчки.
А третій випадок не позначає нічого. Таймаут, обірваний сокет, 5xx або наш власний кривий запит — не провина ключа. Позначки на них спорожнили б набір на добу через погану хвилину в постачальника.
Відпочинок триває стільки, скільки сказав постачальник, і не довше (AGNT2-381). Налаштована доба раніше була не лише замовчуванням, а й нижньою межею, тому сплесковий ліміт, який Anthropic знімає за двадцять секунд, укладав ключ спати на двадцять чотири години — а на інсталяції з одним ключем це вся модель, що спить добу. Тепер слово постачальника перемагає завжди, коли воно є; доба — здогад на випадок, коли він промовчав.
LLM_CREDENTIAL_MARKING=off вимикає позначки зовсім. Це контроль для вимірювання і нічого більше: інсталяція, залишена на ньому, натикатиметься на той самий вичерпаний ключ на кожному виклику, вічно, і щоразу писатиме про це в лог.
Три дії
| кнопка | |
|---|---|
| Add a key | постачальник, назва, ключ і, за бажанням, рівні, які він обслуговує. Значення зберігається зашифрованим і назовні не виходить |
| Switch off / Switch on | вивести з ладу або повернути |
| Put back in service | припинити відпочинок достроково або зняти відмову, з якою ви розібралися |
Видалення немає, і це навмисно. Вимкнення зберігає рядок, бо до рядка прив'язана минула витрата, — а видалений робить питання «у що нам обійшовся цей ключ» без відповіді саме для того ключа, який прибрали саме тому, що він був дорогим.
За екраном — шість маршрутів, усі за префіксом admin/: вартовий дивиться на адресу, а не на декоратор, який хтось має не забути поставити.
GET /admin/llm-credentials усі ключі: постачальник, назва, рівні, останні чотири, стан, трафік
POST /admin/llm-credentials додати
PATCH /admin/llm-credentials/:id/tiers змінити обслуговувані рівні; порожній список — усі
POST /admin/llm-credentials/:id/disable вивести з ладу — це і є тутешнє «видалити»
POST /admin/llm-credentials/:id/enable повернути до ладу
POST /admin/llm-credentials/:id/revive перервати відпочинок достроково або зняти відмову, з якою людина розібраласяЯкий ключ візьме наступний виклик
Задається LLM_CREDENTIAL_ROTATION, і замовчування тут важливіше, ніж здається:
sticky — на розмову (за замовчуванням) | розмова лишається на одному ключі, обраному rendezvous-хешуванням по придатній множині. Дві репліки api доходять до однієї відповіді, нічим не обмінюючись, а відхід ключа на відпочинок зрушує лише ті розмови, що були на ньому |
request — на запит | ключ, яким найдовше не користувалися, на кожен виклик; черга — за lastUsedAt у базі |
За замовчуванням — на розмову, бо кеш промпта в постачальника належить тій робочій області, якій належить ключ. Вісім ключів з восьми робочих областей — це вісім різних кешів, і зміна ключа всередині розмови викидає збережений початок і платить за запис заново. Виміряно на справжньому api: ротація на кожен запит коштує у 3,4 раза дорожче за хід проти теплого кешу, а ротація на розмову коштує стільки ж, скільки один ключ, і при цьому все одно розмазує навантаження.
Що це означає на практиці — на сторінці Рівні моделі та кеш початку запиту.
3,4× — зі звіту AGNT2-366, який лежить на тій картці, а не в репозиторії; дослідження, з якого виріс сам набір ключів, — specs/AGNT2-369-openrouter-or-own-keys/research.md.
Вичерпання має приходити швидко, інакше карусель не дає нічого
Набір, що йде з вичерпаного ключа, марний, якщо «вичерпано» приходить не помилкою, а годиною очікування — людина вже пішла. Тому власний неспинний повтор усередині SDK постачальника вимкнено: він виконує надіслане «зачекай стільки-то» буквально, а на вичерпаній квоті ця вказівка називає час на години вперед, і перервати його нічим. Замість нього в api власний обмежений повтор (AGNT2-377): пауза між спробами не довша за ANTHROPIC_RETRY_WAIT_MAX_MS, а весь блокуючий виклик — не довший за ANTHROPIC_CALL_BUDGET_MS. Усі чотири числа — на сторінці Рівні моделі та кеш початку запиту.
Постачальник, що просить довго чекати, не просить нас повторити. Він повідомляє, що цей ключ на сьогодні все, — а це факт, на який треба реагувати, а не проспати його.
Дві репліки можуть узяти один ключ
Ніщо цьому не заважає, і ніщо не намагається. Усе, що стосується становища ключа, живе в базі — позначки, лічильники, черга, рівні, — бо за N реплік api будь-яке «на процес» — це N незалежних копій факту, який має бути один. Але сенс у тому, щоб розмазати навантаження, а не нормувати його, і сувора черга вимагала б блокування на гарячому шляху кожного виклику моделі заради справедливості, якої ніхто не просив.
Перевірити: завести другий ключ
- Увійдіть до адмінки адміністратором платформи → Ключі моделей.
- Add a key — другий ключ
anthropic, з іншої робочої області, жодного рівня не позначати. Рядок з'являється у стані Працює, у «Рівнях» — «all levels», «Закінчується на» збігається з останніми чотирма символами в консолі постачальника, Виклики — 0. - Проведіть дві різні розмови з агентом, по кілька ходів кожна.
- Відкрийте екран знову. Виклики зросли на обох рядках, і кожна розмова лишилася на одному ключі — це працює ротація за замовчуванням.
- Позначте на новому ключі лише
genius. З наступного виклику звичайні ходи його не чіпають, і його Виклики стоять на місці, доки якийсь хід не оцінять якgenius. - Switch off на одному ключі. Стан стає Switched off, а лічильники на місці.
- Switch on назад; новий трафік до нього повертається.
Єдине, чого не можна зробити в жодному пункті цієї послідовності, — прочитати ключ назад. Це властивість, а не недоробка.
Де код
| набір, стани, рівні, ротація | api/src/slices/system/llmCredential |
| шість маршрутів і їхні DTO | api/src/slices/admin/llmCredential |
| екран | admin/slices/platform/llmCredential |
Див. також Секрети і Роль адміністратора.