Как устроена проверяемость Digit
Галлюцинация — это ситуация, когда языковая модель порождает токены факта. Бороться с ней можно двумя способами: уговаривать модель не выдумывать или убрать канал, по которому порождённое содержание попадает в ответ. Digit устроен вторым способом.
Модель порождает только выбор. Содержание берётся из трёх источников.
| Источник | Что даёт | Кто производит |
|---|---|---|
| Детерминированная утилита | результат вычисления | каталог из 95 утилит, собранный бинарь |
| Дословная цитата из корпуса | фрагмент текста + якорь на файл | поиск по корпусу курсов и проверка дословности |
| Сертификат FTS | вывод из проверенных морфизмов | гейт над исполняемой спецификацией |
Разница между двумя классами ошибок здесь принципиальная. Ошибка выбора даёт «не тот ответ» или «не нашла». Ошибка порождения даёт выдуманный факт. Первый класс остаётся и измеряется. Второй закрыт конструктивно.
Источник первый: детерминированная утилита
Утилиты — это чистые функции: хеши, кодировки, конвертеры форматов, разбор адресов, работа с датами. 95 утилиты в 14 категориях, перенесённых из 75 исходных браузерных инструментов из 86. 11 не перенесены осознанно: они требуют живого браузера, загрузки файла или являются шпаргалкой, а не вычислением.
Каждая утилита несёт минимум один рабочий пример; всего примеров 114, и все они исполняются тестовым набором, а затем повторно — через собранный бинарь по тому же протоколу, которым ходит агент. Тестов — 220.
Небольшое расхождение, которое честнее назвать, чем сгладить: обучающий датасет маршрутизатора покрывает 94 утилиты, а в каталоге числится 95. Откуда разница, в отчётах не объяснено.
Ключевое для проверяемости: модель не пишет результат. Она называет идентификатор утилиты и аргументы, дальше считает код. Если аргументов не хватает, вызов не состоится — вместо ответа будет отказ с указанием, какого именно аргумента нет.
Каталог целиком в контекст не помещается — схемы 95 утилит стоят слишком дорого для маленькой модели. Поэтому маршрутизация двухшаговая: сначала индекс категорий (≈536), затем схемы утилит одной категории. Наружу торчат три инструмента вместо 95 схем.
Источник второй: дословная цитата с якорем
Второй источник — материалы курсов. Индекс собран по корпусу и содержит 29 775 фрагментов; поиск гибридный — плотные векторы модели intfloat/multilingual-e5-base вместе с лексическим поиском, поверх — кросс-энкодер BAAI/bge-reranker-v2-m3, который оценивает пару «вопрос + фрагмент» целиком.
Ответ в этом режиме — не пересказ. Это подстрока конкретного файла плюс якорь на этот файл. Проверка двойная: фрагмент обязан стоять дословно и в индексе, и в текущей редакции файла корпуса. Вторая половина проверки появилась не из теории: во время одной из работ соседний процесс переписал часть корпуса, индекс остался старым, и система начала добросовестно цитировать уцелевшие соседние фрагменты вместо исчезнувших нужных. Сверка с файлом превратила это в отказ.
Источник третий: сертификат над проверенными морфизмами
Третий источник нужен там, где ответ — не факт из текста и не результат вычисления, а вывод из правил предметной области: «положена ли скидка», «какая ставка применяется». Такие правила записываются на FTS — языке исполняемых спецификаций, — и спецификация проходит гейт из нескольких проверок, у каждой свой код отказа. Всего кодов восемь: разбор синтаксиса, статическая семантика, допустимость каждой предпосылки, исполнение авторских примеров и объявленных свойств, построение сертификата, независимая перепроверка, целостность библиотеки и код на непредвиденное условие.
Отдельный слой — детектор структурных логических ошибок: восемь кодов, каждый из которых является алгоритмом над разобранной спецификацией. Круг в основаниях, применение импликации в обратную сторону, непокрытая ветка разбора, порог без объявленного поведения ровно на границе, одно имя на вещь и на высказывание о вещи. Каталог, по которому детектор построен, содержит 67 позиций; механически проверяются 12. Остальные 55 — риторика и содержание, которые алгоритмом не решаются.
Предпосылки берутся не из текста спецификации, а из отдельной библиотеки: 29 записей, из которых 18 проверены человеком и несут ссылку на источник, 3 выведены типизированной композицией из проверенных, 3 имеют статус «предложено» и предпосылкой быть не могут. Тестов у гейта 62, у самой библиотеки FTS — 59.
Предел формального слоя, названный прямо
FTS гарантирует: если посылки верны — вывод верен. Истинность объявленного закона является предпосылкой, а не теоремой. Спецификация, утверждающая неверную доменную аксиому, проходит проверку, тесты и сертификацию полностью зелёной.
В документации гейта это показано рабочим контрпримером: спецификация облагает экспортную поставку налогом по основной ставке вместо нулевой. Три команды формализма отрабатывают безупречно, сертификат корректен, детектор логических ошибок не находит ни одной из 67 позиций каталога. Ошибку ловит не математика, а запись в библиотеке морфизмов, куда человек внёс вердикт «проверено и отклонено».
Отсюда неприятное, но честное следствие: устраняя структурные дефекты, детектор делает ложный вывод более гладким, а не более верным. Единственная точка, где решается предметная истинность, — ревью морфизма при внесении в библиотеку. Сила гарантии здесь равна силе ревью, а не силе формализма.
Два режима и запрет тихой деградации
Режим A — verified. Содержание только из трёх источников выше. Гарантия: ноль выдуманных фактов. Цена: заметная доля отказов.
Режим B — advisory. Свободная генерация модели. Гарантий нет, помечается явно.
Главный инвариант всей конструкции: режим A не деградирует в B молча. Если запрос не укладывается в A — либо отказ, либо явный переход в B с пометкой. Подмена «не смог проверить, но всё же отвечу» ломает конструкцию целиком: смысл режима A в том, что пользователю не нужно гадать, проверен ли конкретный ответ.
Поэтому у утечки режима заведена отдельная метрика с целевым значением ноль, и считается она строго: ответ, заявленный как режим A, обязан приложить хотя бы одну проверяемую улику — разрешимый вызов утилиты, валидную цитату или компилирующуюся спецификацию. Ответ без улики засчитывается как утечка, даже если он верен по существу.
Перед выдачей каждого ответа отрабатывает проверка инвариантов: для каждого элемента режима A должно быть разрешимое происхождение — файл существует и текст в нём есть, идентификатор утилиты есть в каталоге и аргументы приложены, у сертификата есть дайджест. В финальном прогоне нарушений инвариантов ноль.
Отказ принимает код, а не модель
Это важнее, чем кажется. Когда обычный ассистент «не отвечает», это его выбор: его можно переформулировать, уговорить, обойти. Отказ Digit в проверяемом режиме — результат детерминированной проверки.
три гейта проверяют происхождение:
1. релевантность лучшего фрагмента ниже порога → REFUSE_LOW_RELEVANCE
2. ответ не является подстрокой найденного → REFUSE_NOT_VERBATIM
3. лучшие кандидаты расходятся между собой → REFUSE_AMBIGUOUS
Четвёртый гейт проверяет не происхождение, а отвечаемость: относится ли найденный фрагмент к заданному вопросу. Он появился после того, как независимый набор показал, что именно там сидит большинство оставшихся ошибок. Решение и здесь принимает код: ни один путь этого модуля не спрашивает модель, отвечает ли фрагмент на вопрос. Все правила — сравнение строк с границами слов и разбор поверхностного синтаксиса запроса.
Работают два правила из четырёх реализованных:
- сущности вопроса. Латинские токены, идентификаторы, аббревиатуры, имена собственные, литералы в обратных кавычках извлекаются из запроса, и каждое обязано найтись во фрагменте по границе слова. Это правило убрало 8 ложных ответов;
- обстоятельство вместо данных. Перед вызовом утилиты каждый аргумент проверяется на то, чем он управляется в самом запросе. «Посчитай статистику текста в кодировке windows-1251» не содержит текста — содержит указание кодировки. Правило убрало 4 ложных ответа.
Ещё два правила были реализованы, измерены и выключены, потому что не убирают ни одного ложного ответа: правило, которое ничего не чинит, но может отказать, — это чистый риск на невиданных данных. Сломанных верных ответов все правила вместе дали ноль.
Отдельно — сертификационный гейт. Для морфизма, которого нет в проверенной библиотеке, соответствующая функция не определена, правило типизации неприменимо, и вывода просто не существует. Система не «решила промолчать»: она не смогла построить доказательство. Уговорить здесь нечего.
Отказ с числами, а не «я не знаю»
Формулировка отказа устроена как утверждение о закрытой области и обязана называть числа. Пример формы (числа в нём иллюстративные — в реальном отказе стоят фактические значения прогона):
В материалах курсов Digitable этого нет. Проверено 12 фрагментов, максимальная релевантность 0.31 при пороге 0.55.
Причина строгая: «ответа нет» без указания области поиска и без чисел — само по себе непроверяемое утверждение о мире, то есть та же галлюцинация, только скромнее одетая. Каждый отказ несёт код, область поиска и числа: сколько фрагментов проверено, какие сущности вопроса не нашлись, какого аргумента не хватило.
Тот же принцип действует и в другую сторону. Отказ детектора логических ошибок не является утверждением о ложности: найденное имя ошибки возвращает тезис в состояние «не показано», а не опровергает его. Это закреплено тестом, который механически запрещает словам «ложно» и «неверно» появляться в тексте отказа.
Роль маленькой модели
В такой архитектуре модель не пишет ответ, а классифицирует запрос и извлекает аргументы. Поэтому она может быть маленькой — сейчас это Qwen/Qwen3-1.7B, выбранная замером, а не по названию.
Замер, кстати, опроверг ожидание. Русскоязычный instruct-тюнинг поверх той же базы проиграл самой базе по всем существенным метрикам: он улучшает диалог, которого мы не используем, и размывает часть исходных способностей. Модель с расширенным русским словарём даёт настоящую экономию токенов и лучшую маршрутизацию, но платит точностью извлечения аргументов — а в проверяемой архитектуре утилита, вызванная с неверным аргументом, выдаёт проверенный неверный ответ, тогда как неверно выбранная утилита чаще всего не проходит проверку и видна.
Отдельно пришлось учить отказу. В обычном обучающем корпусе у каждого вопроса есть ответ, поэтому модель выучивает мета-правило «ответ существует всегда» и на запросе без ответа уверенно сочиняет. Лечение — доля обучающих примеров, где правильный ответ «отказ»: 24,0 % от 34 421 строк. Осознанный отказ на red-team вырос до 91,3 %, доля ложных ответов упала до 9,2 %.
И честная оговорка: основную работу сделали данные, а не размер модели. Тот же датасет на втрое меньшей базе даёт осознанный отказ 90,7 % против 91,3 %.
Что из этого следует
Конструкция даёт ровно одно: содержание ответа имеет разрешимое происхождение. Она не даёт ни правильности выбора источника, ни истинности того, что записано в корпусе и в библиотеке морфизмов.
Что получилось на измерениях и чего это стоило — на следующей странице. Где проходит граница и что система не ловит — на третьей.