Проверяем Specification Frame на реальной задаче

Зачем это нужно
Запрос «напиши функцию импорта платежей из CSV» выглядит понятным, но в нём нет почти ничего важного: как хранить суммы, что делать с дублями, какую ошибку вернуть на битой строке и как понять, что код готов. Модель заполняет эти пробелы сама. Код получается правдоподобным, запускается и при этом может нарушать требования, о которых вы просто не написали.
Specification Frame — это способ структурировать промпт в четыре блока: контекст, ограничения, контракт результата и проверки. В этой статье мы возьмём одну учебную задачу, сформулируем её двумя способами — коротко и через рамку — и сравним ответы по одному и тому же чек-листу. В итоге у вас будет шаблон промпта и чек-лист, которые можно переиспользовать на своих задачах.
Важно: задача ниже — пример, придуманный для статьи. Мы не приводим «замеров» и процентов улучшения: результат зависит от модели, её версии и настроек. Вместо этого вы проведёте сравнение сами и запишете собственные наблюдения.
Учебная задача
Нужна функция на Python, которая читает CSV-файл с платежами и возвращает итог по каждому клиенту. Формат файла (пример):
payment_id,customer_id,amount,currency
p-001,c-10,1250.50,RUB
p-002,c-11,99.99,RUB
p-001,c-10,1250.50,RUB
p-003,c-10,abc,RUB
В этих четырёх строках уже спрятаны три ловушки: дубль p-001, некорректная
сумма abc и денежные значения, которые опасно считать через float.
Короткий промпт ни одну из них не упоминает.
Шаг 1. Подготовьте рабочую папку
Все команды безопасны: они создают файлы только внутри новой папки и ничего не устанавливают глобально.
mkdir sf-check && cd sf-check
python3 -m venv .venv
source .venv/bin/activate
python -m pip install pytest
Сохраните пример выше в файл payments.csv:
cat > payments.csv <<'EOF'
payment_id,customer_id,amount,currency
p-001,c-10,1250.50,RUB
p-002,c-11,99.99,RUB
p-001,c-10,1250.50,RUB
p-003,c-10,abc,RUB
EOF
Шаг 2. Вариант A — короткий промпт
Отправьте модели буквально такой запрос, в новом диалоге без предыстории:
Напиши функцию на Python, которая считает сумму платежей
по каждому клиенту из CSV-файла.
Сохраните ответ в variant_a.py, не редактируя его.
Шаг 3. Вариант B — промпт по Specification Frame
Теперь та же задача, но разложенная на четыре блока. Откройте ещё один новый диалог с той же моделью:
<context>
Сервис сверки платежей. На вход приходит CSV с колонками
payment_id, customer_id, amount, currency. Файл формирует внешняя
система, она иногда повторяет строки и присылает мусор в amount.
Python 3.11, только стандартная библиотека.
</context>
<constraints>
- Деньги не храни во float: используй decimal.Decimal.
- Строки с одинаковым payment_id считай одним платежом
(учитывай первую встреченную).
- Принимай только currency = RUB; другие валюты — ошибка строки.
- Некорректные строки не роняют обработку: собирай их в список ошибок.
- Не используй сторонние пакеты.
</constraints>
<output_contract>
Функция: summarize(path: str) -> tuple[dict[str, Decimal], list[str]]
- первый элемент: customer_id -> сумма, округлённая до 0.01;
- второй элемент: сообщения об ошибках вида "line N: причина",
где N — номер строки файла, считая заголовок первой строкой.
Верни один файл summarize.py и отдельно файл test_summarize.py.
</output_contract>
<checks>
Тесты pytest должны покрывать: дубль payment_id, нечисловую сумму,
чужую валюту, пустой файл с одним заголовком, точность 0.1 + 0.2.
Перед ответом перечисли, какое требование закрывает каждый тест.
</checks>
Сохраните код в summarize.py и тесты в test_summarize.py.
Шаг 4. Запустите проверки
Сначала прогоните тесты, которые написала сама модель:
python -m pytest -q test_summarize.py
Тесты от модели — не доказательство. Добавьте свою независимую проверку на нашем файле. Это ожидаемое поведение по спецификации, а не результат какого-то прогона:
cat > test_independent.py <<'EOF'
from decimal import Decimal
from summarize import summarize
def test_sample_file():
totals, errors = summarize("payments.csv")
assert totals == {"c-10": Decimal("1250.50"), "c-11": Decimal("99.99")}
assert len(errors) == 1
assert errors[0].startswith("line 5:")
EOF
python -m pytest -q test_independent.py
Для варианта A такой тест обычно нельзя запустить напрямую: у функции другое имя и другая сигнатура. Это уже наблюдение — без контракта результата ответ нельзя проверить автоматически. Оцените вариант A вручную по чек-листу ниже.
Шаг 5. Оцените оба ответа по чек-листу
Каждый пункт — «да» или «нет», без «почти». Чек-лист воспроизводим: другой человек, глядя на тот же код, должен поставить те же отметки.
| # | Критерий | Как проверить | A | B |
|---|---|---|---|---|
| 1 | Суммы не проходят через float | Поиск float( в коде; есть ли Decimal | ||
| 2 | Дубль payment_id учтён один раз | Итог c-10 равен 1250.50 | ||
| 3 | Битая строка не роняет обработку | Функция завершается на payments.csv | ||
| 4 | Ошибка содержит номер строки | Сообщение начинается с line 5: | ||
| 5 | Чужая валюта отклоняется | Добавьте строку с USD и запустите | ||
| 6 | Сигнатура совпадает с контрактом | summarize(path) -> (dict, list) | ||
| 7 | Только стандартная библиотека | Нет импортов pandas и т. п. | ||
| 8 | Тесты запускаются и проходят | python -m pytest -q |
Запишите итог как «N из 8» для каждого варианта, вместе с названием модели и датой. Если повторяете эксперимент, делайте это в новых диалогах: история переписки влияет на ответ.
Как собрать рамку для своей задачи
- Контекст — кто будет использовать результат, откуда берутся данные, версия языка и окружения. Только то, что меняет решение.
- Ограничения — запреты и обязательные правила. Хороший признак: каждое ограничение можно проверить по коду.
- Контракт результата — точные имена, типы, формат ошибок, список файлов. Это то, на что опирается ваш независимый тест.
- Проверки — какие случаи должны быть покрыты и как доказать соответствие. Попросите модель связать каждый тест с требованием.
Практическое правило: каждый пункт чек-листа должен вытекать из какого-то пункта рамки. Если в чек-листе есть критерий, которого нет в промпте, — вы проверяете то, чего не просили.
Типовые ошибки
- Размытые ограничения. «Обработай ошибки корректно» не проверяется. «Собирай ошибки в список строк вида line N: причина» — проверяется.
- Доверие тестам модели. Модель может написать тесты под свою же ошибку. Держите хотя бы один тест, написанный вами по спецификации.
- Сравнение в одном диалоге. Вариант B, отправленный после варианта A, получает подсказки из истории. Каждый вариант — в новом диалоге.
- Перегруженный контекст. Длинное описание бизнеса без влияния на код размывает важные правила. Если пункт не меняет реализацию, уберите его.
- Правка ответа до оценки. Сначала оцените ответ как есть, потом исправляйте. Иначе сравнение теряет смысл.
Ограничения подхода
- Один прогон на одной задаче — это наблюдение, а не статистика. Ответы модели недетерминированы; для выводов повторите сравнение несколько раз и на разных задачах.
- Рамка не исправляет ошибки в самой спецификации. Если вы неверно описали правило дублей, модель аккуратно реализует неверное правило.
- Для совсем мелких задач (переименовать переменную, отформатировать JSON) полная рамка избыточна — хватит одной строки ограничений.
- Чек-лист проверяет только то, что в нём перечислено. Производительность, безопасность и сопровождаемость требуют отдельных критериев.
Что дальше
Сохраните шаблон рамки и чек-лист рядом с проектом и используйте их как стартовую точку для следующих задач. Больше практических разборов — в разделе руководств, а определения терминов из статьи — в глоссарии.