Учебный проект: сравнительный анализ workflow, одиночного LLM-агента и мультиагентной команды для анализа SQL-данных в PostgreSQL.
Команда: Романов Сергей, Александра Железнова.
Цель: сравнить workflow, одиночного LLM-агента и одну MAS с общей чёрной доской по качеству ответа на SQL-вопросы, затратам и времени.
presentation/SQL_Agents_Results_2026-10-22_Clean.pptx— актуальная презентация с реальным LLM-прогоном и заметками докладчика на 5 минут.presentation/SQL_Agents_PostgreSQL.pptx— предыдущая презентация с автономным demo.results/postgres_prepared/report.html— архивные результаты автономного demo, таблицы, графики и история взаимодействий. Открывается обычным браузером без сервера и интернета.DEFENSE.md— сценарий выступления, последовательность показа и вопросы к защите.- Все три варианта запускаются через
main.py, используют одну базу и общие инструменты.
Реальный прогон в актуальной презентации: qwen/qwen3.5-9b через Polza.ai, PostgreSQL 16.2, шесть задач по пять повторов для каждого из трёх вариантов, всего 90 запусков. Входные файлы с результатами предоставлены пользователем, CSV и JSON сверены. Итоговый успех по исходному оценщику: workflow 25/30, Single 10/30, MAS 4/30. Среднее время: 0,113 / 23,184 / 41,751 с. Тарифы в результатах не заданы, стоимость не рассчитана. Токены Single и MAS известны частично: не менее 49 335 и 146 615 соответственно. Оценщик сравнивает точные метки строк, поэтому правильное число с другой подписью может считаться провалом. Пересчёт качества в этом обновлении не выполнялся.
Архивное demo: results/postgres_prepared/ относится к запрограммированным ответам с намеренными ошибками. Оно остаётся примером запуска каркаса и не является реальным LLM-бенчмарком. Новая презентация использует предоставленный live-прогон, а не эти архивные измерения.
Нужен только запущенный Docker Desktop или Docker Engine с Docker Compose. Python, psycopg и PostgreSQL устанавливаются внутри контейнеров.
git clone https://github.com/SergioSboy/mas.git
cd mas
docker compose run --build --rm appCompose собирает приложение, запускает PostgreSQL, дожидается готовности базы после загрузки учебных данных и выполняет все три варианта на шести вопросах. При первом запуске нужен интернет для образов и зависимостей. Для повторного запуска уже собранного приложения:
docker compose run --rm appНовый отчёт появляется на компьютере в results/demo_<дата>/report.html, рядом сохраняются CSV и JSON. В сообщении приложения путь начинается с /app/results/: на компьютере это папка results/ внутри репозитория. Результаты сохраняются после удаления контейнера. HTML открывается браузером без сервера.
Приложение — одноразовая команда: после эксперимента оно завершается, база продолжает работать. Остановить контейнеры, сохранив базу и отчёты:
docker compose downПри первом старте создаются выделенная база salesbench, 48 заказов и роль salesbench_reader только с правами чтения. Владелец базы не используется агентами. Учебные пароли demo_owner и demo_reader заданы в Compose и SQL-инициализации. Внутри сети приложение обращается к db:5432, для внешних инструментов база доступна на 127.0.0.1:55432. Существующий том не перезаписывается при повторном запуске.
Нужны Python 3.10+ и отдельно установленный и запущенный PostgreSQL 14 или новее. Docker не используется ни приложением, ни базой. Оба варианта запуска выполняют один и тот же main.py с одинаковыми задачами и метриками.
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp .env.example .envВ .env уже указаны учебные подключения к PostgreSQL на 127.0.0.1:5432. Если сервер работает на другом порту или используются другие пароли, измените DATABASE_URL и DATABASE_ADMIN_URL. main.py, init_db.py и тесты автоматически читают .env. Явные переменные терминала всегда имеют приоритет.
Один раз выполните скрипт из scripts/ через psql от имени администратора PostgreSQL:
psql -U postgres -d postgres -f scripts/setup_local.sql
.venv/bin/python init_db.pyНа macOS с PostgreSQL из Homebrew администратором часто является текущий пользователь: тогда используйте psql -d postgres -f scripts/setup_local.sql. Если нужен другой адрес, укажите -h, -p, -U для psql и те же адрес и порт в .env. Сам PostgreSQL устанавливается обычным способом для вашей системы, например Homebrew, системным пакетом Linux или официальным установщиком Windows.
SQL-скрипт создаёт отдельные роли salesbench_owner и salesbench_reader, а также базу salesbench. Он не сбрасывает пароли существующих ролей и отказывается менять базу, если у неё другой владелец. Затем init_db.py загружает учебные таблицы в новую схему. Повторный запуск не перезаписывает данные.
./run_local.sh
./run_local.sh --variant mas --task T3
.venv/bin/python -m unittest discover -s tests -vМожно запускать напрямую: .venv/bin/python main.py. На Windows используйте .venv\Scripts\python.exe main.py и тот же путь к Python для установки зависимостей, инициализации и тестов. Bash-скрипт на Windows не требуется.
На macOS есть отдельный файл Запустить локально.command: он запускает Python из .venv и открывает новый HTML-отчёт. Файл Запустить демо.command по-прежнему запускает Docker-вариант. Локальные отчёты тоже сохраняются в results/. Проверены 18 запусков и 16 тестов напрямую через локальные Python и PostgreSQL 14.17, без Docker.
Для настоящей LLM заполните OPENAI_API_KEY и LLM_MODEL в том же .env и выполните ./run_local.sh --backend live --repeat 5. Тесты используют DATABASE_ADMIN_URL и DATABASE_URL. При необходимости отдельные подключения для тестов можно задать через TEST_DATABASE_ADMIN_URL и TEST_DATABASE_URL.
docker compose run --rm app --variant mas --task T3
docker compose run --build --rm testsТесты используют тот же образ Python и настоящий PostgreSQL. Сервис tests получает доступ владельца только для создания своей временной схемы с уникальным именем и удаления этой схемы после проверки. Приложение app такого доступа не получает. Тестовый сервис не запускается при обычном docker compose up.
Проверено полным запуском в Docker: 18 экспериментов и 16 тестов прошли на PostgreSQL 17.11 и Python 3.12. Результаты сохранились в папке проекта после удаления контейнера приложения.
На macOS Запустить демо.command делает то же самое через Docker и открывает новый отчёт. Открыть результаты.command открывает подготовленный отчёт из репозитория. Для чтения уже готовых HTML-файлов Docker не нужен.
В Dockerfile зафиксирована ветка Python 3.12, в requirements.txt — версия psycopg. В образ попадают только исходники и тесты. .dockerignore исключает локальные окружения, Git, ключи, результаты и презентации. Код копируется в образ, поэтому после правки Python-файлов запускайте с --build. Приложение работает от пользователя app, а не root. На Linux при отличии UID владельца папки результатов от 1000 можно запустить docker compose run --user "$(id -u):$(id -g)" --rm app.
Для обычного demo файл .env не нужен. Compose автоматически читает .env рядом с compose.yaml и передаёт в приложение только явно перечисленные настройки LLM. При локальном запуске Python читает этот же .env, не переопределяя уже заданные переменные. В Docker файл .env не копируется: настройки приходят через Compose. Ключ передаётся при запуске контейнера, не входит в образ и исключён из Git.
Для подключения контейнера к своему учебному серверу передайте DATABASE_URL через docker compose run -e DATABASE_URL=... app. Внутри контейнера localhost означает сам контейнер. Для PostgreSQL из стандартного Compose используйте имя db, для внешнего сервера — его сетевой адрес. Подробный прямой запуск описан в разделе «Вариант 2: Без Docker».
init_db.py нужен только при ручной настройке отдельного сервера: база и роль чтения должны уже существовать. Он создаёт новую схему salesbench и не меняет существующую. Обычный main.py ничего не создаёт и не сбрасывает. Docker загружает db/01_roles.sql и db/02_seed.sql только на новом томе.
Подготовленный отчёт в results/postgres_prepared/ относится к исходному прогону на PostgreSQL 14.17, а не к контейнерному запуску. Новые Docker-прогоны сохраняются отдельно, в их JSON указана фактическая версия сервера. Предыдущая презентация использует эти исходные измерения. Актуальная версия от 22 октября отражает предоставленный пользователем live-прогон.
Скопируйте .env.example в .env и укажите OPENAI_API_KEY и точный ID доступной модели в LLM_MODEL. Ключ не нужно добавлять в Dockerfile или передавать при сборке.
cp .env.example .env
# Заполните OPENAI_API_KEY и LLM_MODEL в .env, затем:
docker compose run --build --rm app --backend live --repeat 5Одна и та же модель используется одиночным агентом и всеми ролями MAS. Workflow LLM не вызывает. Если модель расходует токены на внутренние рассуждения, при необходимости увеличьте --max-output-tokens одинаково для Single и MAS.
API должен поддерживать Chat Completions, response_format=json_object, max_completion_tokens и поле usage. По умолчанию используется https://api.openai.com/v1. Для совместимого сервиса задайте LLM_ENDPOINT с окончанием /v1. Формат запроса проверен по официальной документации OpenAI. Доступ к внешнему API в этой подготовке не проверялся. В тестах транспорт заменяется локальными ответами.
Стоимость рассчитывается только при явно указанных тарифах за миллион токенов, например:
docker compose run --rm app --backend live --repeat 5 --input-price 1 --output-price 41 и 4 в примере — условные числа, а не тариф выбранной модели. Укажите актуальные ставки своего сервиса. Стоимость — оценка без скидок на кэш, налогов и пакетных тарифов. Без тарифов стоимость остаётся null, а не превращается в ноль. Если после ошибки API нет usage, итоговый расход также неизвестен, известная часть сохраняется отдельно. Такой запуск остаётся в знаменателе качества и во времени, а в суммарной стоимости будет null. Повторные платные вызовы без информации о расходе блокируются.
| Вариант | Поведение |
|---|---|
| Workflow | Выбирает готовый SQL по ключевым словам, выполняет его, считает статистику, строит график и шаблонную сводку. Поддерживает пять типов вопросов. Для доли возвратов шаблона нет. |
| Single | Одна LLM выбирает SQL, видит результат и ошибки инструмента, затем выдаёт тип графика, сводку и проверяемое числовое утверждение. Может исправить SQL, отдельного Critic нет. |
| MAS | Planner выдаёт подзадачи и допустимый порядок ролей. SQL Agent формирует запрос, общий инструмент выполняет его. Analyst использует статистику инструмента и формирует отчёт. Critic проверяет смысл SQL, график и сводку. При отказе Planner получает ошибки и строит новый план. |
MAS — последовательная команда с чёрной доской, без сложных фреймворков и параллелизма. Planner обязан соблюдать зависимости SQL, Analyst, Critic. При исправлении только отчёта он может пропустить SQL. В первой версии подзадачи сводятся к одному итоговому SQL, сложные соединения и подзапросы выполняет PostgreSQL. Это ограничение небольшого прототипа.
Чёрная доска — словарь question, schema, rules, plan, sql, rows, statistics, report, errors, history. Роли получают это состояние в JSON, результаты возвращаются в него. В results.json остаются полные запросы ролям, ответы, расход и время. Агентам не передаются эталонные запросы и результаты оценщика.
Искусственная база PostgreSQL: 48 заказов за январь–март 2026, 6 покупателей в 3 регионах, позиции заказов и возвраты. Есть отменённые заказы и несколько причин возврата одного заказа. Фикстура полностью воспроизводима, персональных данных нет.
| ID | Задача | График |
|---|---|---|
| T1 | Выручка завершённых заказов по месяцам | Линейный |
| T2 | Выручка по категориям товаров | Столбчатый |
| T3 | Средний чек по регионам | Столбчатый |
| T4 | Уникальные покупатели по регионам | Столбчатый |
| T5 | Общая выручка завершённых заказов | Столбчатый, одно значение |
| T6 | Доля уникальных завершённых заказов с возвратами по регионам | Столбчатый |
Все варианты видят одинаковые определения показателей. Возвраты в этой версии не вычитаются из выручки. Доход считается по завершённым заказам, возврат — отдельный признак.
- На одну задачу и вариант: до 12 вызовов модели, 16 000 входных + выходных токенов суммарно, до 700 выходных токенов на вызов, до 120 секунд и 2 попыток SQL/полного цикла MAS. Лимиты общие для всей команды MAS, не для каждой роли. Workflow использует нулевой LLM-бюджет.
- Допуск следующего вызова использует консервативный резерв по UTF-8-байтам промпта + 256 токенов накладных расходов + выходной лимит. Измеренный расход берётся из API usage, резерв не записывается как расход. Это приблизительный предохранитель, не серверная квота биллинга. Неиспользованный бюджет не требует обязательного расходования.
- Один запрос PostgreSQL ограничен 2 секундами и 500 строками. Агенты используют роль без прав записи и создания временных таблиц. Каждый запрос выполняется в транзакции READ ONLY с серверным statement_timeout. Один подготовленный SELECT оборачивается в подзапрос с LIMIT 501, после чего проверяется лимит 500 строк. Это учебная защита, не промышленная песочница произвольного SQL.
- Успех с первой попытки: внешний оценщик проверяет первый полный кандидат отчёта. Ошибка первого SQL или некорректный первый отчёт сразу означает неуспех первой попытки. У MAS кандидат фиксируется до Critic: если Critic исправляет ложное замечание, метрика первого кандидата всё равно отражает его фактическую корректность.
- Итоговый успех: завершённый отчёт после разрешённых исправлений. У MAS Critic должен принять ответ. Незавершённый прогон из-за лимита входит как провал.
- Таблица должна совпасть с отдельным эталонным SQL: метки и числа, допуск
1e-6, порядок неважен. Затем проверяются тип графика, наличие сводки и числового утверждения, подтверждённого строкой таблицы. Статистику и график строят общие инструменты. - Проверка текста ограничена: код не доказывает смысловую верность всей сводки. Для настоящего исследования участники вручную оценивают соответствие вопросу, подписи и числовые утверждения. Процент успеха здесь прежде всего метрика таблицы и структуры отчёта.
- Время измеряется от старта варианта до остановки, включая неудачные вызовы, переделки, SQL и подготовку отчёта. Внешняя оценка и сохранение файлов после решения не входят. Выводятся среднее и медиана, неудачи не исключаются.
- Варианты запускаются в перемешанном порядке с seed 42. Это seed порядка запуска, не гарантия одинаковых ответов LLM. Шесть задач и один demo-повтор не позволяют делать статистические выводы.
| Вариант | Первый успех | Итоговый успех | Вызовы заглушки |
|---|---|---|---|
| Workflow | 5/6 | 5/6 | 0 |
| Single | 4/6 | 5/6 | 13 |
| MAS | 4/6 | 6/6 | 30 |
Фактические локальные времена лежат в отчёте. Все токены и стоимость API равны нулю, потому что LLM не вызывалась. Вызовы заглушки не являются API-запросами.
В demo оба агентных варианта сначала получают T3 с неверным полем amount и T5 без фильтра status. Single исправляет видимую ошибку PostgreSQL в T3, но принимает неверный по смыслу ответ T5. Demo-Critic проверяет фильтр статуса и отправляет T5 на переделку. Его правило намеренно узкое и не доказывает универсальной проверки SQL. Для T6 Single и MAS получают готовый ответ из публичных шаблонов, а workflow намеренно ограничен пятью типами. Поэтому это заданный учебный сценарий, а не независимый тест обобщения.
- Выбрать точный ID одной доступной модели и записать тарифы и дату в результаты.
- Выполнить
--backend live --repeat 5: 6 задач × 3 варианта × 5 повторов = 90 запусков, из них 60 агентных, 30 workflow. - Сохранить отдельный отчёт, вручную проверить сводки и заменить demo-таблицу в презентации реальными измерениями.
- Расширить набор до 15–20 скрытых перефразированных вопросов. Добавить обоснование отказа Critic и сравнить MAS без Critic при том же общем бюджете.
run_local.sh — запуск без Docker. scripts/setup_local.sql — настройка отдельной локальной базы. salesbench/config.py — чтение .env. Dockerfile и .dockerignore — образ приложения. compose.yaml — приложение, PostgreSQL и тесты в Docker. db/ — роли и учебные данные. requirements.txt — psycopg 3. init_db.py — необязательная инициализация своего сервера. salesbench/database.py — подключение. main.py — запуск и протокол. salesbench/data.py — данные и задания. templates.py — workflow и учебные ответы demo. backend.py — адаптер модели и бюджет. architectures.py — три варианта. tools.py — SQL, статистика и график. evaluate.py — изолированный эталон. reporting.py — CSV, JSON, HTML. tests/ — проверки исправлений, скрытой ошибки, лимитов, API-учёта и режима чтения.
Личный вклад участников нельзя восстановить из автоматически подготовленного каркаса. В презентации указано предлагаемое распределение. Перед 22 октября подтвердите выполненные действия и замените соответствующий слайд фактическим вкладом каждого.