Почему конфигурация LLM-сервиса является кодом поведения

Изменение model alias, температуры, лимита шагов, списка tools или fallback route меняет пользовательский результат без единой строки нового кода. Ошибка может мгновенно затронуть весь парк workers, потому что remote config распространяется быстрее deploy.

Поэтому конфигурация требует тех же гарантий, что релиз: review, тесты, версия, controlled rollout и откат. «Это всего лишь настройка» — частая причина инцидентов с большим blast radius.

Разделите пять классов управляемого состояния

Runtime config — timeout, concurrency и routes. Secrets — ссылки на credentials, но не значения. Policy — права и запреты. AI artifacts — prompt, model, tools и eval contract. Environment wiring — endpoints и регионы.

Не складывайте всё в один JSON. Secrets требуют отдельного vault и rotation, policy — более строгого approval, а prompts — quality evals. Общий manifest может ссылаться на версии каждого слоя.

Typed schema вместо словаря произвольных ключей

JSON Schema фиксирует типы, required-поля, enum, диапазоны и структуру. Указывайте dialect через $schema. Для закрытой конфигурации используйте additionalProperties: false: опечатка max_tool_calss должна отклонить релиз, а не молча оставить старый default.

Версионируйте schema отдельно от instance и проверяйте, какие версии runtime способны её читать.

Структурной валидации недостаточно

Значения могут быть корректного типа и всё равно образовывать опасную комбинацию: timeout шага больше общего deadline, fallback не поддерживает output schema, регион запрещён policy, а max concurrency превышает quota.

Semantic validator должен знать capabilities моделей, data classification, budgets и зависимости. Результат validation сохраняйте как артефакт с версией правил.

Immutable version и content hash

Опубликованную версию не редактируют на месте. Создайте новую, вычислите canonical hash и переведите alias только после проверок. Trace хранит и version ID, и hash — имя без неизменяемости не доказывает содержимое.

Kubernetes поддерживает immutable ConfigMap: после включения его data нельзя изменить. Паттерн «новое имя на каждую версию» упрощает rollback и исключает тихое переписывание mounted state.

Desired state через GitOps

OpenGitOps формулирует desired state как declarative, versioned and immutable, pulled automatically и continuously reconciled. Для LLM-сервиса это означает: утверждённый manifest хранится в version control, а controller приводит environments к нему.

Панель управления не должна становиться вторым источником истины. Emergency override допустим только с TTL, owner и автоматическим возвратом к декларативному состоянию.

Защита от потерянных обновлений

Два оператора могут независимо прочитать v12 и записать разные v13. Используйте compare-and-swap: обновление содержит ожидаемую base version и отклоняется при конфликте.

Kubernetes применяет resourceVersion для обнаружения stale update и возвращает 409. Не решайте конфликт автоматическим last-write-wins для policy, model route и budget.

Atomic snapshot вместо поэлементного reload

Worker сначала загружает полный candidate snapshot, проверяет schema, references и совместимость, затем одной операцией меняет активный указатель. Запрос использует одну resolved version от начала до конца.

Если model route обновился, а prompt ещё нет, возникает несовместимый hybrid. Поэтому related keys публикуются bundle, а не россыпью независимых watchers.

Last-known-good и fail-closed

При недоступном control plane worker продолжает работу с последней проверенной версией в пределах max staleness. Ошибочный новый snapshot не должен вытеснять рабочий.

Но для отзыва permissions или compromised credential бесконечный stale режим опасен. Для каждого класса задайте fail-open или fail-closed, TTL и безопасную деградацию.

Rollout конфигурации по ступеням

Static validation проходит в PR, integration tests — в CI, затем candidate применяется к staging и canary workers или stable cohort. Сравнивайте task success, invalid tool calls, latency, cost и policy events.

Отдельные ключи классифицируйте по риску. Изменение log level можно распространить быстро; новый write-tool или data region требует approval и полноценного progressive delivery.

Config drift: desired не равен actual

Drift возникает из-за ручного override, пропущенного события, stale cache или частично обновлённого региона. Worker регулярно сообщает active version/hash, а reconciler сравнивает их с desired state.

Dashboard показывает распределение versions по service, region и tenant cohort. Алерт нужен на неизвестный hash, долгий mixed state и превышение допустимой staleness.

Audit trail без секретов

Записывайте actor, change request, old/new versions, semantic diff, approvals, rollout stages и rollback reason. В runtime trace достаточно version references; полный snapshot хранится в защищённом registry.

Не помещайте API keys в ConfigMap, Git или audit diff. Kubernetes прямо предупреждает, что ConfigMap не обеспечивает secrecy или encryption.

Rollback — переключение alias, а не ручная правка

Stable target выбирают до релиза и сохраняют прогретым. Откат атомарно меняет desired alias на предыдущий bundle и проходит тем же reconciler. Не пытайтесь вспомнить старые значения по dashboard.

Если новая версия уже создала состояние, проверьте backward compatibility. Rollback runtime не обязан делать старую schema способной прочитать новые checkpoints.

Тесты конфигурационной системы

Проверяйте unknown field, неверный enum, отсутствующую ссылку, несовместимую модель, stale base version, разрыв control plane, corrupted cache и частичное обновление региона. Chaos-тест должен доказать переход на last-known-good.

Отдельный тест запускает два конкурентных update и ожидает один conflict, а не потерю первого изменения.

Чек-лист production-конфигурации

  • Классы config, secrets, policy и artifacts разделены.
  • Schema фиксирует dialect и запрещает неизвестные поля.
  • Semantic validator проверяет cross-field invariants.
  • Published versions immutable и имеют hash.
  • Desired state хранится в одном versioned source.
  • Updates защищены optimistic concurrency.
  • Snapshot применяется атомарно на весь запрос.
  • Last-known-good имеет TTL и fail policy.
  • Worker сообщает actual version для drift detection.
  • Canary и rollback проверены заранее.
Можно ли хранить prompt прямо в ConfigMap?
Технически да для небольшого текста, но production prompt лучше вести как immutable artifact с review, eval report и отдельной версией, на которую ссылается manifest.
Нужно ли перезапускать pods после изменения config?
Зависит от механизма. Для критичных bundle безопаснее controlled rollout; hot reload допустим, если snapshot валидируется и переключается атомарно.
Что делать при недоступном config provider?
Продолжать с last-known-good в пределах заданного TTL либо fail-closed для критичных отзывов доступа. Политика задаётся по классу настройки.
Чем config version отличается от feature flag?
Config version описывает полный immutable snapshot. Feature flag выбирает один из утверждённых вариантов для конкретного context или cohort.
Как обнаружить config drift?
Сравнивать desired version/hash с actual version/hash, которые регулярно сообщают все workers, отдельно по регионам и cohorts.
← Все статьи блога