Перейти к содержимому

Кейс: Codex и GPT-5.6 Sol Ultra написали библиотеку за вечер — alchemy-utils

12 августа 2026 года Simon Willison — автор sqlite-utils и Datasette — поручил Codex и модели GPT-5.6 Sol Ultra собрать кроссплатформенную версию своей библиотеки для работы с базами данных. За вечер агент выдал проект alchemy-utils: поддержка PostgreSQL, DuckDB и SQLite через SQLAlchemy, 164 прошедших теста и готовый CLI. Первый запуск импорта CSV занял почти час — после просьбы оптимизировать Codex сократил его до ~35 секунд. Ниже сам кейс: промпты, решения агента, цифры и границы применимости.

Это перевод-адаптация поста Simon Willison — alchemy-utils 0.1a0 (12.08.2026) и опубликованного им транскрипта сессии Codex.

Зачем Уиллисону библиотека на SQLAlchemy

sqlite-utils — библиотека и CLI Уиллисона для работы с SQLite: создание таблиц, вставка, upsert, интроспекция схемы. Привязка к одному движку ограничивает её там, где данные лежат в PostgreSQL или DuckDB. Идея сделать такой же API поверх SQLAlchemy, чтобы один и тот же код работал на разных движках, висела у автора давно:

I’ve long pondered what a database agnostic version of my sqlite-utils Python library and CLI utility might look like. This morning (literally a shower project) I tasked Codex and GPT-5.6 Sol Ultra with building a prototype.

«Я давно размышлял, как могла бы выглядеть не привязанная к конкретной БД версия моей библиотеки sqlite-utils. Этим утром (буквально проект из душа) я поручил Codex и GPT-5.6 Sol Ultra собрать прототип.» (Simon Willison, 12.08.2026)

Для тех, кто встречает такие кейсы впервые: Codex CLI — официальный терминальный агент OpenAI (Apache-2.0), модели по умолчанию — семейство GPT-5.6 (Sol/Terra/Luna). Установка, вход и модели разобраны в нашем гиде по Codex CLI. В этом кейсе Уиллисон использовал конфигурацию Sol Ultra с максимальной глубиной рассуждений — именно на ней он гоняет тяжёлые агентные задачи.

Один промпт вместо ТЗ

Задачу Уиллисон сформулировал одним промптом:

Do a research spike to see what it would take to build a library with the same core API as SQLite-utils - in particular the insert and upsert and insert_all and upsert_all and create and update methods, and the table introspection stuff - but backed by SQLalchemy so it works for multiple database engines
Test against PostgreSQL and SQLite and duckdb
Use ~/dev/sqlite-utils for reference
Create a git repo for this and commit and early and often - use uv init to start the project - use red/green TDD and pytest, see ~/dev/django-sql-dashboard for one idea as to how the PostgreSQL tests could work

«Проведи research-spike: оцени, что нужно для библиотеки с тем же ядром API, что у SQLite-utils, — прежде всего методы insert, upsert, insert_all, upsert_all, create, update и интроспекцию таблиц, — но на SQLAlchemy, чтобы она работала с несколькими движками БД. Проверяй на PostgreSQL, SQLite и DuckDB. Используй ~/dev/sqlite-utils как референс. Создай git-репозиторий и коммить часто и пораньше; проект начни через uv init; используй red/green TDD и pytest; идею тестов под PostgreSQL посмотри в ~/dev/django-sql-dashboard.» (транскрипт сессии)

В промпте шесть требований, и каждое проверяемо: референс (локальный checkout sqlite-utils), движки (три), способ ведения проекта (uv + git), методология (red/green TDD + pytest) и образец для тестов PostgreSQL. Это готовый шаблон для собственных задач: вместо «напиши библиотеку» — «сделай X, проверь на Y и Z, следуй процессу W, ориентируйся на A».

Скриншот поста Simon Willison о релизе alchemy-utils 0.1a0
Оригинальный пост Release: alchemy-utils 0.1a0 на simonwillison.net: «проект из душа», пример вывода CLI и оптимизация CSV-импорта (скриншот сделан для этой статьи).

Что агент сделал сам, без уточнений

Транскрипт сессии — отдельный gist, последнее обновление 12 августа в 19:44; в начале видно «32 previous messages», то есть опубликована не вся сессия, а её концовка. По записи видно, что из одного промпта Codex развернул целый процесс:

  • инициализация: uv init --lib + git init, первые коммиты «Initial uv library scaffold» и «Add SQLAlchemy and database test dependencies» (SQLAlchemy 2.x, duckdb-engine, psycopg, pytest, testing.postgresql);
  • изучение референса: grep по db.py sqlite-utils — сигнатуры create/insert/upsert/update, классы Database/Table/View, дата-классы Column/ForeignKey/Index;
  • red/green циклы: сначала тест (tests/test_crud.py), потом реализация, запуск pytest, коммит;
  • первый прогон на трёх движках: 18 кейсов CRUD зелёные везде.

Уже в первых выводах агент флагнул проблему, которую человек мог бы пропустить:

One important design constraint is already visible: the natural sqlalchemy-utils name collides with the established third-party package; I’ll keep that provisional name for the spike and call it out explicitly in the findings rather than silently treating it as publishable.

«Уже видно важное ограничение: естественное имя sqlalchemy-utils конфликтует с существующим сторонним пакетом. Оставлю его как рабочее название для спайка и явно отмечу это в выводах, а не буду молча считать его публикуемым.» (транскрипт сессии)

Затем — запрос на рефакторинг: «Refactor the code so engine-specific portability stuff lives in a file for that specific engine — maybe a Database subclass is the best way to do that» («вынеси зависящий от движка код в файл под этот движок — возможно, лучше всего через подкласс Database»). Агент согласился и развёл три реализации — об этом следующий раздел.

Скриншот транскрипта сессии Codex в gist: исходный промпт и ход работы
Транскрипт Codex 5.6 Sol Ultra transcript: первый промпт, ответ агента («рабочий спайк, а не меморандум») и записи команд — uv init, git commit, pytest (скриншот сделан для этой статьи).

Что получилось: API и CLI

К концу сессии библиотека повторяет стиль sqlite-utils «сначала таблица»: методы возвращают тот же объект Table, так что цепочки вызовов пишутся естественно. Пример из README:

from alchemy_utils import Database

db = Database("sqlite:///:memory:")

people = db["people"].insert(
    {"id": 1, "name": "Ada", "profile": {"language": "Python"}},
    pk="id",
)
people.upsert({"id": 1, "name": "Ada Lovelace"})
people.insert_all([{"id": 2, "name": "Grace"}, {"id": 3, "name": "Katherine"}])
people.update(2, {"name": "Grace Hopper"})

assert people.pks == ["id"]
assert people.columns_dict["profile"] is dict
assert people.get(1)["name"] == "Ada Lovelace"

Движок меняется одной строкой — только URL:

postgres = Database("postgresql+psycopg://user:password@localhost/app")
duckdb = Database("duckdb:///analytics.duckdb")
Скриншот репозитория simonw/alchemy-utils на GitHub
Репозиторий simonw/alchemy-utils: README с примером API, лицензия Apache-2.0, на момент публикации 38 коммитов (скриншот сделан для этой статьи).

CLI даёт те же операции из терминала: create-table, insert, upsert, update, а для чтения — tables, views, schema, columns, indexes, foreign-keys, rows, get, count. Ввод — JSON, JSONL, CSV, TSV, файлы или stdin. Уиллисон показал однострочник для своей базы блога в локальном PostgreSQL:

uvx --with 'alchemy-utils[postgresql]' alchemy-utils rows 'postgresql+psycopg://simon@localhost:5432/simonwillisonblog' redirects_redirect

Три движка — три набора граблей

Самый ценный материал кейса — где именно SQLAlchemy не спасает. В RESEARCH.md Уиллисон честно разложил, что переносимо, а что пришлось адаптировать.

Проблема первая: upsert не входит в общий API SQLAlchemy. insert().on_conflict_do_update() есть только у диалектных конструкций (PostgreSQL, SQLite); DuckDB понимает синтаксис PostgreSQL и использует ту же форму. Вторая: DuckDB через duckdb-engine 0.17.0 не отдаёт первичные ключи при рефлексии, JSON видит как VARCHAR, список индексов пустой, счётчик обновлённых строк не сходится. Третья: сгенерированные первичные ключи — duckdb-engine наследует DDL от PostgreSQL и рендерит SERIAL, который DuckDB не принимает; решение — DuckDB Sequence как server default.

The prototype proves the requested surface on SQLite, PostgreSQL, and DuckDB.

«Прототип доказывает требуемую поверхность API на SQLite, PostgreSQL и DuckDB» — и дальше по тексту: портируемый контракт нужно явно определить там, где sqlite-utils сегодня завязан на специфику SQLite. (RESEARCH.md)

Схема архитектуры alchemy-utils: фабрика Database выбирает движок, общий API Table делегирует диалектные решения
Архитектура alchemy-utils по README и RESEARCH.md: общий API Table не знает имён диалектов — конфликты, генерацию ключей и рефлексию решают подклассы Database под SQLite, PostgreSQL и DuckDB. Собственная схема.

TDD как способ разговора с агентом

Ключ к воспроизводимости кейса — требование red/green в промпте. Каждая фича начинается с падающего теста, который фиксирует контракт, затем идёт минимальная реализация до PASS и коммит.

Схема цикла red/green/refactor, заданного в промпте
Цикл из промпта Уиллисона: тест → FAIL (red) → минимальная реализация → PASS (green) → чистка и коммит. Итог сессии: 164 теста PASS, 1 ожидаемый skip, ruff clean, 38 коммитов в репозитории. Собственная схема.

Тесты защищают и самого агента: после переименования проекта полный прогон (pytest -q, ruff check) подтверждал, что ничего не сломалось. Финальная проверка включала сборку wheel, установку в чистую Python 3.10-окружение и round-trip create/insert/read через CLI — это уже сценарий релиза, а не просто «тесты зелёные».

Follow-up промпты и оптимизация CSV

«Very few follow-up prompts» — Уиллисон не публиковал каждый из них, но в транскрипте видны минимум четыре содержательных:

  1. рефакторинг — разнести движкозависимый код по подклассам Database (раздел выше);
  2. CLI — добавить консольные команды create-table, insert, upsert, update, rows, schema, indexes с чтением JSON/JSONL/CSV/TSV и stdin;
  3. переименование — «Let’s rename this to alchemy-utils»: агент сам проверил PyPI (404) и переименовал пакет, модуль, CLI и все тесты;
  4. дебаг-вопрос — «Why does this break? uv run alchemy-utils schema ‘duckdb:///test.duck’». Агент воспроизвёл команду, выяснил, что базы test.duck на месте нет (DuckDB молча создал пустую), и сам признал причину: при уборке репозитория случайно перенёс файл базы. Вернул его (780 КБ) и честно написал:

Sorry—I mistook your database for a generated test artifact.

«Прости — я принял вашу базу за сгенерированный тестовый артефакт.» (транскрипт сессии)

Эпизод показательный: агент не только нашёл собственный баг, но и не стал о нём молчать. Для пользователей таких инструментов это аргумент в пользу открытых журналов действий.

Последний штрих кейса — производительность. Уиллисон показал однострочник импорта каталога деревьев Сан-Франциско в DuckDB:

curl 'https://raw.githubusercontent.com/simonw/sf-tree-history/refs/heads/main/Street_Tree_List.csv' | uvx --with 'alchemy-utils[duckdb]' alchemy-utils insert 'duckdb:////tmp/trees.db' trees - --csv

That took nearly an hour the first time I ran it, so I had Codex optimize it and got it down to around 35 seconds.

«В первый раз это заняло почти час, поэтому я попросил Codex оптимизировать — он сократил время примерно до 35 секунд.» (Simon Willison, 12.08.2026)

Скриншот коммита оптимизации импорта CSV
Коммит Speed up bulk CSV inserts: 6 файлов, +311/−94 строки; CSV и TSV пишутся порциями по 100 записей (скриншот сделан для этой статьи).

Сократить время с часа до 35 секунд удалось за счёт пакетной записи: CSV и TSV читаются и вставляются порциями — по умолчанию 100 записей, размер меняется флагом --batch-size.

Что получилось к концу вечера

Итог — библиотека alchemy-utils (Apache-2.0), релиз 0.1a0 12 августа в 19:51, в репозитории 38 коммитов.

Показатель Значение
Промптов 1 исходный + «очень немного» follow-up (в транскрипте видны 4 содержательных)
Коммитов 38, почти все — агент
Тестов 164 PASS + 1 expected skip (partial index DuckDB), ruff clean
Движки SQLite, PostgreSQL (testing.postgresql), DuckDB 1.5.5
Релиз 0.1a0 (pre-release), 12.08.2026 19:51
Оптимизация импорт CSV: ~1 час → ~35 сек
Стоимость токенов не опубликована

Последняя строка — про деньги — важная оговорка. В отличие от прошлого кейса Уиллисона с Codex Desktop, где он публиковал оценку AgentsView ($23,28 за 52-минутную сессию; разбор — в кейсе про игру), в этом посте точные затраты на токены не названы. Прикинуть бюджет можно по правилу «длинная агентная сессия по API — десятки долларов», но цифра за этим конкретным вечером остаётся неизвестной.

Таймлайн вечера 12 августа: от промпта до релиза
Ход сессии по транскрипту и посту: один промпт → скелет и зависимости → ядро API → follow-up (рефакторинг, CLI, переименование, дебаг) → оптимизация CSV и релиз 0.1a0 в 19:51. Собственная схема.

Как это повторить

Рецепт из кейса переносится на обычные задачи:

  • Промпт-ТЗ вместо промпта-пожелания. Шесть пунктов Уиллисона закрывают всё: цель, референс, критерии проверки (движки), процесс (TDD, git), инструменты (uv), образец (django-sql-dashboard).
  • Red/green как страховка от «красивого, но сломанного». Падающий тест фиксирует контракт до реализации; агент не может «забыть» требование, если оно записано в тесте. В кейсе про легаси тесты называли главным инструментом, но и главной ловушкой — валидность тестов всё равно проверяет человек.
  • Follow-up вместо одного гигантского запроса. Библиотека не вышла за один промпт: рефакторинг, CLI, переименование и дебаг пришли отдельными короткими вопросами. Агент на сложной задаче работает итерациями, когда каждый шаг проверяем.
  • Открытый журнал действий. Транскрипт и коммиты показывают, что агент решал сам и где ошибался — включая собственную ошибку с test.duck.

Что кейс даёт и чего не даёт

Ограничения стоит назвать так же прямо, как это сделал сам автор.

  • Это спайк, а не обещание совместимости. В README это первая строка: «An executable research spike… This is a spike, not a published compatibility promise». Ряд функций sqlite-utils не перенесён вовсе: hash_id, extracts, conversions, FTS, полные transforms.
  • Память и типовая адаптация — в задел. Bulk-вставка материализуется в памяти (batch_size принят, но потоковой записи пока нет); alter=True добавляет только nullable-колонки; DuckDB expression-indexes разбираются «best-effort».
  • Оценка production. В RESEARCH.md — прямо: довести ядро до документированного v0.1 — примерно 3–5 недель работы опытного инженера; полный паритет с sqlite-utils — «многомесячная работа». Вечер агента — это прототип и контракт API, а не готовая библиотека.
  • Стоимость не раскрыта, репозиторий маленький (5 звёзд на момент публикации), API без стабильных гарантий — использовать на проде рано.

Российские реалии

Кейс строится на трёх слоях, и доступность у них разная.

Codex и модели OpenAI. Вход и оплата — главные ограничения из РФ. OpenAI блокирует доступ с российских IP с декабря 2022 года: для входа в Codex нужен VPN с иностранным IP («ChatGPT: как пользоваться нейросетью в России»). Платные подписки российскими картами не оплатить: рабочие схемы — зарубежная виртуальная карта, карты банков Казахстана/Грузии/Армении, посредники; OpenAI отклоняет карты и может блокировать аккаунт при входе с российского IP (личный опыт с суммами). Бесплатный план Codex в ChatGPT не требует карты и хватает на десятки небольших задач в неделю. Есть и обходной путь — переключить Codex CLI на российский инференс (разбор подключения к API Яндекса). Свежее сравнение Codex, Claude Code и Kimi на одной задаче — Habr, 4 августа 2026.

Сам alchemy-utils и его окружение. Библиотека открытая (Apache-2.0) и живёт на GitHub и PyPI — оба доступны из РФ без VPN. SQLAlchemy, DuckDB, SQLite, psycopg — тоже открытые пакеты. То есть повторить сам кейс («написать библиотеку на SQLAlchemy») из России можно полностью: мешает только доступ к агенту, а не к инструментам.

Русскоязычных кейсов «агент написал библиотеку за вечер» на момент публикации не нашлось — тема свежая, и этот материал по сути один из первых разборов на русском. Ближайшее, что есть в рунете, — гайды по установке и настройке Codex CLI (Habr) и общие сравнения агентов. Честно: механика кейса от страны не зависит, а вот воспроизводимость на платном доступе упирается в оплату.

Вывод

Один продуманный промпт дал за вечер рабочий прототип библиотеки с 164 тестами и CLI: процесс (референс, движки, red/green, uv+git) оказался важнее объёма текста в задании. Ценность кейса не в «ИИ написал библиотеку», а в том, что границы возможного видны изнутри: агент сам нашёл конфликт имён, сам починил собственный баг и сам ускорил импорт примерно в 100 раз — а довести результат до продакшена всё равно нужно человеку на 3–5 недель. Это реалистичная картина агентного кодинга в 2026-м: скорость прототипирования — да, гарантии зрелости — нет.

По серии: общая вводная — «Что такое агентный кодинг», установка и настройка Codex CLI — «Codex CLI от OpenAI», сравнение агентов — «Лучший ИИ-агент для кодинга в 2026», смежные кейсы — «Переписываем легаси с ИИ-агентами» и «Игра с ИИ-агентом за час».

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка / 5. Количество оценок:

Оценок пока нет. Поставьте оценку первым.

Добавить комментарий

Adblock
detector