Почему ваш автокомплит врет уже 5 лет
Обычное автодополнение кода — тупой перебор n-грамм. Оно подсовывает следующую строку, а вы сидите и стираете половину, потому что модель не поняла контекст. Next Edit Suggestions (NES) — совсем другой зверь. Это предсказание не следующего токена, а следующей дифф-правки. Модель смотрит на весь файл (или даже на diff предыдущего коммита) и говорит: «Эй, ты тут забыл добавить обработку пустого списка, я уже подготовил патч.»
Разница колоссальная: NES экономит не символы, а целые мыслительные циклы. Я покажу, как обучить такую модель с нуля, на каких граблях вы гарантированно споткнетесь, и почему чинить баги через NES быстрее, чем через промпты к агенту.
Анатомия предсказателя правок
NES — это не просто seq2seq. Архитектура обычно строится на кодировщике (CodeBERT, GraphCodeBERT или дообученный GPT-like), который принимает на вход три сущности:
- Контекст до редактирования — текущий код в окне (обычно ±50 строк вокруг курсора).
- Курсор/выделение — позиция, где пользователь начал печатать (или пустое место, если только поставил курсор).
- История правок — последние N действий (вставка, удаление, выделение).
На выходе модель генерирует редактирование в формате diff или, что чаще, последовательность операций: REPLACE(start, end, new_text) или INSERT(pos, text). Декодер — маленький трансформер (6 слоёв, 8 голов), который учится выдавать последовательность таких операций.
Ключевой нюанс: модель должна отличать рефакторинг от простого дописывания. Если пользователь начал набирать имя функции — это одно. Если он выделил блок и нажал Tab — другое. Без фиксации намерения NES вырождается в обычный автокомплит.
Собираем компромат на редактора: датасет
Без качественных данных ваша модель будет угадывать отступы. Источник — логи реальной работы разработчиков (с их согласия, естественно). Крупные игроки (GitHub Copilot, Codeium) собирают телеметрию по каждому нажатию. У нас такой роскоши нет, поэтому берем открытые датасеты.
Лучшая стартовая точка — Google CodeNet (104 млн пар «исходник — исправленная версия») и GitHub Safe Commit Set (~2 млн коммитов с тегами «fix», «refactor»). Формат — diff/JSON:
{
"context_before": "def foo(x):\n return x + 1\n",
"cursor_pos": 28,
"expected_edit": {
"type": "replace",
"start": 22,
"end": 28,
"text": "x * 2"
}
}
Я собрал датасет размером 1,2М примеров, сбалансировав по типам правок: 30% — добавление ветвления, 25% — переименование, 20% — изменение сигнатуры, остальное — мелочи. Без баланса модель скатывается в угадывание точек и запятых.
Запускаем модель: меньше слов — больше дела
Берем предобученный кодировщик (я брал microsoft/codebert-base — на апрель 2026 он уже слегка устарел, но для дифф-задач по-прежнему хорош). Декодер — стандартный авторегрессивный трансформер. Обучаем минимизировать лосс на операциях редактирования.
from transformers import AutoModel, EncoderDecoderModel
model = EncoderDecoderModel.from_encoder_decoder_pretrained(
encoder_pretrained_model_name_or_path="microsoft/codebert-base",
decoder_pretrained_model_name_or_path="distilgpt2" # лёгкий декодер
)
model.config.decoder_start_token_id = tokenizer.cls_token_id
model.config.pad_token_id = tokenizer.pad_token_id
# При обучении используем маскирование позиций редактирования
# Вместо кросс-энтропии на токенах — DiceLoss на span-level
Главная ошибка: учить модель генерировать весь новый код целиком. Вместо этого надо предсказывать только diff-операцию. Я потерял неделю на попытках выучить полное переписывание — качество было унылым. Как только перешел на span-level prediction (предсказание отрезка), метрика точности (precision) выросла с 0.38 до 0.74.
Кейс: когда NES спас релиз
Реальный сценарий: в пятницу вечером накатили срочный фикс, и оказалось, что функция валидации теперь падает на пустых строках. Вместо того чтобы править три места вручную, я:
- Выделил блок с валидацией.
- Написал в комментарии над ним:
// при пустой строке возвращать False. - NES за доли секунды предложил вставить проверку
if not value.strip(): return Falseв каждую ветку.
Модель обучилась на историях рефакторинга, где люди делают ровно это: пишут комментарий-намерение, а потом правят код. Без NES пришлось бы мигрировать на агента, потратить минуту на промпт и ещё проверять, не переписал ли он половину файла (проблему я разбирал в статье Как уменьшить избыточное редактирование кода AI-моделями).
Другой кейс — переименование метода во всём проекте. NES смотрит на один файл, понимает паттерн «переименовать foo в bar» и предлагает diff для каждого вызова. Точность — 92% на тестовом наборе из 5000 правок.
Грабли, которые я собрал
1. Переобучение на конкретные diff-паттерны. Если датасет содержит много однотипных правок (скажем, 40% — добавление logger.info), модель перестанет предлагать что-то другое. Решение — кластеризовать правки по AST-изменениям и ресемплировать.
2. Лаги инференса. Генерация diff-операций медленнее, чем next-token prediction. Оптимизация — использовать speculative decoding с маленькой draft-моделью, которая предсказывает тривиальные правки (добавить точку, закрыть скобку), а основная модель подключается только для сложных.
3. Несоответствие контекста. Модель обучалась на фиксированном окне в 50 строк, а пользователь редактирует файл на 1000 строк. Решение — тренировать со случайным сдвигом окна и учить модель запрашивать дополнительный контекст через специальный токен [MORE_CONTEXT].
Подробно о проблемах избыточной редактуры и их решениях я писал в статье Как обучить кодинг-модель не переписывать весь код.
Что дальше: NES как база для агента
Сейчас я экспериментирую с гибридом: NES для локальных правок, и кодинг-агент (типа Claude Code) для глобального рефакторинга. Агент выдает план, NES выполняет точечные изменения. Это резко снижает количество лишних изменений — агенту не нужно лезть в каждую строку, он только координирует. Об архитектуре Claude Code есть отличный разбор в статье Архитектура Claude Code: глубокий разбор от Sebastian Raschka — там как раз показано, как агенту не хватает точечного редактора.
Если вы хотите докрутить качество, стоит заглянуть в разреженные автоэнкодеры (SAE) — они помогают подсмотреть, какие нейроны отвечают за конкретные типы правок, и скорректировать поведение модели без переобучения. Кому интересно — вот статья с разбором SAE.
Последний совет: не пытайтесь скормить NES всю кодовую базу. Она должна работать на уровне одного файла, а для проекта — комбинировать её с RAG-индексацией сигнатур. Иначе latency убьёт весь интерактив. Пробуйте, пилите свои датасеты, и пусть модель перестанет подсовывать вам фигурные скобки не в том месте.