Модуль 13

Senior-практики: архитектура, решения, лидерство

Сеньора отличают решения, которые переживают смену модели, и команда, которая их исполняет без него. Как записывать и защищать архитектурные решения (ADR, design review), выбирать модель и вендора по TCO, держать надёжность недетерминированной системы через SLO и error budget, разбирать AI-инциденты без поиска виноватых, говорить с бизнесом на языке денег и риска и задавать стандарты команды, включая работу с coding agents.

≈ 8 часовGoJava3 визуализациина момент написания: октябрь 2026

System design (модуль 12) отвечает на вопрос «как устроить систему». Здесь — что начинается после доски: как решение записать, провести через ревью, пересмотреть, эксплуатировать и объяснить людям, которые не читают трейсы. Код — калькулятор TCO, error budget и линтер ADR на GoJava — лежит в examples/m13-seniorexamples/java/m13-senior, шаблоны документов — в examples/m13-senior/templates.

Контракт поведения и архитектурное решение

Основные спецификации из модуля SDD фиксируют, что должна делать система; ADR объясняет, как и почему выбран способ реализации. Перед design review проверьте связь требований, дельт и сценариев приёмки.

Архитектурные решения: ADR для AI-систем

ADR (Architecture Decision Record, запись об архитектурном решении) — короткий документ об одном решении. Формат предложил Майкл Найгард в 2011 году: заголовок, контекст (какие силы конфликтуют), решение («мы будем…»), статус (предложено, принято, заменено) и последствия — хорошие, плохие и нейтральные. ADR лежат в репозитории рядом с кодом (обычно docs/adr/0007-….md), нумеруются и не переписываются: если решение поменялось, пишут новый ADR со ссылкой «заменяет ADR-0007», а старый получает статус «заменено». Через год история решений читается как changelog архитектуры.

Для AI-системы пяти разделов мало: решения устаревают быстрее и опираются на доказательства, которые сами устаревают. Модели выводят из эксплуатации — Anthropic, например, предупреждает о выводе публично выпущенной модели минимум за 60 дней (по состоянию на октябрь 2026). Цены сдвигаются, и «self-host, потому что API дорого» однажды становится неверным. «Haiku достаточно» верно только для конкретной версии eval-набора. А модель не падает с 500, она уверенно ошибается. Поэтому в ADR добавляются разделы:

РазделЧто писать в AI-системе
АльтернативыМинимум две реальные, включая «ничего не менять» и «без LLM», с цифрами по тем же метрикам
Eval-доказательстваНабор (имя, версия, n), метрики с доверительными интервалами, критичные кейсы, прогон CI
Стоимость$ за запрос и в месяц сейчас и при ×10, допущения по токенам и кэшу, цены с датой
Данные и комплаенсЧто уходит провайдеру, PII, хранение, регион; что проверяет юрист
Режимы отказаПровайдер недоступен, уверенная ошибка, рост латентности, атака: что видит пользователь, чем ловим
План откатаФлаг или реестр промптов, сколько минут, кто может, когда проверяли. «Откатим» без механизма — это деплой посреди инцидента
ПересмотрДата и триггеры: вывод модели, изменение цены, рост объёма, метрика ниже порога

Когда писать ADR: решение дорого развернуть назад (вендор, где хранятся данные, граница между моделью и кодом), затрагивает другие команды или через полгода вызовет вопрос «почему так?». Правке промпта хватает PR с evals (модуль 3), смене модели в продакшене — нет.

# ADR-NNNN: <решение одной строкой: глагол + объект>

## Статус
Предложено | Принято | Заменено ADR-NNNN. Дата: ГГГГ-ММ-ДД. Владелец, кто утверждает.

## Контекст
Задача и метрика успеха; ограничения (латентность, бюджет, PII, регуляторика); почему решать сейчас.

## Решение
«Мы будем …». Модель, промпт (имя@версия), параметры, граница между моделью и кодом.

## Альтернативы
| Вариант | Качество на eval | $ за запрос | p95 латентности | Почему не выбран |
|---|---|---|---|---|
| Ничего не менять | | | | |
| Без LLM (правила, классический ML) | | | | |
| <реальная альтернатива> | | | | |

## Последствия
Что лучше, что хуже, что сложнее. Lock-in: что переделывать при смене модели или провайдера.

## Eval-доказательства
Набор (имя, версия, n), метрики с интервалами, критичные кейсы, прогон в CI. Где хуже baseline — прямо.

## Стоимость
$ за запрос и в месяц сейчас и при ×10; токены, доля кэша, цены с датой; TCO.

## Данные и комплаенс
Что уходит провайдеру, маскирование PII, хранение логов, регион; вопросы юристу.

## Режимы отказа
Провайдер недоступен, уверенная ошибка, рост латентности, атака: что видит пользователь, чем ловим.

## План отката
Механизм (флаг, реестр), время, кто может, по какому сигналу, когда проверяли.

## Пересмотр
Дата: ГГГГ-ММ-ДД. Досрочно: вывод модели из эксплуатации, цена ±N%, объём ×N, метрика ниже порога.

Пример учебный: компания PayFlow, цифры eval и латентности вымышлены. Цены — Claude по курсу, сентябрь 2026.

ADR-0012: Триаж обращений на Haiku 4.5 с эскалацией на Sonnet 5

Статус

Принято 2026-09-18. Владелец: команда support-ai. Утвердили: техлид поддержки и владелец продукта.

Контекст

Триаж 600 тыс. обращений в месяц на Sonnet 5. Категория должна появляться до открытия карточки оператором: p95 ≤ 1 с, сейчас 1.9 с. Стоимость (≈ $2.6K в месяц) не главный драйвер.

Решение

Мы будем классифицировать обращения на Haiku 4.5 (support_triage@v7) и отправлять на Sonnet 5, если категория рисковая (fraud, legal, dispute) или other, JSON не прошёл схему или предфильтр нашёл маркеры мошенничества. Маршрут выбирает код, а не самооценка модели.

Альтернативы

ВариантТочность (n = 1 200)p95$/месПочему нет
Оставить Sonnet 594.0%1.9 с$2 556Не выполняет требование по латентности
Только Haiku 4.590.5%0.8 с$1 278Провал на fraud и legal (критичные кейсы 141/150)
Fine-tune малой моделине мерили——≈ 5K размеченных примеров и переобучение; при 600K в месяц не окупается
Без LLM: правила по словам71%10 мс≈ 0Остаётся как деградированный режим

Последствия

Два промпта в реестре и два набора регрессий. Эскалированные 15% обращений стали медленнее (два вызова). Lock-in прежний: оба вызова идут через шлюз.

Eval-доказательства

triage-golden@v5, n = 1 200, из них 150 критичных. Роутер 93.6% против 94.0%: Δ −0.4 п.п., 95% bootstrap-интервал [−1.3; +0.5]; критичные 150/150. Shadow на 10% трафика две недели: совпадение с Sonnet 96.8%, эскалаций 15.2%. Здесь же ссылка на прогон CI.

Стоимость

Вход 3 000 токенов (60% из кэша), выход 150: Sonnet 5 — $0.00426 за вызов, Haiku 4.5 — $0.00213. Роутер: 0.85 × 0.00213 + 0.15 × (0.00213 + 0.00426) ≈ $0.0028. В месяц ≈ $1 660 против $2 556.

Данные и комплаенс

Без изменений относительно ADR-0004: номера карт маскируются до вызова модели, провайдер и регион те же.

Режимы отказа

Haiku недоступен — шлюз шлёт всё на Sonnet, p95 возвращается к 1.9 с. Haiku ошибся в fraud-обращении — страхуют предфильтр и рисковые категории; метрика — доля переклассификаций операторами.

План отката

Флаг triage.router=off: весь трафик на Sonnet за < 5 минут без деплоя, может дежурный. Проверено на staging 2026-09-16.

Пересмотр

2027-03-01. Досрочно: объявлен вывод claude-haiku-4-5, эскалаций выше 25% неделю подряд или точность на онлайн-выборке ниже 92%.

В примере решение принято ради латентности, а экономия ≈ $900 в месяц названа побочным эффектом: это снимает на ревью спор «ради $900 усложнили систему». И роутер честно хуже Sonnet по точности: минус с доверительным интервалом убеждает лучше таблицы, где выбранный вариант выигрывает во всех столбцах.

Типичные дефекты ADRАльтернативы-чучела (ревьюер это видит и перестаёт доверять документу); цифры без даты и допущений; eval «с 88 до 91%» на 80 примерах — шум (модуль 8); ADR задним числом; откат без механизма и владельца.

Срок пересмотра работает, только если его проверяют. Дешевле всего — линтер обязательных разделов в CI и ночная джоба, открывающая задачу на каждый ADR с прошедшей датой: функции MissingSections и ReviewBy в examples/m13-senior/adr.gomissingSections и reviewBy в examples/java/m13-senior/…/Adr.java, тест прогоняет их по шаблону выше.

Design review: какие вопросы задавать AI-системе

Design doc описывает систему целиком и ссылается на ADR по ключевым решениям. Структура — как в эссе Мальте Убля «Design Docs at Google»: контекст и масштаб, цели и не-цели, решение, альтернативы, сквозные вопросы (безопасность, приватность, наблюдаемость). Для AI-системы добавляются метрика успеха и критерии запуска, evals и стоимость. Шаблон из examples/m13-senior/templates/design-doc.md:

# Design doc: <название>

Автор · ревьюеры (данные, безопасность, SRE, продукт) · статус · дата · ADR: <ссылки>

## 1. Контекст и масштаб
Кто пользователи, какая боль, что уже есть.

## 2. Цели и не-цели
Что система делает и что сознательно НЕ делает.

## 3. Метрика успеха и критерии запуска
Метрика и способ измерения, baseline без AI, порог запуска, guardrail-метрики, kill-критерий.

## 4. Требования и цифры
Нагрузка и пик, латентность (TTFT, полное время), бюджет, PII, регуляторика.

## 5. Решение
Схема и поток данных, модель на каждом шаге. Права, деньги и необратимые действия — в коде.

## 6. Альтернативы
Минимум две, включая «без LLM»: качество, стоимость, латентность, риски.

## 7. Evals
Набор и критичные кейсы, судья и калибровка, онлайн-метрики, гейты в CI.

## 8. Безопасность и данные
Модель угроз, права инструментов, PII, хранение логов и доступ к ним.

## 9. Надёжность и эксплуатация
SLO и политика error budget, fallback, деградация, runbook, эскалация к людям.

## 10. Стоимость
$ за запрос и в месяц, TCO, рычаги снижения.

## 11. Запуск и откат
Этапы, canary, критерии перехода, откат.

## 12. Открытые вопросы
Что не решено, кто и к какому сроку решает.

Чек-лист ревьюера

Ревью ищет риски, а не проверяет эрудицию автора. Третий столбец — ответы, после которых стоит копать глубже.

ОбластьВопросыКрасный флаг в ответе
Продукт и метрикаКакая метрика говорит, что фича удалась? Baseline без AI? Когда закрываем? Кто владелец и дежурный?«Пользователям понравится»; метрика появится после запуска
ДанныеОткуда знания, как часто меняются? Права доступа к документам? Удаление по запросу?«Положим всё в векторную БД»; ACL не упомянуты
EvalsКакой набор, откуда кейсы, какие критичные? Судья откалиброван? Что блокирует релиз?Набор из 20 придуманных автором вопросов; судья без калибровки; гейта нет
БезопасностьЧто сделает скомпрометированная модель с этими инструментами? Где проверяются права?«В системном промпте запретим»; инструменты с правами сервиса, а не пользователя
Стоимость$ за запрос и в месяц, при ×10? Лимиты на тенанта и на цикл агента?Стоимость не посчитана; агент без лимита шагов
Отказ и деградацияЧто видит пользователь, когда провайдер лёг или модель уверенно ошибается? Как откатываем?«Провайдер надёжный»; откат через деплой
НаблюдаемостьПо трейсу можно объяснить плохой ответ? Версии в каждом вызове? SLO и алерты?Логируем только ошибки; качество в проде не измеряется
КомплаенсPII и регулируемые данные: куда уходят, сколько хранятся? Раскрываем ли, что это ИИ? Кто из юристов смотрел?«Это вопрос юристов, потом»; логи диалогов в общем стеке

Как проводить ревью

Как защищать свой дизайн

Аналогия из JavaСама Java меняется так же: значимое изменение начинается с JEP (JDK Enhancement Proposal), обсуждается публично в рассылках OpenJDK, проходит стадии preview и incubator, и только потом становится постоянной частью языка. Records и pattern matching прошли через несколько preview-релизов, прежде чем стать финальными; StructuredTaskScope в Java 21 всё ещё preview. Ваши решения проще, но цикл тот же: предложение, обсуждение, записанное решение.
Аналогия из GoСам Go меняется так же: значимое изменение начинается с proposal, крупное — с design doc в репозитории golang/proposal, обсуждается публично, и группа ревью выносит решение: принято, отклонено, отложено. Generics прошли через несколько черновиков дизайна, прежде чем попали в Go 1.18. Ваши решения проще, но цикл тот же: предложение, обсуждение, записанное решение.

Build-vs-buy и выбор модели или вендора

На senior-уровне вопрос не «какую модель взять», а какой вариант реализации даёт нужное качество при приемлемых стоимости, латентности и рисках на горизонте года, с учётом людей на эксплуатацию. В книге Chip Huyen «AI Engineering» это раздел «Model Build Versus Buy» (глава 4); ниже — рабочая версия для команды.

КритерийКак измерятьЛовушка
КачествоВаш eval-набор и критичные кейсы, а не публичные бенчмаркиВыбор по лидерборду: на вашей задаче порядок моделей другой
СтоимостьTCO в месяц при текущем объёме и ×10, стоимость на успешную задачуСравнение цены токена с ценой GPU-часа без людей и утилизации
ЛатентностьTTFT и p95 полного ответа под вашей нагрузкой, из вашего регионаЦифры из демо на пустом эндпоинте
Lock-inСтоимость выхода: сколько недель займёт переход на другой вариант при готовом eval-гейтеВидеть lock-in в самих vendor-фичах: prompt caching и batch экономят деньги, их стоит брать
КомплаенсРегион, сертификаты, договор, zero data retention, отраслевые требованияУзнать о требованиях за неделю до запуска
ДанныеЧто уходит наружу, обучаются ли на этом, сколько хранится, можно ли маскировать PIIПутать «нельзя отправлять» (ограничение) и «лучше бы не отправлять» (вес)

Жёсткие ограничения отсекают, веса ранжируют. «Данные не покидают периметр банка» — не вес 5 из 5, а фильтр: нарушающие его варианты не участвуют в сравнении. Если после фильтров не осталось ничего, это результат: требования пересматривают с бизнесом.

Четыре типичных варианта: API frontier (максимум качества, ноль инфраструктуры, самая дорогая цена токена), API дешёвая модель (в разы дешевле и быстрее, хуже на сложном), self-host open-weight (данные в периметре, но GPU, минимум две реплики и люди — модуль 10) и fine-tuned малая модель (на узкой задаче догоняет большие, цена — датасет и переобучение при смене базы).

TCO: что входит кроме токенов

TCO/мес = токены API (запросов × $ за запрос) + инфраструктура (реплик × $/мес, минимум 2 ради отказоустойчивости) + люди (доля инженера × полная стоимость: эксплуатация, on-call, обновления) + evals (прогонов в месяц × (модель + судья)) + разметка (часов × ставка: golden set, калибровка судьи, данные для FT) + миграции (раз в N месяцев × недели работы при смене модели) стоимость на успешную задачу = TCO / (запросов × доля успешных)

Последние две строки обычно забывают: разметку для fine-tuned модели перепроверяют при каждой смене базы, а миграции у API неизбежны. Стоимость на успешную задачу честнее стоимости на запрос: модель, решающая 70% задач, может обойтись дороже модели, решающей 90%, если неуспешные уходят оператору.

Матрица выбора модели и вендора

Данные вариантов условные: порядок величин, а не замеры. Оценки 0–10, 10 — лучше (для lock-in — «легко уйти»); стоимость считается из TCO при выбранном объёме. Под схемой — какие изменения весов и объёма сменят лидера.

На пресете «Банк» включите ограничение по периметру: API-варианты исчезают. Добавьте «качество ≥ 8» — не останется ничего, и это честный ответ: требования противоречат друг другу. Если лидер меняется при сдвиге одного веса на единицу, решение хрупкое, и в ADR стоит записать, на какие веса оно опирается.

Тот же расчёт на Go (цена вызова API — APICost в том же файле,Java (цена вызова API — Tco.apiCost в том же классе, формула из модуля 12):

// Фрагмент Tco.java
    /**
     * Вариант реализации фичи. Все цифры — ваши параметры, а не справочник.
     *
     * @param perRequest  $ переменной части: токены API (0 для self-host)
     * @param unitMonthly $ в месяц за реплику self-host: GPU, хостинг (0 для API)
     * @param unitCap     запросов в месяц на реплику при целевой утилизации
     * @param minUnits    минимум реплик ради отказоустойчивости
     * @param opsFte      доля инженера: эксплуатация, on-call, обновления, evals, разметка
     * @param successRate доля успешно решённых задач на вашем eval-наборе
     */
    public record Option(String name, double perRequest, double unitMonthly, double unitCap, int minUnits,
                         double opsFte, double successRate) {
        public static Option api(String name, double perRequest, double opsFte, double successRate) {
            return new Option(name, perRequest, 0, 0, 0, opsFte, successRate);
        }

        public static Option selfHost(String name, double unitMonthly, double unitCap, int minUnits,
                                      double opsFte, double successRate) {
            return new Option(name, 0, unitMonthly, unitCap, minUnits, opsFte, successRate);
        }
    }

    /** Полная стоимость владения в месяц и её структура. */
    public record Result(double variable, double infra, double people, double total, int units,
                         double perRequest, double perSuccess) {}

    /**
     * TCO при объёме requests в месяц; fteMonthly — полная стоимость инженера в месяц.
     * Упрощение: evals и разметка сидят в opsFte. Выделите их отдельной строкой, если они заметны в бюджете.
     */
    public static Result monthly(Option o, double requests, double fteMonthly) {
        double variable = o.perRequest() * requests, people = o.opsFte() * fteMonthly, infra = 0;
        int units = 0;
        if (o.unitCap() > 0) {
            units = Math.max(o.minUnits(), (int) Math.ceil(requests / o.unitCap()));
            infra = units * o.unitMonthly();
        }
        double total = variable + infra + people, perRequest = 0, perSuccess = 0;
        if (requests > 0) {
            perRequest = total / requests;
            if (o.successRate() > 0) perSuccess = total / (requests * o.successRate());
        }
        return new Result(variable, infra, people, total, units, perRequest, perSuccess);
    }
}
// Option — вариант реализации фичи. Все цифры — ваши параметры, а не справочник.
type Option struct {
	Name        string
	PerRequest  float64 // $ переменной части: токены API (0 для self-host)
	UnitMonthly float64 // $ в месяц за реплику self-host: GPU, хостинг (0 для API)
	UnitCap     float64 // запросов в месяц на реплику при целевой утилизации
	MinUnits    int     // минимум реплик ради отказоустойчивости
	OpsFTE      float64 // доля инженера: эксплуатация, on-call, обновления, evals, разметка
	SuccessRate float64 // доля успешно решённых задач на вашем eval-наборе
}

// TCO — полная стоимость владения в месяц и её структура.
type TCO struct {
	Variable, Infra, People, Total float64
	Units                          int
	PerRequest, PerSuccess         float64
}

// MonthlyTCO считает TCO при объёме requests в месяц; fteMonthly — полная стоимость инженера в месяц.
// Упрощение: evals и разметка сидят в OpsFTE. Выделите их отдельной строкой, если они заметны в бюджете.
func MonthlyTCO(o Option, requests, fteMonthly float64) TCO {
	t := TCO{Variable: o.PerRequest * requests, People: o.OpsFTE * fteMonthly}
	if o.UnitCap > 0 {
		t.Units = max(o.MinUnits, int(math.Ceil(requests/o.UnitCap)))
		t.Infra = float64(t.Units) * o.UnitMonthly
	}
	t.Total = t.Variable + t.Infra + t.People
	if requests > 0 {
		t.PerRequest = t.Total / requests
		if o.SuccessRate > 0 {
			t.PerSuccess = t.Total / (requests * o.SuccessRate)
		}
	}
	return t
}

Как не проиграть на смене модели

Выбранная сегодня модель через год будет либо выведена из эксплуатации, либо проиграет новой. Выигрывает тот, кто меняет её за дни, а не за квартал.

Аналогия из JavaПринцип разделения интерфейсов (ISP) здесь работает буквально. Не оборачивайте весь SDK провайдера в свой «универсальный LLM-клиент» на 40 методов: он повторит API одного вендора и не налезет на другого. Объявите в пакете фичи маленький интерфейс из одного-двух методов (interface Classifier { Category classify(Ticket t); }) и сделайте по реализации на вариант — отдельный Spring-бин, выбираемый конфигом. Смена модели — новая реализация, прошедшая тот же eval-тест.
Аналогия из GoGo-идиома «интерфейсы объявляет потребитель» здесь работает буквально. Не оборачивайте весь SDK провайдера в свой «универсальный LLM-клиент» на 40 методов: он повторит API одного вендора и не налезет на другого. Объявите в пакете фичи маленький интерфейс из одного-двух методов (type Classifier interface { Classify(ctx context.Context, t Ticket) (Category, error) }) и сделайте по реализации на вариант. Смена модели — новая реализация, прошедшая тот же eval-тест.

SLO, error budget и on-call для недетерминированных систем

Термины из книги Google SRE. SLI — доля хороших событий: ответов без ошибки, быстрее порога, признанных судьёй верными. SLO — цель для SLI за скользящее окно, например 98% за 28 дней. Error budget — допустимая доля плохих событий, 1 − SLO. Бюджет превращает спор «релизить или чинить» в арифметику: бюджет есть — команда меняет систему, кончился — чинит надёжность. Техническая сторона (SLI по трейсам, burn-rate алерты в Prometheus) — в модуле 9 (SLO в продакшене), здесь — что из этого следует для команды.

Что считать плохим событием

У LLM-системы ответ может прийти за секунду с кодом 200 и быть неверным, поэтому SLI несколько (латентность — как обычно, по трейсам):

SLIХорошее событиеИсточникНюанс
ДоступностьОтвет без ошибки и таймаутаШлюз, трейсыРешите заранее, считается ли хорошим ответ fallback-модели и режим «только ссылки»; долю деградации — отдельной метрикой
КачествоОнлайн-судья не пометил ответ как неверный или небезопасныйВыборка 1–5% ответов (модуль 8, онлайн-оценка)Только выборка и с задержкой; у судьи своя ошибка (калибровка)
Прокси качестваНет эскалации и повторного обращения за 24 чАналитикаЗапаздывает; может улучшаться при опасной регрессии (постмортем ниже)
Безопасность—Guardrails, аудитНе бюджетируется: утечка PII, успешная инъекция, необратимое действие без подтверждения — всегда инцидент с постмортемом, даже при выполненном SLO

Пример: 50 000 ответов в сутки, SLO качества 98% за 28 дней — 1.4 млн ответов, бюджет 28 000 плохих. Burn rate — во сколько раз быстрее нормы сжигается бюджет: при 1% плохих он 0.5, при 2% — 1 (бюджет кончится в последний день), при 30% — 15 (меньше двух суток).

Алерты строят по скорости сжигания на двух окнах сразу. SRE Workbook рекомендует для 30-дневного окна: 2% бюджета за 1 час (burn rate 14.4, будить), 5% за 6 часов (6, будить), 10% за 3 дня (1, тикет); короткое окно (5 мин, 30 мин, 6 ч) подтверждает, что сжигание идёт сейчас, и гасит алерт после инцидента. Порог = доля бюджета × окно SLO ÷ длинное окно; для 28 дней — 13.44, 5.6 и 0.93.

Качество видно только на выборкеПри 50 000 ответов в сутки и выборке судьи 2% за час набирается около 40 оценок: разница между 2% и 5% плохих — одна оценка против двух, это шум. Быстрые окна ловят по качеству только грубые поломки, тонкие регрессии видны на окнах 6 часов и 3 дня. Поэтому технические SLI будят быстро, а для качества нужны дополнительные быстрые сигналы: срабатывания guardrails, эскалации, жалобы операторов.

Error budget за 28 дней: фон, инцидент и политика релизов

Плохой ответ — ошибка или ответ, который онлайн-судья счёл неверным. Сверху — остаток бюджета, снизу — burn rate по часам. Алерты по часовым данным (короткие окна не моделируются); для инцидента качества алерт требует ≥ 30 оценок судьи в окне.

Политика error budget

Бюджет без политики — просто график. Политику заранее подписывают продукт, разработка и дежурные. В примере из SRE Workbook: бюджет за четыре недели превышен — релизы, кроме исправлений P0 и безопасности, останавливаются до возвращения в SLO; инцидент съел больше 20% бюджета — постмортем хотя бы с одним действием P0; споры о расчёте решает CTO. Для AI-фичи релиз — любое изменение поведения: промпт, модель, параметры, индекс.

Остаток бюджетаЧто разрешеноКто решает
> 50%Обычные релизы через eval-гейт, эксперименты (A/B дешёвой модели)Команда
0–50%Релизы только с полным прогоном evals, canary медленнее, без экспериментовТехлид
< 0Заморозка: только откаты, фиксы надёжности и безопасности, кейсы в golden setПо политике
Инцидент > 20% бюджетаПостмортем с P0-действием, независимо от общего остаткаПо политике

Политика как код — фрагмент examples/m13-senior/budget.go (там же FromSample:examples/java/m13-senior/…/Budget.java (там же Window.fromSample: перенос доли плохих по выборке судьи на весь трафик):

// Фрагмент Budget.java
/**
 * Правило multiwindow multi-burn-rate из Google SRE Workbook («Alerting on SLOs»):
 * за окно long сожжена доля budget бюджета, и сжигание всё ещё идёт на коротком окне shortW.
 *
 * @param budget доля бюджета всего окна SLO, например 0.02
 * @param page   true — будить дежурного, false — тикет в рабочее время
 */
public record Alert(String name, Duration longW, Duration shortW, double budget, boolean page) {}

/** Рекомендованные пороги SRE Workbook (таблица 5-8), от срочного к медленному. */
public static final List<Alert> DEFAULT_ALERTS = List.of(
        new Alert("page-fast", Duration.ofHours(1), Duration.ofMinutes(5), 0.02, true),
        new Alert("page-slow", Duration.ofHours(6), Duration.ofMinutes(30), 0.05, true),
        new Alert("ticket", Duration.ofHours(72), Duration.ofHours(6), 0.10, false));

/** События за последние d — обёртка над вашим Prometheus или ClickHouse. */
public interface Metrics extends Function<Duration, Window> {}

/**
 * Что говорит политика error budget прямо сейчас.
 *
 * @param budgetLeft доля оставшегося бюджета окна, бывает &lt; 0
 * @param fired      первый сработавший алерт, null — тихо
 * @param freeze     бюджет исчерпан: релизы промптов и моделей заморожены, кроме фиксов
 */
public record Status(double budgetLeft, Alert fired, boolean freeze) {}

/** Цель по доле хороших событий за скользящее окно, например 0.99 за 28 суток. */
public record Slo(double target, Duration period) {
    /** Во сколько раз быстрее нормы сжигается бюджет; 1 — ровно к концу окна. */
    public double burnRate(Window w) {
        return w.total() == 0 ? 0 : w.bad() / w.total() / (1 - target);
    }

    /** Доля бюджета → порог burn rate: 2% за 1 ч при окне 30 суток → 14.4. */
    public double threshold(Alert a) {
        return a.budget() * period.toNanos() / a.longW().toNanos();
    }

    /** Применяет алерты и политику заморозки к текущим метрикам. */
    public Status check(Metrics m, List<Alert> alerts) {
        double left = 1;
        Window w = m.apply(period);
        if (w.total() > 0) left = 1 - w.bad() / ((1 - target) * w.total());
        for (Alert a : alerts) {
            double th = threshold(a);
            if (burnRate(m.apply(a.longW())) >= th && burnRate(m.apply(a.shortW())) >= th) {
                return new Status(left, a, left <= 0);
            }
        }
        return new Status(left, null, left <= 0);
    }
}
// Alert — правило multiwindow multi-burn-rate из Google SRE Workbook («Alerting on SLOs»):
// за окно Long сожжена доля Budget бюджета, и сжигание всё ещё идёт на коротком окне Short.
type Alert struct {
	Name        string
	Long, Short time.Duration
	Budget      float64 // доля бюджета всего окна SLO, например 0.02
	Page        bool    // true — будить дежурного, false — тикет в рабочее время
}

// Threshold переводит долю бюджета в порог burn rate: 2% за 1 ч при окне 30 суток → 14.4.
func (s SLO) Threshold(a Alert) float64 { return a.Budget * float64(s.Period) / float64(a.Long) }

// DefaultAlerts — рекомендованные пороги SRE Workbook (таблица 5-8), от срочного к медленному.
var DefaultAlerts = []Alert{
	{"page-fast", time.Hour, 5 * time.Minute, 0.02, true},
	{"page-slow", 6 * time.Hour, 30 * time.Minute, 0.05, true},
	{"ticket", 72 * time.Hour, 6 * time.Hour, 0.10, false},
}

// Metrics отдаёт события за последние d — обёртка над вашим Prometheus или ClickHouse.
type Metrics func(d time.Duration) Window

// Status — что говорит политика error budget прямо сейчас.
type Status struct {
	BudgetLeft float64 // доля оставшегося бюджета окна, бывает < 0
	Fired      *Alert  // первый сработавший алерт, nil — тихо
	Freeze     bool    // бюджет исчерпан: релизы промптов и моделей заморожены, кроме фиксов
}

// Check применяет алерты и политику заморозки к текущим метрикам.
func (s SLO) Check(m Metrics, alerts []Alert) Status {
	st := Status{BudgetLeft: 1}
	if w := m(s.Period); w.Total > 0 {
		st.BudgetLeft = 1 - w.Bad/((1-s.Target)*w.Total)
	}
	st.Freeze = st.BudgetLeft <= 0
	for i, a := range alerts {
		th := s.Threshold(a)
		if s.BurnRate(m(a.Long)) >= th && s.BurnRate(m(a.Short)) >= th {
			st.Fired = &alerts[i]
			break
		}
	}
	return st
}

On-call и runbook

У дежурства по AI-фиче три особенности: сигнал о качестве приходит медленно и часто от людей; главный рычаг — откат версии промпта или модели в реестре; живой fallback — операторы с ограниченной ёмкостью. Runbook — до первого инцидента:

СигналПервые 15 минутДальшеЭскалация
Ошибки провайдера, breaker открытПроверить fallback и статус провайдераДольше 30 минут — деградированный режим, предупредить поддержкуВладелец шлюза
Burn rate качества выше порогаЧто менялось за 48 часов? Откат последнего изменения флагомРелизов не было — дрейф входа или данных (модуль 9), разбор трейсовВладелец фичи и eval-набора
Утечка PII, успешная инъекция, опасное действиеSEV-1: выключить фичу или инструмент kill switch'ем, сохранить трейсыОценка масштаба, уведомления по срокам закона (модуль 11)Безопасность, DPO, юристы — сразу

Постмортем AI-инцидента

Постмортем — письменный разбор инцидента: влияние, сдерживание, причины и меры против повторения. Культура blameless из книги Google SRE: разбор ищет условия, в которых ошибка стала возможной, исходит из добросовестности участников и не обвиняет людей или команды. Это не «ничего не делаем»: результат — изменения в системе и процессе с владельцами и сроками. Если причина звучит как «инженер не проверил», настоящий вопрос — почему система позволила выкатить непроверенное.

Когда писать: видимый пользователям простой или деградация сверх порога, потеря данных, ручное вмешательство дежурного, слишком долгое устранение, инцидент, найденный людьми, а не мониторингом. Для AI-систем добавьте любой инцидент безопасности или PII, любое обязательство, данное системой от имени компании, и регрессию качества, которую первыми заметили пользователи.

Главное отличие AI-постмортема — обязательный вопрос «почему evals и гейты не поймали»: корневая причина чаще в пробеле набора или метрики, чем в строке кода.

# Постмортем: <короткое название инцидента>

Статус · SEV-N · начало и конец · авторы · blameless: ищем условия, а не виноватых.

## Кратко
3–5 предложений: что случилось, влияние, причина, что меняем.

## Влияние
Пользователи, диалоги, деньги, регуляторика. Как посчитано и с какой погрешностью.

## Таймлайн (UTC)
| Время | Событие | Источник (трейс, алерт, релиз) |
|---|---|---|

## Обнаружение
Как узнали, сколько прошло с начала, почему не раньше.

## Корневая причина и способствующие факторы

## Почему evals и гейты не поймали
Каких кейсов не было, какой проверки не было в CI, какая онлайн-метрика молчала.

## Что сработало и где повезло

## Действия
| Действие | Тип: предотвратить / обнаружить / смягчить | Владелец | Срок |
|---|---|---|---|
Обязательно: кейсы в golden set, гейт или метрика canary, guardrail, правка runbook.

Разбор: бот поддержки обещает компенсации

Кейс вымышленныйPayFlow, версии, время и цифры придуманы. Но риск реальный: в деле Moffatt v. Air Canada (2024) трибунал Британской Колумбии признал авиакомпанию ответственной за неверную информацию о возврате денег от её чат-бота и отклонил довод, что бот — отдельная сущность. В другой юрисдикции последствия оценивает юрист.

Кратко. Релиз support_reply@v12 вместе с переходом с Opus 5 на более новую и дешёвую Opus 5.5 привёл к тому, что бот обещал клиентам вернуть комиссии и зачислить компенсации, не имея на это полномочий. Заметили операторы; до дежурного сигнал дошёл через 31 час после выкатки на 100%, откатили через 22 минуты.

Время (UTC)СобытиеИсточник
Вт 14:00Смержен support_reply@v12: инструкция «прояви эмпатию и предложи конкретное решение», модель Opus 5.5. CI: 92.1% → 93.4%, критичные 40/40. Canary 5%CI, реестр
Ср 10:00Метрики canary: 👎 −0.4 п.п., эскалации −2.1 п.п., стоимость −17%. Выкатка на 100%Дашборд canary
Чт 11:20Оператор в чате поддержки: третий клиент за утро ссылается на «обещание бота». До дежурного не доходитЧат
Чт 16:40Руководитель смены поддержки эскалирует дежурному support-aiЗвонок
Чт 16:55–17:02В трейсах «вернём вам комиссию», «зачислим 500 ₽». SEV-2; флаг в реестре: v11 на Opus 5Трейсы, реестр
Чт 18:30Классификатор обязательств по 51 000 диалогам на v12: 1 380 с обещаниями (2.7%)Офлайн-джоба

Влияние. ≈ 1 380 клиентов получили обещание денег, ≈ 940 обратились за выполнением; обещания до установленной суммы решили выполнить. Около 120 часов работы операторов.

Корневая причина. Система позволяла модели давать денежные обязательства текстом. Запрет существовал только как инструкция в промпте, и ни одна проверка не измеряла это свойство. Способствующие факторы: новая инструкция конфликтовала со старым запретом, и новая модель разрешала конфликт в пользу «помочь»; модель и промпт поменяли в одном релизе; у сигнала операторов не было пути к дежурному.

Почему evals не поймали.

Сработало: откат флагом без деплоя; версия промпта в трейсах дала точный подсчёт влияния. Повезло: суммы были небольшими.

ДействиеТипВладелецСрок
60 многоходовых кейсов «давление на компенсацию» как критичные; v12 их проваливаетпредотвратитьвладелец eval-набора1 неделя
Критерий судьи «обязательства вне полномочий», калибровка на 100 размеченных диалогахобнаружитьML-инженер2 недели
Guardrail: классификатор обязательств на выходе, при срабатывании — операторсмягчитьsupport-ai1 неделя
Компенсации только через инструмент offer_compensation с лимитами; слова о деньгах — только после tool_result (ADR-0019)предотвратитьтехлид1 месяц
Метрика canary «доля ответов с обязательствами» (порог +0.1 п.п.); правило: модель и промпт не меняются в одном релизеобнаружитьSRE, техлид1 неделя
Кнопка «бот ответил не то» у оператора с алертом дежурному; владелец политики поддержки — в CODEOWNERS prompts/support_*обнаружить, предотвратитьпродукт поддержки2 недели

В действиях есть все три типа: предотвращение (кейсы, инструмент вместо текста), обнаружение (судья, метрика, кнопка) и смягчение (guardrail). Постмортем только с «улучшить промпт» не дошёл до системных причин: следующая версия найдёт новый способ пообещать.

Второй типичный сценарий: семантический кэшКлиент спрашивает «какой у меня лимит по карте?» и получает лимит и последние цифры карты другого клиента: семантический кэш (модуль 9) хранил ответы по эмбеддингу вопроса без учёта пользователя. Это инцидент безопасности: kill switch кэша, оценка масштаба, уведомления по требованиям закона (модуль 11). Действия: персонализированные ответы не кэшируются, ключ включает область видимости, тест в CI с двумя пользователями и одинаковым вопросом.

Встреча: ведущий — не участник инцидента; таймлайн по трейсам, потом причины, потом действия; язык «система позволила», а не «Петя выкатил»; через две недели — проверка действий.

Коммуникация: метрики evals на языке бизнеса

Руководителю нужны не κ и p95 TTFT, а ответы на четыре вопроса: сколько задач система решает, сколько это стоит, чем рискуем и что даст следующее вложение.

Инженерная метрикаВопрос бизнесаКак сказать
Точность 92% на golden set, n = 400«Насколько ему можно доверять?»«На 400 реальных обращениях: 92 из 100 правильно, 5 передаёт оператору, в 3 ошибается — вот примеры»
$0.012 за запрос«Сколько стоит?»«Решённое ботом обращение — $0.6 с учётом команды, у оператора — $4»
Δ −0.4 п.п., интервал [−1.3; +0.5]«Стало хуже?»«Разница в пределах погрешности; чтобы различить 1 п.п., нужно ещё N кейсов»

Правила: всегда знаменатель, примеры ошибок, деньги рядом с риском, диапазон там, где есть неопределённость. И точность на golden set — не доля решённых обращений: между ними вопросы, на которые бот не отвечает вовсе, и клиенты, не поверившие правильному ответу.

Договориться о метрике до разработки

Самый дорогой провал — запуск, после которого никто не может сказать, удался ли он. Метрика успеха согласуется до первой строки кода, как контракт. Пример для бота PayFlow (вымышленный):

ПунктЗначение
Целевая метрикаДоля обращений, закрытых без оператора и без повторного обращения за 7 дней
Как измеряемПродуктовая аналитика; ручная проверка 200 диалогов в неделю
BaselineВсё обрабатывают операторы, поиск по FAQ закрывает около 10%
Порог запуска на 100%≥ 40% на canary в течение двух недель
Guardrail-метрикиCSAT не ниже базового более чем на 2 п.п.; уверенных ошибок ≤ 1%; обязательств вне политики — 0; стоимость ≤ $0.05 за диалог
Kill-критерийЧерез 8 недель доля решённых ниже 25% — сворачиваем до поиска по FAQ

Контракт подписывают продукт, техлид и руководитель операторов. Kill-критерий делает проект безопасным для заказчика: известна максимальная цена неудачи. Guardrail-метрики защищают от оптимизации любой ценой: бот, обещающий компенсации, отлично закрывает обращения.

От метрик evals к деньгам и риску

Доля решённых — онлайн-метрика, уверенные ошибки — из разметки и судьи. LLM: 4 вызова на диалог по 3 000 токенов входа и 300 выхода без кэша, цены сентября 2026. Прочее условно — подставьте свои цифры.

Схема показывает маржинальную цену улучшений: если ошибка дорогая, −0.1 п.п. ошибок ценнее +1 п.п. автоматизации, и роадмап должен это отражать. На планировании это сильнее, чем «качество стало выше».

Как сказать «здесь LLM не нужна»

Иногда лучший вклад сеньора — убедить не делать AI-часть. Когда стоит предложить альтернативу:

СигналПочему LLM проигрываетЧто вместо
Есть детерминированное правило или справочникПравило бесплатно, мгновенно и всегда одинаковоРегулярки, SQL, таблица решений
Нужна точность расчёта: деньги, проценты, юридически значимые числаМодель не калькулятор, аудитор спросит, как получено числоКод считает, LLM максимум объясняет
Латентность в десятки миллисекунд, огромный объём простых решенийДаже малая модель — сотни миллисекундКлассический классификатор на эмбеддингах или признаках
Нечем мерить качество: нет данных и эксперта для разметкиНеизвестно, работает ли системаСначала данные и разметка

Как сказать: не «нет», а «вот что даст результат быстрее и дешевле», с цифрами и гибридом. «Правила по 30 шаблонам закрывают 80% таких обращений за неделю работы и бесплатно в эксплуатации. Для оставшихся 20% LLM имеет смысл — вернёмся к ней, когда соберём 300 размеченных примеров для evals». Тот же вопрос в формате собеседования — в модуле 14 (вопросы с собеседований).

Команда: менторство, ревью промптов и evals, стандарты

Сеньор масштабируется через других: стандарты, которые не нужно помнить, ревью, которое учит, и людей, которые через год пишут ADR без него. В AI-команде к обычным стандартам добавляются промпты, eval-наборы и данные — артефакты, которые по привычке живут вне процессов.

СтандартМинимумЧем обеспечивается
Промпты в репозиторииprompts/*.tmpl через embedprompts/*.txt в ресурсах classpath (src/main/resources), владелец и changelog (модуль 3)CODEOWNERS на prompts/
Версии в трейсахИмя, версия и хэш промпта, модель и параметры в каждом трейсе и рядом с результатомАлерт на вызовы без prompt.version
Evals в CISmoke на каждый PR в промптах, eval-наборах, конфиге моделей; полный набор ночьюОбязательная проверка (модуль 8)
Политика данныхКакие данные каким провайдерам, маскирование PII, сроки логовПроверка в шлюзе (модуль 11)
ADR и владениеADR для смены модели, вендора, хранения данных; у каждой фичи владелец, SLO, runbookЛинтер ADR в CI; без runbook фича не выходит из беты
Аналогия из JavaФорматтеры вроде Spotless с google-java-format побеждают потому, что не обсуждаются: spotless:check в сборке, CI красный. Стандарты AI-команды стоит строить так же: правило «промпт только с evals», которое проверяют глазами, нарушат в пятницу вечером, а обязательную проверку в CI на изменения в prompts/ — нет.
Аналогия из Gogofmt победил потому, что не обсуждается: инструмент один, настроек нет, CI красный. Стандарты AI-команды стоит строить так же: правило «промпт только с evals», которое проверяют глазами, нарушат в пятницу вечером, а обязательную проверку в CI на изменения в prompts/ — нет.

Ревью PR с изменением промпта

Менторство и онбординг

Coding agents как командная дисциплина

Инструментальная часть — AGENTS.md и CLAUDE.md, режимы разрешений, sandbox, хуки, рабочий цикл «план → тесты → реализация → ревью диффа» — в модуле 4 (coding agents). Здесь — правила для команды, где агенты пишут заметную часть кода. Без них получается либо запрет, который тихо нарушают, либо «каждый как хочет» и код, который через полгода никто не понимает.

ЗонаПравило командыЧем обеспечивается
ОтветственностьЗа код отвечает автор PR, кто бы его ни набрал. «Это сгенерировал агент» не аргумент ни на ревью, ни в постмортемеКультура ревью; пометка в PR об агенте — для фокуса ревьюера, не для наказания
Тесты прежде кодаСначала падающий тест; агент не ослабляет и не удаляет тесты; эталон evals меняет человек отдельным PRФайл инструкций, Stop-хук и отдельное ревью диффа тестов (модуль 4)
РевьюПланка та же, внимание другое: правдоподобный, но неверный код, выдуманные API, лишние зависимости, тихо изменённые проверки. Большие диффы дробятсяЧек-лист ревью, лимит размера PR
Права и секретыПродовых секретов в окружении агента нет вообще; деплой и доступ к проду — никогда; автономные режимы — только в изоляцииНастройки разрешений в репозитории, управляемые настройки организации, sandbox или контейнер
Инструкции репозиторияФайл инструкций — стандарт команды и ревьюится как код; это контекст для модели, а не принуждениеCODEOWNERS на файл; всё обязательное дублируется в CI
Где не использовать без экспертаАвторизация, криптография, миграции данных, обработка PII; домен, в котором вы не можете проверить результатCODEOWNERS на чувствительные пакеты, метка «нужен ревью эксперта»

Узкие права нужны ещё и потому, что агент читает недоверенный контент — issues, README зависимостей, чужой код. Это непрямая инъекция (модуль 7), и lethal trifecta собирается легко: секреты в окружении, недоверенный текст, сеть. Разрывать её проще всего на секретах и сети.

Что мерить. Не «долю кода от агента» — она поощряет объём. Отчёт DORA 2024 связал рост использования AI с ростом индивидуальной продуктивности, но со снижением стабильности и пропускной способности поставки. Смотрите на метрики поставки: время до прода, долю изменений с инцидентами, время восстановления, размер PR. Выросли PR и откаты — правила ужесточают, даже если все довольны.

Практика

Задание 1: design doc с ADR для «AI-поддержки банка» и его защита

Спроектируйте чат-ассистента поддержки условного розничного банка и защитите дизайн. Этот design doc — заготовка для защиты капстоуна (модуль 15): там те же документы пишутся для вашего агента из сквозных проектов, а здесь ограничения чужие, и «просто взять самую сильную модель» не выйдет.

ПараметрОграничение
Объём400 тыс. диалогов в месяц, в среднем 5 сообщений клиента; пик ×6 в дни зарплат
СценарииТарифы (≈ 3 000 статей); статус карт и операций через API банка от имени клиента; блокировка карты с подтверждением; споры — оператору
Бюджет≤ $0.03 на диалог на модели и инфраструктуру; команда — 3 инженера
ЛатентностьTTFT p95 ≤ 2 с, полный ответ ≤ 8 с
PIIФИО, карты, счета, паспорт, операции; номер карты целиком — ни провайдеру, ни в логи
РегуляторикаБанковская тайна (в РФ — ст. 26 закона «О банках и банковской деятельности»), 152-ФЗ или GDPR, раскрытие ИИ (EU AI Act, ст. 50 — модуль 11), в ЕС — DORA. Применимость определяет юрист; от вас — инженерные последствия и вопросы к нему
Вне скоупаРешения о кредитах (в EU AI Act оценка кредитоспособности — высокорисковая система, Annex III), обещания ставок и компенсаций

Что сдать: design doc по шаблону (до 8 страниц); три ADR, проходящих линтер из examples/m13-seniorexamples/java/m13-senior (модель или вендор с матрицей; где живут и как маскируются PII; эскалация и подтверждение действий); стоимость и TCO; SLO и политику error budget; модель угроз (модуль 7). Защита: 15 минут рассказа и 30 минут вопросов. Ревьюер — коллега или LLM, которой дали эту рубрику и попросили спрашивать, а не помогать.

Критерий0 — не принято1 — принято2 — сильно
1. Требования и метрикаНет цифр или метрикиЦифры, метрика, baselineПлюс guardrail-метрики и kill-критерий
2. Граница модели и кодаМодель решает о правах и деньгахПрава и необратимые действия — в кодеПлюс деградированный режим, поток PII на схеме
3. Альтернативы и ADRАльтернативы-чучелаТри ADR, линтер зелёныйПлюс честные минусы, триггеры пересмотра
4. Evals и гейтыНет набора или гейтаGolden set ≥ 200, критичные кейсы, гейт в CIПлюс многоходовые и атакующие кейсы, калибровка судьи, canary
5. Безопасность и PIIPII уходит провайдеру без анализаМаскирование, инструменты от имени клиента, модель угрозПлюс набор атак, kill switch
6. РегуляторикаНе упомянутаТребования и инженерные последствияПлюс сроки хранения логов, вопросы юристу
7. Стоимость и TCOНе посчитана$ за диалог в бюджетеПлюс TCO с людьми, ×10, стоимость на решённое обращение
8. НадёжностьНе описанаSLO, fallback, откатПлюс политика error budget, runbook, ёмкость операторов
9. ЗащитаСпорит без данныхОтвечает по существу, «не знаю» — с планомПлюс меняет дизайн по сильным замечаниям и фиксирует в ADR

Порог: не меньше 12 из 18 и ни одного нуля по критериям 4–6; senior — от 15. Вопросы, которые стоит ждать: клиент вставил номер карты целиком — где этот текст окажется? Кто проверяет право клиента видеть операцию — модель или код? Провайдер лёг на два часа в день зарплаты — что видит клиент и выдержат ли операторы? При каком объёме или требовании вы перейдёте на self-host? Как через месяц вы узнаете, что качество просело? Юрист запретил отправлять даже маскированные диалоги наружу — что из дизайна переживёт это?

Задание 2: постмортем на сбой своего проекта

Возьмите реальный сбой сквозного проекта (A–D) или вызовите учебный: другая модель эмбеддингов в A, смена модели без смены промпта в B, таймаут MCP-инструмента в C, упавший провайдер в D. Постмортем — по шаблону.

Задание 3: стандарт команды для своего репозитория

Оформите репозиторий сквозных проектов так, будто завтра в него придут три инженера и coding agent.

Самопроверка

Чем ADR отличается от design doc?

Design doc описывает систему целиком. ADR фиксирует одно решение с альтернативами и последствиями, не переписывается, а заменяется новым ADR. Design doc ссылается на ADR по ключевым решениям.

Зачем в ADR для AI-системы срок пересмотра?

Решение опирается на модель, цены и eval-набор, которые меняются: модели выводят из эксплуатации, цены сдвигаются, объём растёт. Без даты и триггеров решение живёт, пока не сломается или пока провайдер не отключит модель.

Чем жёсткое ограничение отличается от веса в матрице выбора?

Ограничение отсекает варианты, и они не участвуют в сравнении. Вес лишь ранжирует. Записав «данные не покидают периметр» весом 5, можно получить победу API-варианта по сумме — и неверное решение.

SLO 98%, 1.4 млн ответов за окно, доля плохих 3%. Каков бюджет и burn rate?

Бюджет — 28 000 плохих ответов. Burn rate = 3% ÷ 2% = 1.5: бюджет кончится через 28 ÷ 1.5 ≈ 19 дней.

Почему SLI качества по онлайн-судье плохо подходит для быстрых алертов?

За час судья выдаёт десятки оценок: умеренную регрессию не отличить от шума, плюс задержка и ошибка судьи. Нужны быстрые прокси: guardrails, эскалации, кнопка оператора.

Какие события нельзя «списывать» на error budget?

Утечки PII и чужих данных, успешные инъекции с действием, необратимые действия без подтверждения, опасные ответы. Каждое — инцидент с постмортемом, даже при выполненном SLO.

Почему blameless — не «никто не виноват»?

Разбор ищет условия, в которых ошибка стала возможной, а результат — изменения в системе и процессе с владельцами и сроками. Вопрос не «кто выкатил», а «почему система позволила выкатить без проверки».

Почему метрики canary могут улучшиться при опасной регрессии?

Прокси (👎, эскалации, CSAT) отражают удовольствие клиента, а некоторые отказы ему приятны: бот, обещающий компенсации, реже получает негатив. Нужны guardrail-метрики, которые прямо измеряют опасное свойство.

Почему файл инструкций для coding agent — не механизм безопасности?

Это контекст для модели: обычно ему следуют, но не гарантированно, и недоверенный текст может его перебить. Запреты обеспечивают права агента, хуки, sandbox и отсутствие секретов в окружении, а качество — CI и ревью.

Вопросы с собеседований

Тренировка senior-вопросов — модуль 14.

Расскажите о сложном архитектурном решении в LLM-системе, которое вам пришлось защищать.

Структура как в рассказе о проекте (модуль 14), но вокруг решения: контекст и ограничения с цифрами; две-три альтернативы и как их сравнивали (eval-набор, стоимость, латентность); кто был против и какие аргументы сработали — данные, прототип, расчёт TCO, а не статус; что записали в ADR, включая минусы и условия пересмотра; метрики после запуска. Сильный ответ содержит момент, где вы изменили решение под возражением или признали ошибку.

На что смотрит интервьюер: решения по данным и записанные; влияние без полномочий; видите ли минусы своего решения; есть ли «я», а не только «мы».

Вышла новая модель: на 40% дешевле и по бенчмаркам лучше текущей. Ваши действия?

Бенчмарк — повод проверить. Прогон golden set по сегментам и критичным кейсам с текущим и адаптированным промптом, с доверительными интервалами. Стоимость на реальном трафике: дешевле за токен не значит дешевле за задачу, если модель многословнее. Затем shadow, canary с guardrail-метриками, без других изменений в том же релизе. Решение — новый ADR, заменяющий старый. С абстракцией и eval-гейтом это неделя; без них главный вывод — построить их.

На что смотрит интервьюер: не верите ли бенчмаркам на слово; одна переменная за раз; инфраструктура для миграций вместо героизма.

Как определить SLO для чат-бота, если ответы недетерминированы?

Несколько SLI: доступность и латентность по трейсам, качество — доля ответов, не помеченных онлайн-судьёй на выборке, плюс прокси вроде эскалаций. Цели — от baseline и цены ошибки, вместе с продуктом. Burn-rate алерты на нескольких окнах, для качества — медленные: выборка мала. Безопасность вне бюджета. И заранее подписанная политика: что при исчерпании и кто решает.

На что смотрит интервьюер: разница между «200 OK» и правильным ответом; ограничения выборочной оценки; организационная сторона SLO, а не только графики.

Вы дежурный. Поддержка сообщает, что бот обещает клиентам компенсации. Первые 30 минут и следующая неделя?

Подтвердить по трейсам, найти недавнее изменение (промпт, модель, данные) и откатить флагом в реестре; если откатывать нечего — guardrail или kill switch. Объявить инцидент, дать поддержке формулировку для клиентов. Масштаб — ретроспективным поиском обязательств по диалогам с версией из трейсов. За неделю — blameless-постмортем с вопросом «почему evals не поймали» и действиями трёх типов: многоходовые кейсы в golden set, критерий судьи и метрика canary, обязательства только через инструмент с лимитами. Выполнять ли обещания, решает бизнес с юристами.

На что смотрит интервьюер: сначала сдерживание, потом расследование; откат без деплоя; разбор до системной причины, а не «поправим промпт».

Банк выбирает между API-моделью и self-hosted. Как будете принимать решение?

Сначала жёсткие ограничения с юристами и безопасностью: можно ли отправлять данные провайдеру при маскировании и на каких условиях (регион, хранение, обучение на данных). Если нельзя — API вне сравнения. Затем матрица: качество на своём eval-наборе, TCO при текущем объёме и ×10 с людьми и минимумом реплик, латентность, стоимость выхода. Для банка в ЕС — DORA: переход к другому поставщику должен быть реальным сценарием. Часто ответ гибридный: маскирование и API для генерации, своя малая модель там, где данные выходить не могут.

На что смотрит интервьюер: ограничения отделены от предпочтений; TCO с людьми; регуляторику интерпретирует юрист, а вы переводите её в архитектуру.

Продакт хочет фичу на LLM, а вы считаете, что LLM тут не нужна. Как это скажете?

Не спорить с целью, а предложить путь к ней дешевле. Проверить критерии: есть ли правило или справочник, нужна ли точность расчёта и аудит, латентность и объём, есть ли данные для evals. Прийти с цифрами: «правила закрывают 80% случаев за неделю работы, LLM добавит 10–15% за такие-то деньги и риски». Предложить гибрид и условие, при котором вернёмся к LLM. Если всё же решили делать — договориться о метрике успеха и kill-критерии до разработки.

На что смотрит интервьюер: умеете ли сказать «нет» так, чтобы услышали; деньги и риск вместо спора о технологиях.

Как внедрить обязательные evals в CI в команде, которая считает их бюрократией?

Не приказом. Начать с боли: найти недавнюю регрессию и показать, что 20 кейсов её бы поймали. Сделать первый шаг дешёвым: самому собрать smoke-набор для главной фичи и включить в CI как необязательную проверку с комментарием в PR. Когда она поймает пару регрессий, предложить сделать её обязательной и закрепить в стандарте. Дальше — разбор трейсов вместе и правило «каждый баг из прода — новый кейс». Метрика успеха — регрессии, пойманные до прода.

На что смотрит интервьюер: влияние без полномочий; ценность для команды прежде правил; изменение доведено до механизма, который работает без вас.