Unknown Not A Value

Отсутствующее измерение в агентной системе почти никогда не остаётся отсутствующим: его молча заменяют нулём, плановым значением, дефолтом или именем стадии. Подстановка невидима, а решения дальше принимаются по ней — поэтому «неизвестно» обязано быть отдельным значением, доживающим до конца цепочки, а не пропуском, который кто-то по пути заполнит.

Почему это отдельная тема, а не мелочь отчётности

Пропуск сам по себе безвреден. Опасно то, что происходит с ним дальше: агентная система состоит из шагов, каждый из которых обязан что-то решить — допустить эксперимент, повысить свидетельство, закрыть этап, посчитать расход. Шаг, получивший пропуск, решение всё равно примет; вопрос только в том, по какому числу.

Отличие от обычной ошибки в том, что подстановка не отказывает. Нуль в расходе выглядит как дешёвый запуск, плановое значение — как достигнутое, дефолт — как выбранное. Ни один из этих случаев не поднимает тревогу, потому что на выходе получается правдоподобная величина, а не пустое место.

Восемь мест, где подстановка происходит

Все восемь наблюдались в одной системе, и это полезно само по себе: они не вариации одного места, а разные слои, где дефект возникает независимо.

Где Что подставляется Чем правильно
Учёт расхода ноль, когда провайдер не вернул расход отдельное значение «расход неизвестен»
Свидетельства плановое значение в поле измеренного план и факт — разные поля, разрыв между ними наблюдаем
Зрелость результата имя стадии вместо коэффициента покрытия без коэффициентов результат остаётся сигналом, а не свидетельством
Завершение команды пустой вывод, прочитанный как незавершённость статус завершения владеет этим фактом, а не текст на выходе
Замер потребления нулевой сэмпл на выключении как «ресурс не нужен» недостаточная выборка — это «неизвестно», а не «ноль»
Оценка времени средняя длительность вместо неизвестной неизвестная оценка остаётся неизвестной
Разбор конфигурации опечатка в ключе, молча взявшая дефолт отказ на разборе
Выбор ресурса подмена класса устройства при неизвестном профиле отказ вместо подмены

Строка про пустой вывод стоит отдельного внимания, потому что ошибка встречается чаще прочих: успешная команда может не напечатать ничего, и текст на выходе поэтому вообще не является фактом о завершении. Тот же класс, что три состояния вызова инструмента в Agent Failure Modes — «нашлось», «не нашлось», «сломалось» — только со стороны того, кто читает результат, а не того, кто его отдаёт.

Ноль — худшая из подстановок

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

Практическое следствие: поле, у которого ноль осмыслен, обязано иметь отдельное представление неизвестного — не 0, не null, который где-то дальше приведётся к нулю, а явное значение, которое переживёт сериализацию и агрегацию.

Что делать вместо подстановки

Форм ровно три, и выбирают между ними по тому, чем грозит продолжение.

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

Выбрать консервативно, а не средне. Неизвестный спрос на ресурс получает эксклюзивное размещение, неизвестная зрелость не повышается, неизвестный исход побочного эффекта считается произошедшим (Idempotency For Agents). Середина здесь — не компромисс, а тихая ставка на то, что всё в порядке.

Отказать громко. Неизвестный ключ конфигурации, неверный тип, несуществующий профиль — это не выбор пользователя, а его отсутствие, и молчаливый дефолт превращает опечатку в изменение политики. Отказ на разборе дешевле, чем прогон, отработавший не по той конфигурации.

Граница: где дефолт законен

Правило не запрещает дефолты, иначе конфигурация стала бы неподъёмной. Линия проходит по тому, был ли выбор:

  • пропущенное поле — это выбор «как принято», и документированный дефолт здесь корректен;
  • неизвестный или искажённый ключ — это отсутствие выбора, и дефолт здесь маскирует ошибку.

Второе уточнение: дефолт обязан быть назван дефолтом. Калиброванное общее значение — не истина предметной области, и там, где оно попадает в отчёт, оно помечается как подставленное. Иначе через полгода число обсуждают как измеренное.

Связано с

  • Agent Failure Modes — три состояния вызова инструмента: тот же принцип со стороны того, кто отдаёт результат
  • Idempotency For Agents — неизвестный побочный эффект как отдельная категория, а не «наверное, не произошло»
  • Quality Metric Design — почему подставленное значение портит метрику незаметнее, чем неверное
  • Agent Evals — свидетельство без измерения не повышается до результата
  • Cost Anomaly Alerting — расход, записанный нулём, не поднимает тревогу и не попадает в аномалии
  • Blameless Postmortem — та же дисциплина в разборе инцидента: «неизвестно» честнее догадки
  • Agent First Repository — инвариант, записываемый в спецификации: отсутствие значения показывает «недоступно», а не ноль