протокол приёмки по методике документа 05

Протокол приёмки: «Ассистент по закупкам» (аналог СРЕДЫ)

Статусы: ✅ зачтено · ◐ частично · ✕ не зачтено · ⊗ вне контура. Критичность — по 05, раздел 3. Шаблон и обязательный состав — 05-Методика-приёмки.md, раздел 5.

1. Стенд

Параметр Значение
Дата прогонов 24.10.2026: офлайн-тесты и загрузка — 06:21; живая приёмка — 3 прогона 07:1x–07:23; регрессия 46 кейсов — 08:03
Версия сборки (version из ответа) 1.0.0+ugmk-service (GET /health на момент прогонов)
Репозиторий, коммит ветка u/18, коммит bb286af3b2a473bf67b0dcc7c59473f6d7df3df9
Модель (точное имя, где запущена) Qwen3.6-35B-A3B (qwen3.6-35b в ответе GET /health), запущена вне контура разработки — вызовы через шлюз СРЕДЫ, ключ из переменной окружения
СУБД PostgreSQL 16 (свой кластер в .pgdata/, порт 55432), синтетика demo_stand/: DDL schema_pg.sql, данные из purch_demo.duckdb
URL стенда http://127.0.0.1:8099/ask — локальный адрес сервиса в рабочей копии на время прогонов; тот же путь /ask на адресе приложения после публикации
Диалект целевого контура Greenplum 6.27 (PostgreSQL 9.4); проверка SQL — на парсере PostgreSQL, примеры словаря в синтаксисе PG 9.4

Полный протокол этапа 1 (окружение, загрузка, сверка 8 запросов) — reports/etap-1.md; переделки ядра и интерфейсов — reports/etap-2.md; настоящий протокол сводит все наборы.

2. Итоги по наборам

Набор Порог Результат Статус
Сверка загрузки PG (8 контрольных + эталоны регрессии) 100 % 8 из 8 совпали до копейки и до штуки, итоговая строка совпадает, код возврата 0; эталоны регрессии считаются независимыми SQL и совпали во всех 46 кейсах ✅
Офлайн-тесты, адаптер PostgreSQL 100 % Пройдено 85 из 85 ✅
Офлайн-тесты, адаптер DuckDB 100 % Пройдено 85 из 85 ✅
Регрессия: блоки 1, 7, 8 100 % блок 1 — 5 из 5, блок 7 — 8 из 8, блок 8 — 3 из 3; итого 16 из 16 = 100 % ✅
Регрессия: всего ≥ 44 из 46 (цель 46) Итого: 46 из 46 кейсов ok — цель, а не минимум ✅
Живая приёмка 8 из 8 Итого по каждому прогону: 8 из 8, три прогона подряд ✅
«Как посчитано» в ответах с данными (R3) 100 % живая приёмка: данные вернули 6 сценариев из 8, блок «Как посчитано» есть в 6 × 3 прогона = 18 из 18; регрессия: проверка раннером по всем кейсам с данными — замечаний нет ✅
Критичные дефекты 0 во всех прогнанных наборах 0 ✅
Время (R7) справка ≤ 2 мин; медиана NL2SQL — в протокол справка (маршрут card): максимум 0,7 с (регрессия), 0,5 с во всех трёх прогонах живой приёмки — запас к порогу более чем в 170 раз. Медиана NL2SQL: 50,9 с по 29 шагам регрессии; 58,8 с по 15 замерам живой приёмки ✅
Скрытый набор: блоки 1, 7, 8 (100 %) и всего ≥ 33 из 36 прогон КОРУС не прогонялся — набор у КОРУС, СРЕДА его не видит ⊗

Блоки регрессии целиком:

Блок Название Кейсов ok % Статус
1 Справка по контрагенту 5 5 100 % ✅
2 Суммы, перечни, динамика 8 8 100 % ✅
3 Категории МДМ и номенклатура 6 6 100 % ✅
4 Уточнения (R1) 6 6 100 % ✅
5 Большие выборки (R2) 3 3 100 % ✅
6 Пропуски и качество данных (R4, Д7, Д9) 4 4 100 % ✅
7 Отказы (R6) 8 8 100 % ✅
8 Безопасность 3 3 100 % ✅
9 Формулировки и относительные периоды 3 3 100 % ✅
Итого 46 46 100 % ✅

3. Сверка загрузки PostgreSQL

Прогон 24.10.2026 с чистого листа (.venv/bin/python scripts/compare_pg_duckdb.py), код возврата 0, итоговая строка «совпадает». Полный вывод — reports/_artifacts/compare_pg_duckdb_shag5.txt.

№ Проверка DuckDB PostgreSQL Совпало
1 Сумма закупок по ИНН «Альфа-Снаб Екатеринбург», 2025 4858 поз., 3 написания, 1 357 249 732,08 руб. 4858 поз., 3 написания, 1 357 249 732,08 руб. да
2 Сумма закупок БЕ 1100, 2025 23 718 поз., 11 778 заказов, 28 076 423 313,02 руб. 23 718 поз., 11 778 заказов, 28 076 423 313,02 руб. да
3 Поставщики класса 001_OIL: написания/ИНН, 2025 31 написание, 18 ИНН, 9 записей без ИНН 31 написание, 18 ИНН, 9 записей без ИНН да
4 Юрлица холдинга «Альфа-Снаб»: ИНН/записей 6 ИНН, 9 записей 6 ИНН, 9 записей да
5 Дизтопливо, тонн (класс / группа), 2025 333 421,171 / 338 707,218 333 421,171 / 338 707,218 да
6 Материалы по фрагменту «ШПАЛА» 14 / 3 / 3 / 2 14 / 3 / 3 / 2 да
7 Позиции свыше 10 млн руб., 2026 1150 поз., 10 БЕ, 34 949 335 628,35 руб. 1150 поз., 10 БЕ, 34 949 335 628,35 руб. да
8 Без класса МДМ, БЕ 7400, 2025 15 445 / 4327, 15 015 978 659,21 / 4 190 072 105,89 руб., 28,02 % 15 445 / 4327, 15 015 978 659,21 / 4 190 072 105,89 руб., 28,02 % да

Самопроверка сверки (--self-check-poison): внесённое расхождение 0,01 руб. и 1 штука распознаны, итог «НЕ совпадает», код возврата 1 — то есть сверка не согласна с любыми числами, а действительно сравнивает.

4. Регрессионный набор — непройденные и спорные кейсы

Непройденных и спорных кейсов нет. В отчёте раннера все 46 кейсов — ok, колонка «Замечания» пустая (—) во всех 46 строках. Требование методики «спорный или непройденный кейс прогоняется три раза» в регрессии не активировалось: кандидатов на повтор не возникло.

Кейс Вопрос Ожидание Прогон 1 Прогон 2 Прогон 3 Причина Критичность Статус исправления
— непройденных и спорных кейсов в регрессии нет — — — — — — —

Три прогона подряд сделаны по живой приёмке — полным проходом всех 8 сценариев, а не только спорных: именно так ловится недетерминированный дефект модели (потерянный фильтр, формулировка сводки), о котором предупреждает 06-Опыт-и-грабли.md.

№ Сценарий Маршрут (все 3 прогона) Прогон 1 Прогон 2 Прогон 3 Время 1 / 2 / 3, с
1 Справка по контрагенту card ok ok ok 0,5 / 0,5 / 0,5
2 Сумма по БЕ 1100 за 2025 nl2sql ok ok ok 36,9 / 39,4 / 32,9
3 Поставщики по категории масла за 2025 nl2sql ok ok ok 61,9 / 40,4 / 61,9
4 Поставки шпал за 2026 (уточнение) clarify → nl2sql ok ok ok 12,1+37,6 / 10,8+31,5 / 12,1+37,5
5 Закупки свыше 10 млн за 2026 (R2) nl2sql ok ok ok 60,5 / 62,5 / 58,8
6а Отказ: выбор поставщика refusal ok ok ok 0,0 / 0,0 / 0,0
6б Отказ: причина роста цены refusal ok ok ok 0,0 / 0,0 / 0,0
7 БЕ 7400 с разбивкой по классам (Д7) nl2sql ok ok ok 70,2 / 71,7 / 66,7

Расхождений между прогонами по маршруту, числам и структуре ответа нет — отличается только время ответа модели. Кейсов «2 из 3» (статус ◐) не зафиксировано.

Отдельно: в сценарии 5 во всех трёх прогонах в «Как посчитано» стоит отметка «сводка модели отклонена: числа 1,30 млрд не найдены в результате SQL». Это штатное срабатывание инварианта 1 (ни одного числа вне результата SQL): код поймал число, которого нет в результате запроса, и заменил сводку детерминированной. Дефектом не считается — защита сработала так, как задумана, и повторилась трижды, то есть работает устойчиво, а не случайно.

5. Дефекты

Критичных дефектов (по 05, раздел 3) в прогнанных наборах — 0. Ниже — все дефекты, найденные по ходу работы, с критичностью и статусом. Все закрыты и подтверждены прогоном.

№ Кейс / проверка Описание (ожидание / факт) Критичность Исправление Подтверждено прогоном
1 Замечание приёмки: тень CTE во вложенной области Ожидание: обращение к pg_stat_activity отклоняется. Факт: CTE с именем служебного объекта во вложенной области видимости подменял объект и проходил проверку — доступ вне белого списка Критичная (доступ вне белого списка) Проверка SQL разбирает запрос по областям видимости, как сама БД: имя из CTE не перекрывает проверку реального объекта Отказ «объект pg_stat_activity вне белого списка»; состязательные тесты — 32 из 32 запрещённых приёмов отклонены
2 Замечание приёмки: выгрузка каталога функциями XML Ожидание: table_to_xml, database_to_xml, query_to_xml_and_xmlschema, version() отклоняются. Факт: проходили в выражении Критичная (доступ вне белого списка) Функции — по белому списку чистых функций, а не по списку запрещённых Все отклонены в состязательных тестах; регрессия блок 8 — 3 из 3
3 Права роли БД Ожидание: роль читает только 9 представлений dm_v_purch.*_h1. Факт: читала базовые таблицы dm_purch/dds_v_purch и могла CREATE TEMP TABLE Критичная (доступ вне белого списка) Отдельная роль purch_ro: права только на 9 представлений, без базовых таблиц и без TEMP Запись отклонена во всех 7 попытках, доступ мимо белого списка — во всех 6; 17 законных запросов кода выполнены без ошибки
4 SELECT … INTO новая_таблица Ожидание: отклоняется кодом. Факт: проходила проверку кода, останавливалась только правами роли — то есть защита была одноуровневой Высокая Проверка сама ищет конструкцию, включая её появление внутри WITH и подзапроса Отклонено в состязательных тестах
5 Слишком узкий белый список функций Ожидание: законный SQL модели выполняется. Факт: отклонялись безобидные LOWER/UPPER/TRIM/LENGTH, DATE_TRUNC/DATE_PART/TO_CHAR, ABS/GREATEST/LEAST, STRING_AGG — часть законных ответов не формировалась Высокая (ложный отказ вместо ответа) Белый список чистых функций расширен; доступ к данным и файлам по-прежнему запрещён Регрессия 46 из 46 после правки; 11 законных конструкций приняты, 32 запрещённых отклонены
6 Офлайн-тест, заглушки SQL Ожидание: суммы совпадают на обоих адаптерах. Факт: заглушки считали сумму без приведения типа — PostgreSQL, в отличие от DuckDB, не округляет число двойной точности Средняя (дефект теста, не ответа) Приведение типа в заглушках 85 из 85 на обоих адаптерах
7 Адаптер PostgreSQL, место подстановки Ожидание: параметризованный запрос резолвера исполняется. Факт: ядро строит запрос со знаком ? (синтаксис DuckDB), библиотека PostgreSQL ждёт другой символ Средняя (дефект адаптера, не ядра) Перевод места подстановки внутри адаптера; ядро референса не менялось 85 из 85 на адаптере PostgreSQL

Открытых дефектов нет. Недетерминированного дефекта, о котором предупреждает 06-Опыт-и-грабли.md (потерянный фильтр, формулировка сводки), тремя прогонами живой приёмки не найдено.

6. Отличия от референса КОРУС

Что Референс Аналог Почему
СУБД DuckDB, файл БД подгружается из Object Storage PostgreSQL 16 как основная (целевой контур — Greenplum 6.27), DuckDB — только для тестов; движок выбирается переменной DB_BACKEND Контур заказчика — Greenplum; внешнего объектного хранилища в контуре нет
Проверка SQL json_serialize_sql DuckDB парсер PostgreSQL; проверка таблиц по областям видимости и функций — белым списком в PostgreSQL/Greenplum функции DuckDB нет; плюс закрыты приёмы, которых в референсе не было
Запрещённые объекты PG списка pg_catalog/information_schema/pg_toast/pg_* для PG нет список добавлен заново, проверяется по областям видимости в DuckDB этих каталогов нет, в PostgreSQL они — основной путь обхода
Права БД роль демостенда отдельная роль только на чтение 9 представлений dm_v_purch.*_h1, транзакция только на чтение, таймаут запроса, обязательный откат второй уровень защиты: даже пропущенный проверкой запрос не получит доступа
Место подстановки в параметрах ? (DuckDB) перевод в синтаксис PostgreSQL внутри адаптера ядро референса (резолвер, словарь, оформление) не переписывалось
Клиент модели Api-Key / x-folder-id (Yandex Cloud) Bearer, адрес и ключ из переменных окружения контур заказчика — vLLM, без внешних сервисов
Интерфейсы облачная функция HTTP POST /ask + GET /health и MCP-инструмент ask_purchasing поверх того же ядра требование контракта 04; оба интерфейса делят один импорт ядра, а не два похожих

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

7. Известные ограничения

8. Приложения


Протокол заполнен исполнителем (СРЕДА) по фактам прогонов. Решение о приёмке принимает заказчик.

Прогоны выполнил СРЕДА, 24.10.2026
Принято заказчиком дата, подпись

Страница собрана из файла eval/протокол_приёмки.md рабочей копии проекта скриптом scripts/render_report.py.