протокол приёмки по методике документа 05
Статусы: ✅ зачтено · ◐ частично · ✕ не зачтено · ⊗ вне контура. Критичность — по 05, раздел 3.
Шаблон и обязательный состав — 05-Методика-приёмки.md, раздел 5.
| Параметр | Значение |
|---|---|
| Дата прогонов | 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; настоящий протокол сводит все наборы.
| Набор | Порог | Результат | Статус |
|---|---|---|---|
| Сверка загрузки 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 % | ✅ |
Прогон 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 — то есть сверка не согласна с любыми числами,
а действительно сравнивает.
Непройденных и спорных кейсов нет. В отчёте раннера все 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): код поймал число, которого нет в результате запроса, и заменил сводку детерминированной. Дефектом не считается — защита сработала так, как задумана, и повторилась трижды, то есть работает устойчиво, а не случайно.
Критичных дефектов (по 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
(потерянный фильтр, формулировка сводки), тремя прогонами живой приёмки не найдено.
| Что | Референс | Аналог | Почему |
|---|---|---|---|
| СУБД | 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; оба интерфейса делят один импорт ядра, а не два похожих |
Ядро референса — резолвер, словарь, проверки и оформление ответа — переиспользовано без изменения логики: новый код живёт в отдельном пакете сервиса и реализует ровно тот контракт, которого ядро ждёт.
eval/out/РЕГРЕССИОННЫЙ_ПРОГОН.md — все 46 кейсов регрессии с маршрутами, временем и ответами
(на сайте — «Приложение 1»)eval/out/ЖИВАЯ_ПРИЁМКА.md — живая приёмка, 8 сценариев, три прогона, полные ответы
(на сайте — «Приложение 2»)eval/out/runs/run2.md, eval/out/runs/run3.md — прогоны 2 и 3 живой приёмкиreports/_artifacts/offline_pg_shag2.txt (PostgreSQL),
reports/_artifacts/duckdb_tests_shag5.txt (DuckDB) — по 85 из 85reports/_artifacts/compare_pg_duckdb_shag5.txt — сверка 8 контрольных запросовreports/_artifacts/adversarial_sql_shag2.txt — состязательные тесты проверки SQLreports/_artifacts/check_ro_role_shag2.txt — права роли БДПротокол заполнен исполнителем (СРЕДА) по фактам прогонов. Решение о приёмке принимает заказчик.
| Прогоны выполнил | СРЕДА, 24.10.2026 |
| Принято заказчиком | дата, подпись |