Как это работает

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

Четыре оси качества (SMOP)

SMOP — первые буквы четырёх осей (Syntax · Meaning · Optimization · Platform). Как призма раскладывает свет на спектр, PRISM раскладывает расплывчатое «качество кода» на четыре независимых измерения. У каждого — свой оракул (чем меряем), своя шкала 0–10 и свой критерий (за что балл). Одно «не прошло» превращается в диагноз: что именно сломалось.

осьвопросоракулбалл выше, когда
S Синтаксискод компилируется?BSL LS · компилятор 1Сменьше ошибок сборки
M Смыслкод решает задачу?скрытые тесты на исполнениибольше тестов пройдено
O Оптимальностькод не будет тормозить?codestat · техжурнал 1Скласс роста ближе к оптимальному
P Платформаверно обращается к объектам 1С?прогон против синтетической базыменьше обращений к выдуманным метаданным

Дальше — каждая ось подробно, с таблицей критерия.

S — Синтаксис

Код вообще компилируется — или там ошибки? Самый базовый уровень: машина пробует собрать модуль. Оракул — BSL Language Server для алгоритмики (A) и компилятор 1С (DESIGNER /CheckModules) для платформенных задач (B). Балл считается по числу корневых причин ошибок (соседние ошибки одного места объединяются в одну):

корневых причин ошибокбалл
0 — чисто10
18
2–36
4–64
больше 62

Обрезанную генерацию ловит отдельная проверка

Если нарушен баланс парных конструкций (Функция/КонецФункции, Если, Цикл, Попытка) — балл сразу 0, минуя шкалу. Так отсекается генерация, оборванная на полуслове, которую парсер иначе молча проглотил бы.

Синтаксис модуля ≠ синтаксис запроса

S смотрит на компиляцию модуля. Текст запроса (внутри кавычек) для компилятора — просто строка: кривой запрос или обращение к несуществующему полю он пропускает. Такая ошибка вылезет уже на исполнении (Запрос.Выполнить()) и уйдёт в M или P, а не в S. Поэтому нормально видеть S = 10 и «синтаксическую ошибку» в логе одновременно — это синтаксис разных вещей.

M — Смысл

Код решает поставленную задачу? Машина запускает решение и прогоняет скрытые тесты — заготовленные пары «на таком входе должен быть такой результат», которых модель не видела (иначе подогнала бы ответ под них). Оракул — OneScript для A и настоящая 1С против синтетической базы для B. Балл — доля пройденных тестов:

доля пройденных тестовбалл
100 %10
≥ 85 %8
≥ 60 %6
≥ 50 %4
больше 02
0 % или не исполнился0

На лидерборде M плавная (доля × 10), без мёртвых зон грубой шкалы: 4 теста из 5 дают ровно 8.0, а не округляются вниз. Не скомпилировался или не запустился — 0: подтверждённого смысла нет. Код может прекрасно компилироваться (высокая S) и при этом давать неверный ответ (низкая M) — это разные вещи, и PRISM их разделяет.

O — Оптимальность

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

Показатель роста p сравнивается с оптимальным для задачи p_opt, сигнал — отклонение d = p − p_opt:

отклонение класса роста dбалл
на уровне оптимума (≤ 0.2)10
чуть хуже (≤ 0.5)8
заметно хуже (≤ 0.8)6
на класс хуже, n → n² (≤ 1.2)4
ещё хуже2

С таймаутом в двух категориях поступают по-разному. В алгоритмике не дождались — значит, и правда слишком медленно: это сигнал, балл 2. В платформенных задачах таймаут говорит лишь о том, что замер не состоялся, — тогда ось остаётся неизмеренной (N/A), а не наказывается баллом.

«Запрос в цикле» на цифрах

На одной платформенной задаче эталон читает регистр дважды, и с ростом базы это число не меняется. Решение с запросом внутри цикла на той же базе делает уже 62 обращения — и чем больше база, тем больше. Оба варианта проходят тесты (M одинаковая), но по оси O они расходятся: один держит нагрузку, другой ляжет на реальных данных. Статический анализ увидел бы это «по виду кода» не всегда — а исполнение ловит по эффекту.

Гейт по исполнению: не запустился — O = N/A

O ставится только исполнившемуся коду. Не скомпилировался или упал — балл N/A (ось выпадает из оценки), а не высокий. Иначе анализ «по виду кода» льстил бы неисполнимому решению: в категории B встречалось O = 10 при M = 0 — красивый на вид код, который вообще не работает.

Замер есть не у каждой задачи

Гонять решение на растущем входе можно там, где к задаче написан профиль нагрузки (perf.yaml). Для остальных B-задач, где нет нагрузочных тестов оптимальность пока оценивается по-старому — разбором самого кода: BSL Language Server ищет типовые антипаттерны (запрос в цикле, виртуальная таблица без параметров и подобное), их суммарный вес и даёт балл — по своей шкале, не по таблице выше. Это запасной путь, а не основной: где профиль есть, статику не спрашивают вовсе. И тот же гейт по исполнению действует и здесь — неисполнимый код получит N/A, а не высокий балл за красивый вид.

Насколько удачно выбран сам подход к решению — этого машина не судит; это уже к эксперту (ниже).

P — Платформа

Код верно обращается к объектам 1С — справочникам, регистрам, реквизитам? Нейросети часто выдумывают несуществующие поля и объекты: со стороны код правдоподобен, но падает на первом же реальном обращении к базе. PRISM запускает код против синтетической базы и смотрит, пережил ли он контакт с метаданными. Сигнал берётся из того же прогона, что и M — один запуск, два измерения. Балл — доля тестов, отработавших без платформенной ошибки:

доля тестов без платформенной ошибкибалл
все обращения отработали (100 %)10
большинство верны (≥ 50 %)6
хоть один тест пережил базу4
ни одного0

Ось применима только к категории B: в алгоритмике обращений к метаданным нет, там P = N/A. Если функции кандидата нет или модуль не компилируется — P = 0: ни одного подтверждённого обращения к метаданным.

Зачем разделять M и P

По задумке методики нельзя сваливают любой провал в одно «не прошло». PRISM различает две болезни: «выдумал несуществующее поле» (P) и «поля верные, но логика неверна» (M). Для разработчика это разные диагнозы с разным лечением — и PRISM показывает, какой именно.

Точные пороги — в протоколе метрики

Таблицы выше даны для наглядности. Канонический источник порогов, весов и правил — metrics/smop_l1_auto.yaml; скореры читают их оттуда, а не из этого текста. Так числа живут в одном месте и не расходятся.

Два уровня: машина и эксперт

Оценку ставят на двух уровнях, и у каждого своя роль.

Уровень 1 — машина. Берёт код и запускает: компилятор, тесты, реальная база, техжурнал. Работает сама, на тысячах ответов. Именно она строит лидерборд и отвечает на вопрос «какая нейросеть лучше». Балл каждой оси выводится из одной таблицы — единого источника правды и для скорера, и для человека.

Уровень 2 — эксперт (пока в планах; прототип платформы — genlab-1c-web). Оценивать руками каждый ответ нельзя: задач десятки, а ответов — тысячи, людей не хватит. Поэтому роль эксперта другая:

Если коротко: машина измеряет код, а эксперт измеряет машину. Оба мерят одно и то же по одной конституции (metrics/smop.yaml) — различаются лишь инструментом и дробностью балла. Их согласие — каппа Коэна между авто- и экспертной оценкой — и есть главный научно-исследовательский результат PRISM.

Знак «проверено» (Verified)

Совпадение машины с экспертом превращает «машина так посчитала» в «этой оценке можно верить». Проверенная экспертом часть бенчмарка получит отметку Verified — слой доверия поверх автоматического лидерборда. Пока такого слоя нет (Уровень 2 в планах), но методика и шкала под него уже заложены.

Шкала оценок

Эксперт ставит баллы «через одну»: 0 · 2 · 4 · 6 · 8 · 10 — без серединок, чтобы человек не отлынивал нейтральной оценкой, а определялся. Машина же считает точно и хранит плавный балл: «4 теста из 5» — это 80 %, и на лидерборде так и показывается — 8.0, в полном разрешении. В грубые шесть ступеней точную оценку проецируют только в один момент — при сверке с экспертом (у него другой шкалы нет). То есть точное число — настоящая оценка для лидерборда, а ступень — общий язык для сравнения с человеком, а не само измерение.

Что такое «задача»

Задача в PRISM — это всегда условие + скрытая проверка + эталон:

Задачи бывают двух категорий:

Категория B даёт модели инструменты (function calling)

Чтобы написать платформенный код, модель должна знать схему базы — и собирает её сама, вызовами инструментов: спрашивает, какие есть регистры и поля, и пишет код, уже зная схему. Модель без поддержки инструментов схему не видит → выдумывает метаданные → низкий балл по оси P. Это часть замысла: грамотная работа с метаданными 1С — как раз тот навык, что мы и меряем.

Где живёт качество бенчмарка

Машина оценивает ровно настолько хорошо, насколько хороши скрытые тесты к задаче. Поэтому самая ценная работа человека — не проверять оценки, а придумывать сильные задания с сильными тестами. Хотите помочь проекту — начните с этого (Как участвовать).

Общая оценка Q

Иногда хочется одну цифру «на глаз» — для этого есть Q, среднее по применимым к категории осям: A: (S+M+O)/3, B: (S+M+O+P)/4. Но это вторичная величина: одна цифра прячет, где именно модель ошиблась. Главный результат PRISM — всегда вектор из четырёх осей, а не Q.

Дальше