Отладка с ИИ работает лучше всего, когда вы даёте модели то же, что дали бы коллеге за своим столом: полный текст ошибки, код, который её вызвал, что вы ожидали, что произошло вместо этого и что вы уже исключили. Модель не может запустить вашу программу или увидеть ваши данные, поэтому всё, что она знает о баге, берётся из того, что вы вставили. Самая полезная привычка: просить причину до исправления.
Вставляйте всё, а не только ошибку
Две вкладки ниже описывают один и тот же баг. Скрипт читает CSV-файл, экспортированный из Excel, и падает с KeyError: 'email', хотя в файле явно есть колонка email.
KeyError означает, что в словаре нет ключа 'email'. Ошибки можно избежать с помощью .get(), который возвращает None, а не выбрасывает исключение:
email = row.get("email")
Или сначала проверить:
if "email" in row:
email = row["email"]
Первый ответ верно объясняет, что такое KeyError, но его совет здесь вреден. С row.get("email") падение исчезает, а каждый email становится None. Модель ответила на самую частую версию ошибки, потому что промпт ничего больше не описывал.
Полный отчёт сработал благодаря двум строкам. «Падает только первая колонка» и «сохранён из Excel как CSV UTF-8»: это подсказки, которые указывают на метку порядка байтов, и ни одной из них нет в сообщении об ошибке. Вы редко знаете, какая из ваших деталей окажется подсказкой, поэтому и вставляете все.
Пять вещей, которые нужно включить
- Полный текст ошибки. Весь traceback или вывод консоли, а не пересказ. Номера строк и цепочка вызовов показывают, где началась ошибка, а это часто несколько фреймов в стороне от того места, где она проявилась.
- Код, который её вызвал. Падающая функция и код, который её вызывает. Если в ошибке упоминается строка, убедитесь, что эта строка есть во вставленном фрагменте.
- Ожидаемое и фактическое. По одному предложению. Для багов вообще без ошибки (неверный вывод, пустая страница, медленный запрос) это и есть весь баг-репорт.
- Ваше окружение. Версия языка, операционная система, версии фреймворка и библиотек, если они могут иметь значение. Баги с кодировками, путями и датами часто зависят от платформы.
- Что вы уже пробовали. Это не даст модели предложить то же самое снова, а результат каждой попытки: доказательство. «Если убрать часть с email, всё работает» сузило баг с CSV до одной колонки.
Просите причину до исправления
Просьба об исправлении приглашает модель менять код, пока ошибка не исчезнет. Просьба о причине заставляет её рассуждать о том, почему ошибка возникает, и исправление следует из этого. Попросите короткий ранжированный список причин со способом проверить каждую, а потом выполните проверки сами. Этот шаблон делает именно это:
TypeError: Cannot read properties of undefined (reading 'map')function UserList() {
const [users, setUsers] = useState();
useEffect(() => {
fetch("/api/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}- Компонент рендерится раньше, чем появляются данные.
useState()начинается сundefined, а React рендерит компонент один раз до запуска эффекта, так чтоusers.mapвызывается наundefined. Одно это даёт ровно вашу ошибку при каждой загрузке. Проверка: добавьтеconsole.log(users)прямо передreturn. Если выводитсяundefinedи сразу за ним ошибка, дело в этом. - API возвращает не массив. Если бы он вернул объект вроде
{ users: [...] }, ошибка была бы другой (users.map is not a function), так что это не та ошибка, которую вы вставили. Но это может стать следующей проблемой после исправления причины 1. Проверка:console.log(data)внутри второго.then. - Запрос не проходит. Пока ни при чём: поскольку первый рендер выбрасывает ошибку, React так и не запускает эффект, и запрос даже не отправляется. Проверка: во вкладке Network нет запроса
/api/users, что заодно подтверждает причину 1.
Ответ делает то, что пропускает ответ «сначала исправление»: он исключает причины, потому что вызванная ими ошибка отличалась бы от вставленной или потому что код до них просто не доходит. Поэтому точный текст ошибки и важен. Просьба перечислить причины перед ответом: лёгкая форма chain of thought промптинга: рассуждение идёт первым, а вывод на него опирается.
Сделайте минимальное воспроизведение
Минимальное воспроизведение: это самая маленькая программа, которая всё ещё показывает баг. Захардкоженные данные вместо запроса к базе, одна функция вместо целого модуля. Пока вы его строите, баг часто находится сам, ещё до того, как вы кого-то спросите, потому что каждая убранная часть либо сохраняет баг (она была ни при чём), либо заставляет его исчезнуть (она была при чём). Если нет, воспроизведение становится идеальным промптом: достаточно коротким, чтобы модель прочитала каждую строку, и без постороннего кода, который мог бы отправить её гоняться не за той проблемой.
Если баг зависит от данных, включите несколько строк, которые его вызывают. Модель может рассуждать о [{"id": 1, "name": null}]; о «некоторых строках в продакшене» рассуждать она не может.
Когда исправления перестают работать
Если третье исправление от модели проваливается так же, четвёртая просьба об исправлении вряд ли поможет. Больше помогают две вещи:
- Дайте ей новые доказательства. Добавьте print или строку лога, которая показывает реальные значения в месте сбоя, запустите и вставьте вывод. Доказательства, противоречащие теории модели: самый быстрый путь к лучшей теории.
- Начните новый разговор. Длинные ветки отладки заполняются брошенными теориями и старыми версиями кода, и модель может продолжать на них опираться. Новый чат с текущим кодом, ошибкой, доказательствами и строкой «уже исключено: X и Y» часто продвигается дальше за один ответ.
Осторожнее с уверенными ответами о поведении библиотек. Модель может описать опцию или функцию, которой не существует; как это проверить, смотрите на странице галлюцинации ИИ. Когда код не сломан, но вы не понимаете, почему он делает то, что делает, лучше подойдёт промпт для объяснения кода.
Часто задаваемые вопросы
Как попросить ChatGPT или Claude исправить мой код?
Вставьте полный текст ошибки и код, который её вызвал, а затем добавьте три короткие строки: что вы ожидали, что произошло вместо этого и что вы уже пробовали. Попросите наиболее вероятную причину и способ её подтвердить, прежде чем просить исправление. Голое «исправь это» даёт исправление для самой частой версии ошибки, которая может оказаться не вашей.
Нужно ли вставлять в ИИ весь проект?
Нет. Вставьте функцию, где происходит ошибка, код, который её вызывает, и образец данных, которые она получает. Ещё лучше сократить проблему до минимальной программы, которая её всё ещё показывает. Не относящиеся к делу файлы замедляют ответ и дают модели больше мест, где искать проблему, которой там нет.
Почему после исправления от ИИ ошибка исчезает, а программа всё равно не работает?
Исправление лечило симптом. Например, замена row["email"] на row.get("email") убирает KeyError, но если ключа нет из-за бага раньше по цепочке, каждый email теперь молча становится None. Просьба сначала назвать причину и способ её подтвердить помогает избежать исправлений, которые только прячут проблему.
Что делать, если ИИ раз за разом предлагает неработающие исправления?
Перестаньте просить исправления и дайте ему доказательства. Сообщите, что изменила каждая попытка, добавьте print или строку лога, которая показывает реальные значения, и вставьте этот вывод. Если разговор длинный, начните новый с чистым изложением: код, ошибка, доказательства и уже исключённые исправления.