Как это работает
Ниже изложена методика оценки качества кода через бенчмарк 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 |
| 1 | 8 |
| 2–3 | 6 |
| 4–6 | 4 |
| больше 6 | 2 |
Обрезанную генерацию ловит отдельная проверка
Если нарушен баланс парных конструкций (
Функция/КонецФункции,Если,Цикл,Попытка) — балл сразу 0, минуя шкалу. Так отсекается генерация, оборванная на полуслове, которую парсер иначе молча проглотил бы.
Синтаксис модуля ≠ синтаксис запроса
S смотрит на компиляцию модуля. Текст запроса (внутри кавычек) для компилятора — просто строка: кривой запрос или обращение к несуществующему полю он пропускает. Такая ошибка вылезет уже на исполнении (
Запрос.Выполнить()) и уйдёт в M или P, а не в S. Поэтому нормально видеть S = 10 и «синтаксическую ошибку» в логе одновременно — это синтаксис разных вещей.
M — Смысл
Код решает поставленную задачу? Машина запускает решение и прогоняет скрытые тесты — заготовленные пары «на таком входе должен быть такой результат», которых модель не видела (иначе подогнала бы ответ под них). Оракул — OneScript для A и настоящая 1С против синтетической базы для B. Балл — доля пройденных тестов:
| доля пройденных тестов | балл |
|---|---|
| 100 % | 10 |
| ≥ 85 % | 8 |
| ≥ 60 % | 6 |
| ≥ 50 % | 4 |
| больше 0 | 2 |
| 0 % или не исполнился | 0 |
На лидерборде M плавная (доля × 10), без мёртвых зон грубой шкалы: 4 теста из 5 дают ровно 8.0, а не округляются вниз. Не скомпилировался или не запустился — 0: подтверждённого смысла нет. Код может прекрасно компилироваться (высокая S) и при этом давать неверный ответ (низкая M) — это разные вещи, и PRISM их разделяет.
O — Оптимальность
Код написан грамотно — или будет тормозить? Решать задачу можно правильно, но ужасно медленно. Ключевой приём здесь: оптимальность мы меряем не «по виду кода», а исполнением — гоняем решение на растущем входе и смотрим, как быстро растёт работа.
- в алгоритмике (A) счётчик — число операций (
OneScript -codestat): подаём всё больший вход и смотрим на класс роста; - в платформенных задачах (B) счётчик — число обращений к СУБД (техжурнал 1С) на растущей базе: кто берёт данные набором (одним запросом) — обращений почти не прибавляет; кто лезет в базу в цикле — прибавляет на каждую строку.
Показатель роста 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 — это всегда условие + скрытая проверка + эталон:
- условие — текст задания, который получает нейросеть;
- скрытая проверка — тесты, которые модель не видит (иначе подгоняла бы ответ);
- эталон — образцовое решение, которое само обязано проходить эти тесты на 100 % (не проходит свои тесты — значит, сломаны тесты или эталон, и задача в банк не идёт).
Задачи бывают двух категорий:
- Категория A — алгоритмика на чистом языке 1С: сортировки, разбор строк, коллекции, даты. База не нужна — оценивается сам алгоритм и его класс роста. Исполняется в OneScript, бесплатном движке языка 1С. Оси: S · M · O.
- Категория B — платформенные задачи: остатки на складе, списание по FIFO, расчёт себестоимости. Им нужна настоящая 1С с базой, поэтому исполняются в реальной платформе (1С в Docker) против синтетической базы, которую PRISM собирает из описания самой задачи. Добавляется ось P. Оси: S · M · O · P.
Категория B даёт модели инструменты (function calling)
Чтобы написать платформенный код, модель должна знать схему базы — и собирает её сама, вызовами инструментов: спрашивает, какие есть регистры и поля, и пишет код, уже зная схему. Модель без поддержки инструментов схему не видит → выдумывает метаданные → низкий балл по оси P. Это часть замысла: грамотная работа с метаданными 1С — как раз тот навык, что мы и меряем.
Где живёт качество бенчмарка
Машина оценивает ровно настолько хорошо, насколько хороши скрытые тесты к задаче. Поэтому самая ценная работа человека — не проверять оценки, а придумывать сильные задания с сильными тестами. Хотите помочь проекту — начните с этого (Как участвовать).
Общая оценка Q
Иногда хочется одну цифру «на глаз» — для этого есть Q, среднее по применимым к категории осям: A: (S+M+O)/3, B: (S+M+O+P)/4. Но это вторичная величина: одна цифра прячет, где именно модель ошиблась. Главный результат PRISM — всегда вектор из четырёх осей, а не Q.
Дальше
- Лидерборд — кто сейчас впереди.
- Что умеет сейчас — честное состояние бенчмарка.
- Честные границы — что инструмент вправе утверждать, а что пока нет.
- Как запустить — установка и команды.
