Skip to content

Ключі моделей

Набір ключів постачальників, якими інсталяція може відповідати, і екран, на якому оператор їх додає, вимикає та оживляє. До нього продукт мав один ключ, він жив в оточенні 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 відповідає ключем зі свого оточення

Вище — інсталяція, у наборі якої немає нічого: стан, з якого починається будь-яка інсталяція. Api відповідає ключем з оточення, і екран каже про це над таблицею — бо таблиця з восьми ключів на відпочинку і таблиця без ключів описують той самий працюючий продукт. (Знімок зроблено до появи стовпця Рівні.)

Що каже рядок

стовпець
Постачальникanthropic або openai
Назваяк ви його назвали — робоча область або акаунт, звідки він
Рівніякі рівні моделі цей ключ обслуговує; «all levels», якщо не названо жодного
Закінчується наостанні чотири символи: саме за ними ви звіряєте його з консоллю постачальника
Станодин із чотирьох нижче
Останнє використання, Виклики, Токенищо через нього реально пройшло

Один ключ — це один рядок LlmCredential, і звичайне читання цієї таблиці показує рівно те саме, що показує екран:

sql
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 незалежних копій факту, який має бути один. Але сенс у тому, щоб розмазати навантаження, а не нормувати його, і сувора черга вимагала б блокування на гарячому шляху кожного виклику моделі заради справедливості, якої ніхто не просив.

Перевірити: завести другий ключ

  1. Увійдіть до адмінки адміністратором платформи → Ключі моделей.
  2. Add a key — другий ключ anthropic, з іншої робочої області, жодного рівня не позначати. Рядок з'являється у стані Працює, у «Рівнях» — «all levels», «Закінчується на» збігається з останніми чотирма символами в консолі постачальника, Виклики — 0.
  3. Проведіть дві різні розмови з агентом, по кілька ходів кожна.
  4. Відкрийте екран знову. Виклики зросли на обох рядках, і кожна розмова лишилася на одному ключі — це працює ротація за замовчуванням.
  5. Позначте на новому ключі лише genius. З наступного виклику звичайні ходи його не чіпають, і його Виклики стоять на місці, доки якийсь хід не оцінять як genius.
  6. Switch off на одному ключі. Стан стає Switched off, а лічильники на місці.
  7. Switch on назад; новий трафік до нього повертається.

Єдине, чого не можна зробити в жодному пункті цієї послідовності, — прочитати ключ назад. Це властивість, а не недоробка.

Де код

набір, стани, рівні, ротаціяapi/src/slices/system/llmCredential
шість маршрутів і їхні DTOapi/src/slices/admin/llmCredential
екранadmin/slices/platform/llmCredential

Див. також Секрети і Роль адміністратора.