Нейросеть для кодинга: лучшие модели на одной функции
Лучшие модели нейросетей для кодинга: сравнение назначений Claude и OpenAI, настоящий ответ LibraChat и проверка функции на повторах, пустом списке и типах.
Д
Денис
Продакт LibraChat
12 мин чтения
В Python проверка `isinstance(True, int)` даёт `True`. Для моей функции это был бы неверный пропуск: логические значения я запретил, хотя обычные целые числа разрешил. Нейросеть LibraChat вернула код с более строгой проверкой и выдержала этот вход. Лучшие модели для кодинга я теперь отбирал бы через такие конкретные требования. Для узкой операции в пробу можно включить Haiku 5.5 или Luna; для обычного рабочего кода — Sonnet 5.5 или Sol. Но сначала прочитаем, что именно должен сохранить любой кандидат.
У меня есть один фактический ответ и журнал его локального запуска. Вместо длинного списка чужих баллов я пройду по этому коду: типы, отдельные коллекции, разрывы и завершение диапазона. В каждом месте есть причина принять результат или вернуть его на исправление. После чтения станет понятнее, с какой парой моделей продолжать и почему более тяжёлый вариант пока может не понадобиться.
Один ответ, который помещается целиком
Функция должна собрать соседние целые числа в диапазоны. Например, неупорядоченный список с повторами `[-2, 2, -1, 1, 2, 4]` превращается в `[(-2, -1), (1, 2), (4, 4)]`. Это авторское упражнение, для него не нужен доступ к рабочему репозиторию. Вход и ожидаемый выход известны заранее.
Вот постановка, которую я действительно отправил в LibraChat:
Напиши только Python-код одной чистой функции consecutive_ranges(values), без импортов, запуска, файлов и пояснений. Вход — список целых чисел. Верни отсортированный список кортежей (начало, конец) для максимальных последовательностей соседних целых. Повторы не создают новых диапазонов. Отрицательные числа допустимы. Пустой список даёт []. Исходный список не менять. При любом элементе, у которого type(x) is not int (включая bool), подними TypeError. Например [-2, 2, -1, 1, 2, 4] должно дать [(-2, -1), (1, 2), (4, 4)].
Полученный код оставляю без редакторских исправлений:
Такую небольшую постановку можно прямо сейчас дать чату LibraChat со своими условными числами. Важны именно ограничения: что считать допустимым входом, что вернуть и что сохранить. Просьба «напиши аккуратно» этих условий не заменяет. Она не сообщит модели, что делать с повтором или логическим значением.
Я сначала прочитал ответ на предмет действий, которых не заказывал. В функции нет импортов, файловых операций и обращений к сети. Затем запустил её локально на синтетических данных. Это был опыт с одной функцией; IDE, большой проект и работу других перечисленных моделей он не измерял. Теперь разберу саму запись, прежде чем обращаться к каталогу кандидатов.
Сначала читаю строчку с type(x)
Первая содержательная развилка находится до сортировки. Цикл обходит элементы и требует точный тип `int`. Для значения `True` условие выполняется, поэтому поднимается `TypeError`. Это соответствует моему запрету. Проверка через `isinstance` в данном случае означала бы другое правило: в Python `bool` является подтипом `int`.
Я отдельно сверил это поведение в используемом интерпретаторе. `isinstance(True, int)` оказался истинным, а `type(True) is int` — ложным. Здесь нет ошибки самого языка или универсально неправильного способа проверки. Различаются вопросы: допустим ли подтип или требуется именно указанный тип. Решение принимает контракт функции. В моём запросе это было записано прямо.
После такого чтения я могу назвать причину отклонения будущего ответа одной фразой: «Принят запрещённый логический элемент». Это гораздо полезнее замечания «код мне не нравится». Модель может предложить красивую реализацию, которая последовательно выполняет другое правило. Пока не определено правило, сравнение красоты не приведёт к пригодной функции.
Та же проверка объясняет порядок операций. Сначала выясняется, допустимы ли все элементы. Только потом создаётся множество и появляется сортировка. Если перепутать смысл этих этапов, можно начать обрабатывать значение как число раньше, чем решён вопрос о его допустимости. В полученном коде отказ происходит в начале. Это видно и по записи, и по сохранённым результатам запусков.
Я проверил ещё маленькое свойство самого Python, отдельно от функции: множество из `[1, True]` в моём интерпретаторе содержит один элемент `1` с типом `int`. Здесь важна потеря различия во входных данных. Если сначала убрать повторы, а уже потом искать запрещённый тип, можно не увидеть логическое значение, которое человек действительно передал. У исходной последовательности требований порядок имеет значение.
Этот опыт не является вторым ответом нейросети. Это моя локальная проверка операции, которая уже присутствует в полученном коде. Она помогает объяснить, почему я читаю валидацию раньше строки с множеством. Одно дело — получить правильный список диапазонов для допустимого входа. Другое — исполнить обязанность отказаться от любого входа с запрещённым элементом, даже если после преобразования он мог бы выглядеть обычным числом.
Так появляется более точное замечание к будущему кандидату. Я проверяю не только наличие проверки типа, но и то, не исчезла ли к этому моменту существенная часть входа. Похожее требование бывает и при чистке текста или нормализации записей: преобразование способно скрыть то, что следовало заметить до него. В этой функции такой порядок можно увидеть буквально по нескольким соседним строкам.
Копия и набор делают разную работу
В строке `items = list(values)` создаётся отдельный список. Дальше `set(items)` убирает повторы, а `sorted(...)` возвращает упорядоченную последовательность. Я не объединяю эти действия словом «подготовка»: они отвечают за разные части условия. Повторы нельзя превращать в дополнительные диапазоны, порядок результата должен быть определён, а исходник нужно сохранить.
Само наличие копии ещё не заменяет чтение остальных строк. Я смотрю, к какой коллекции обращается каждая операция и где она создаёт новый результат. В этой реализации исходный `values` не сортируется на месте. Журнал тестов подтверждает, что переданные списки сохранили содержимое после вызова. Это проверенный эффект данного кода, а не обещание, которое даёт одна знакомая строка с `list`.
При гарантированном входном списке первая копия также является поводом отдельно обсудить расход памяти. В показанном цикле `items` не меняется; затем всё равно создаются множество и отсортированный список. Я не переписывал функцию перед публикацией и не измерял, сколько памяти или времени она сэкономила бы после изменения. Принять результат по заданному контракту и сделать его экономнее — разные редакции кода.
Эта разница пригодится при сравнении ответов. Более короткий вариант не обязательно бережнее обращается с данными. Более длинный не обязательно ошибочен. Сначала я определяю, откуда берётся нужное свойство: новая коллекция, отказ от изменения исходника или что-то ещё. Если не могу объяснить это по строкам, просить новую модель «оптимизировать» пока рано.
Для подробного чтения незнакомого фрагмента можно использовать подход из материала об анализе кода с нейросетью. Здесь полезен узкий вопрос: «Покажи, какие объекты меняются и какой переданный список останется прежним». Я бы держал его отдельно от просьбы сократить весь код, чтобы сначала получить объяснение эффекта.
Диапазон заканчивается не там, где кончился цикл
После сортировки `start` и `prev` хранят начало и конец ещё не завершённого диапазона. Пока следующее число равно `prev + 1`, конец сдвигается. При разрыве накопленная пара добавляется в результат, а новое число становится началом следующего диапазона. На нашем входе переход от минус одного к единице — как раз разрыв, через который нельзя протянуть один общий интервал.
Но добавления внутри `else` недостаточно. Последний диапазон может продолжаться до самого конца последовательности и больше не встретить разрыва. Для него после цикла стоит отдельный `result.append((start, prev))`. На входе из одного числа цикл вообще не выполняется, а эта строка всё равно возвращает нужную одиночную пару. Так конкретная строка объясняет сразу два крайних случая.
Пустой список обрабатывается ещё раньше. Возврат расположен до обращения к `nums[0]`, поэтому начальный элемент не пытаются взять у пустой последовательности. Я не искал здесь самый эффектный алгоритм. Мне важно проследить незавершённый диапазон до места, где он действительно попадает в результат, и отдельно увидеть путь без единого числа.
После чтения я оставил рядом с функцией три коротких вопроса:
Где заканчивается уже начатый диапазон при разрыве?
Где добавляется последний диапазон без нового разрыва?
Что произойдёт до первого обращения по индексу, если вход пуст?
Если другой кандидат пишет функцию иначе, эти вопросы всё равно остаются. Ответ может использовать другой цикл или иную организацию промежуточных данных. Я буду искать то же поведение, а не требовать буквального повторения показанного решения. Иначе получится конкурс похожести на первый ответ вместо сравнения пригодного кода.
Шесть списков в журнале и три отказа
Сохранённая локальная проверка содержит шесть допустимых входов. Для каждого я заранее задал результат и проверил, что исходный список не изменился. В таблице ниже показано именно полученное при выполнении этой функции.
Вход · Полученный и ожидаемый выход · Участок, который помогает проверить
`[]` — `[]` — Ранний возврат
`[3]` — `[(3, 3)]` — Завершение после пустого цикла
`[3, 3]` — `[(3, 3)]` — Удаление повтора
`[-2, 2, -1, 1, 2, 4]` — `[(-2, -1), (1, 2), (4, 4)]` — Сортировка, отрицательные числа и разрывы
`[0, -1, 1]` — `[(-1, 1)]` — Последовательность через ноль
Ещё три вызова содержали соответственно `True`, `1.5` и строку `'2'` среди целых элементов. Каждый завершился требуемым `TypeError`. Речь о контролируемом отказе, который был частью задания. Если считать любой отказ неудачей, мы поощрим функцию принимать запрещённые данные ради красивого числа успешных запусков.
Шесть примеров не доказывают пригодность для любого будущего проекта. Они сообщают, что произошло на показанных входах, и связываются с прочитанными ветками кода. Нагрузку, взаимодействие модулей или соблюдение чужого API этот журнал не содержит. Для следующей задачи я добавлю её собственные условия, сохранив уже найденные крайние случаи.
Чтобы сравнить две модели, я передал бы им одинаковый контракт, затем прочитал обе реализации и запустил один набор проверок. Если после первого отказа уточнение получил только один кандидат, это надо записать как дополнительную попытку. Удачный второй ответ нельзя молча поставить рядом с чужим первым и объявить итог чистым свойством модели.
Haiku, Luna, Sonnet, Sol: кому дать следующую версию
Теперь у меня есть основание выбирать стартовых кандидатов. Официальные руководства Anthropic и OpenAI, сверенные 11 ноября 2026 года, описывают разные роли моделей. Я сопоставляю эти роли с работой над кодом. Таблица задаёт пары для будущей пробы; результаты такой пробы здесь не выдумываются.
Кандидат · Документированный акцент · Зачем я рассмотрел бы его после чтения функции
Claude Haiku 5.5 — Низкая задержка и стоимость, много однотипных задач — Повторяемая небольшая операция с известными отказами
Luna, OpenAI — Ограниченные задачи и частые автоматизации — Чётко заданное преобразование, принимаемое одним набором входов
Claude Sonnet 5.5 — Повседневный код, сочетание скорости и возможностей — Новая функция или обычное исправление, которое нужно прочитать и проверить
GPT-6.1 Sol — Сложная работа с учётом времени и стоимости — Несколько требований, которые придётся согласовать в одном изменении
Claude Opus 5.5 — Длительная агентная разработка и сложные изменения — Связанные модули и большой рефакторинг с собственными регрессиями
Astra, OpenAI — Неоднозначные проблемы и глубокий анализ — Неясная причина сбоя и несколько конкурирующих объяснений
Для нашей функции я бы сначала попробовал узкого кандидата. Для обычного исправления — рабочую пару Sonnet и Sol. Opus или Astra рассматривал бы, когда сама постановка действительно требует более сложного разбора. Это авторский выбор начала, а не измеренное равенство моделей внутри каждой пары. Один ответ LibraChat не расставляет между ними места по качеству.
Отдельно я записал бы глубину рассуждения и точную версию. Изменение режима может повлиять на результат, поэтому его не стоит прятать за одним семейным названием. При повторе постановку оставляют прежней, а изменяемый фактор называют. Тогда после ошибки можно понять, помогла другая модель, другая настройка или просто новое существенное уточнение.
Перед платным продолжением нужно увидеть доступный продукт и текущие условия. В LibraChat состав доступа можно проверить через тарифы и меню моделей; перечисленные конкуренты не являются заявлением о составе этого меню. Для собственной пробы важны возможность выполнить запрос и полный расход до принятого кода, включая повторные попытки.
Из показанного ответа я сохраню связь требования со строкой:
запрет `bool` ведёт к проверке точного типа до множества;
пустой вход требует возврата раньше обращения по индексу;
последний диапазон получает своё добавление после цикла.
Когда следующий кандидат нарушит одно из этих условий, у меня будет конкретное замечание для него. С такого замечания и начинается полезный следующий ответ.