В эпоху быстрого развития AI и LLM я все чаще вижу, что многие люди думают AI заменят всех программистов и они будут не нужны. Такую мысль высказывал даже CEO Nvidia - Jensen Huang.
Но несмотря на впечатляющие достижения ИИ в области генерации кода, существует фундаментальное ограничение, которое не позволяет им полностью заменить человека в программировании — неспособность к настоящему рассуждению (reasoning).
Иллюзия интеллекта
Когда мы отправляем запрос к LLM, кажется, что он знает ответы на абсолютно все вопросы. Это выглядит магическим, но причина достаточно проста — большие LLM реально обучены на всем интернете. Это трудно вообразить, но в тренировочном наборе LLM действительно есть абсолютно весь интернет. Из-за этого уровня масштаба создается ощущение, что LLM обладает интеллектом. Но важно понимать, что это всего лишь извлечение (retrieval) уже существующих решений или их комбинаций, но никак не решение новых задач.
Тест на способность к рассуждению - Strawberry
Очень простой пример: Сколько букв r в слове strawberry? Любой человек скажет что их 3, но LLM отвечает - 2. Секрет, почему ChatGPT неверно счиатает количество букв R кроется в токенизации. Токены можно представлять как строительный блоки, которые модель использует для понимания и генерации текста. Наличие токенов позволяет быстро и эффективно обрабатывать текст, но при этом не подразумевает у модели наличия способностей к рассуждению.
В токенайзере GPT-4 можно подробно увидеть почему это происходит
С точки зрения AI, "strawberry" - это не последовательность отдельных букв, это также не серия строк [str, aw, berry], а скорее последовательность токен IDs [496, 675, 15717]. LLM несмотря на их кажущуюся магию, следует воспринимать как систему статистического моделирования, выполняющую задачу предсказания следующего токена на основе тренировочных данных. Именно поэтому она и не может посчитать количество r в слове strawberry/
Тест на способность к рассуждению - Алгоритмическая задача
Возьмем простую и достаточно старую задачу для подготовки к алгоритмической сессии почти в любую IT компанию - Invert Binary Tree. Эту задачу легко решить стажер в почти любой компании, и в ее классическом виде ее также решат все LLM.
-
Добавим всего несколько нюансов, чтобы сделать задачу новой, которой не было в обучающей выборке LLM.
Нужно инвертировать ключи (перестановка с обращением битов)
Решение должно быть чистой рекурсивной функцией без зависимостей
Функция должна иметь тип
Bit -> Tree -> Tree
Задачу с дополнением этих нюансов решит любой программист, умеющий кодить. При этом ни одна LLM решить ее не может, даже последние версии chatGPT, Claude или Gemini. Решение очень простое и включает всего 7 строчек, но требует способности к рассуждению.
Эти небольшие изменения достаточны, чтобы вывести задачу из "зоны запомненных решений". Ее нет в интернете, и, что самое важное, все современные ИИ не справляются с ней, независимо от того, как их запрашивать.
Ссылка на источник задачи: https://gist.github.com/VictorTaelin/45440a737e47b872d7505c6cda27b6aa
Почему это важно?
Неспособность к оригинальному мышлению: LLM могут комбинировать существующие решения, но не способны создавать действительно новые.
Ограничения в исследованиях: Эта неспособность решать новые задачи означает, что LLM не смогут вносить оригинальный вклад в научные исследования.
Иллюзия понимания: LLM создают впечатление понимания, но на самом деле они лишь воспроизводят комбинации из своей обучающей выборки.
Предел автоматизации: Хотя LLM могут автоматизировать многие задачи, они не способны заменить человека в создании действительно инновационных решений.
Стоит отметить, что LLM в любом случае дают сильный рост продуктивности текущих программистов:
Они значительно повысят продуктивность программистов.
Вероятно, автоматизируют 99% существующих задач.
Окажут влияние, сравнимое с появлением компьютеров и интернета.
Сегодня ключевое ограничение остается: LLM не способны к настоящему рассуждению и созданию оригинальных решений.
Заключение
Понимание принципов работы современного искусственного интеллекта актуально как никогда. Фундаментальное понимание того, как работает AI, поможет вам критически оценивать заявления продавцов AI-продуктов и фильтровать их утверждения о том, что AI полностью заменит программистов.
Среди рекомендуемых материалов для изучения могу выделить
Глубокое понимание принципов работы AI поможет вам лучше фильтровать новости и заявления, особенно те, которые исходят от людей, не имеющих глубокого понимания технологии.
P.S. Как early adopter AI - я занимался машинным обучением и NLP задолго до появления ChatGPT. Моя вера в потенциал этих технологий остается непоколебимой. Цель этой статьи - объективно осветить текущие ограничения LLM, признавая при этом их значительный прогресс и потенциал для дальнейшего развития. Это не критика технологии, а скорее анализ ее нынешнего состояния, направленный на стимулирование дискуссии о путях ее совершенствования.
Комментарии (21)
Octabun
14.10.2024 08:09Как вариант. AI точно заменит программистов. Возможно, некоторые программисты переучатся на профессию которая будет называться иначе и продолжат участвовать в создании приложений. Потому, что люди уже обращаются к AI так, как будто он думает, пусть пока и в попытках доказать что AI не думает.
Zenitchik
14.10.2024 08:09Нет. Программисты переучатся на профессию, которая будет по сути другой, но называться будет так же. Такое уже случалось.
rk-helper Автор
14.10.2024 08:09Да, в этом и смысл статьи - может быть писать код и не надо будет. Но иметь reasoning и уметь решать новые проблемы все еще кто-то должен. К сожалению, LLM может это делать только в пределах трэйн сета, а ее трэйн сет это только доступный в интернете текст.
Zenitchik
14.10.2024 08:09Всё равно надо будет. Возможно, на сильно формализованном подъязыке естественного языка и называя его не кодом, а спецификацией.
Предельно подробное описание того, что должна сделать машина, называется "программа".
rk-helper Автор
14.10.2024 08:09Да, то что вы описали я и имел ввиду. Написание псевдокода для решения задачи, а LLM например сама переведет это в нужный синтаксис и тд.
TerraV
14.10.2024 08:09Как же вы задолбали со своим Strawberry. Мне это напоминает анекдот про японскую бензопилу и лом. У вас блин окно на миллион токенов, вы используете от силы 1000 на вход и потом обвиняете что LLM косячит.
Скрытый текст
Да, это действительно связано с тем, как я обрабатываю текст, включая токенизацию. Токенизация — это процесс разбиения текста на отдельные компоненты (токены), такие как слова или символы, которые я затем анализирую.
Когда ты задал вопрос про количество букв "r" в слове "strawberry", моя первоначальная реакция не была связана с подробной токенизацией каждого символа. Вместо этого я полагался на "быстрое" знание, которое связано с типичными примерами слов. Это сработало, но я мог недооценить ситуацию и не заметил, что буква "r" встречается три раза, а не два.
Когда ты запросил таблицу частоты букв, я использовал более точный метод, который включает работу с каждым символом слова. Здесь важна символная токенизация, где каждый символ анализируется по отдельности, что исключает вероятность ошибки.
Таким образом, можно сказать, что первоначальный ответ больше зависел от общей текстовой обработки и моего быстрого "знания", тогда как более точный подсчет частоты символов уже требует детализированной токенизации. Это позволяет мне более точно работать с текстом, когда важно учитывать каждый символ или слово по отдельности.
Надеюсь, это объяснение помогает лучше понять, как я обрабатываю текст!
Вместо того чтобы разбираться какие границы применимости есть у технологии, мы огульно скажем - LLM шлак и пойдем рубить дальше.
Я скажу больше, у меня сейчас ChatGPT 4o пишет код на уровне мидла, o1 preview на уровне сеньора. Просто нужно помнить что узкое место как правило между клавиатурой и сиденьем.
rk-helper Автор
14.10.2024 08:09Мой поинт не в том, что LLM шлак - это супер полезная технология, увеличивающая продуктивность и позволяющая упрощать многие моменты в программирование. Но написать что-то за пределами трэйн сета она не может.
talraaash
14.10.2024 08:09Я скажу больше, у меня сейчас ChatGPT 4o пишет код на уровне мидла
Я уже постил тут задачу для стажера... добьетесь от LLM её решения? У меня ушло больше трех часов и кучи итераций промтов. В итоге получился почти работающий код. https://habr.com/ru/news/847802/comments/#comment_27370784
TerraV
14.10.2024 08:09В такой постановке не удивительно что вы получаете шлак на выходе. У вас не постановка задачи а какой-то бессвязный поток мыслей. Ответтье мне на следующие вопросы и я дам вам промпт:
Что такое header
Что такое content
Что такое hashtag
talraaash
14.10.2024 08:09Если вы не уловили, то по ссылке - не промт для LLM, а текст для человека. Для LLM писались другие промты, в том числе с примерами того что должно быть в output. А по тексту, вы как человек не смогли вычленить ответы на заданные вопросы?
Что такое header
один с headers[список, контекст вполне читаем], т.е. каждый элемент начиная с # до \n,
Что такое content
второй с headers + content т.е. все что до следующего #
Тут отчасти согласен, можно было точнее выразится, что conten - все что следует за header, до следующего валидного header т.е. патерна "# Любой символ".
Что такое hashtag
при этом хештеги #tags должны попадать в контент
TerraV
14.10.2024 08:09Как отличить хедер от хэштега, стоящего первым в строке? Будет ли корректно сказать что синтаксис похож на markdown?
P.S. ща с обеда вернусь и скину промпт
talraaash
14.10.2024 08:09Как отличить хедер от хэштега, стоящего первым в строке?
Вы серьезно? Различий в "# Header" и "#tags" не видите(если что, правильный паттерн для каждого элемента дополнительно уточнялся в промте для LLM) Там по ссылке даже есть пример петли, в которую попадала LLM и переубедить её, что она не права уже не получалось, приходилось сбрасывать контекст:
A valid header must have at least one hashtag followed by whitespace and then one or more word characters. "# Hello" does not meet this requirement.
TerraV
14.10.2024 08:09Промпт
Привет дружище, как насчет написать небольшую программку на javascript? Условия немного мутные, так что надо будет покрошить на человекочитаемые и человекомисправляемые методы чтоб если чё можно было допилить напильником. Вот входные условия: На входе строка, которая на самом деле не строка а множество строк с разделителем. Разделитель \n, но это не точно. Каждая индивидуальная строка может быть либо header (начинается с одного или нескольких #, после чего следует минимум один пробел и дальше какая-то шляпа) либо content - любая строка не попадающая под критерии header. Нужно написать такую функцию, которая на выходе даст два списка. Первый список должен содержать только header'ы, второй должен содержать конкатенации header + весь content ему принадлежащий. Конкатенация должна быть через тот же разедлитель что и оригинальная строка. Когда будешь писать код, постарайся учесть что может поменяться во входном условии и изолировать эти изменения в самостоятельных функциях
ChatGPT 4o
Привет! Вот вариант программы на JavaScript, которая отвечает твоим условиям, с учетом гибкости для возможных изменений:
function parseInput(input, separator = '\n') { // Функция для разделения строки на массив подстрок function splitString(str, sep) { return str.split(sep); } // Функция для проверки, является ли строка заголовком function isHeader(line) { return /^#+\s/.test(line); // Проверка на наличие одного или нескольких # и хотя бы одного пробела } // Основная функция для получения двух списков function processInput(input, sep) { const lines = splitString(input, sep); const headers = []; const contentBlocks = []; let currentHeader = null; let currentContent = []; lines.forEach(line => { if (isHeader(line)) { // Если был предыдущий заголовок, сохраняем его с контентом if (currentHeader) { contentBlocks.push(currentHeader + sep + currentContent.join(sep)); } // Начинаем новый блок headers.push(line); currentHeader = line; currentContent = []; } else { // Если строка не заголовок, добавляем её к текущему контенту currentContent.push(line); } }); // Добавляем последний блок контента, если он есть if (currentHeader) { contentBlocks.push(currentHeader + sep + currentContent.join(sep)); } return { headers, contentBlocks }; } // Вызов основной функции return processInput(input, separator); } // Пример использования const inputString = ` # Header 1 Some content under header 1 Another line of content ## Subheader 1.1 Content for subheader 1.1 # Header 2 Content under header 2 More content under header 2 `; const result = parseInput(inputString.trim()); console.log("Headers:", result.headers); console.log("Content blocks:", result.contentBlocks);
Пояснение:
splitString: Функция, которая разделяет входную строку по заданному разделителю (по умолчанию —
\n
). Это позволяет легко изменить способ разделения строк, если изменится разделитель.isHeader: Проверяет, является ли строка заголовком. Использует регулярное выражение, которое ищет строки, начинающиеся с одного или нескольких символов
#
, за которыми следует хотя бы один пробел. Эту проверку можно изменить, если изменятся условия распознавания заголовков.processInput: Основная функция, которая проходит по каждой строке, разделенной на массив. Сохраняет заголовки в один список, а блоки "заголовок + контент" в другой.
Результат: На выходе функция возвращает два массива — список заголовков и список конкатенированных блоков "заголовок + контент".
Пример вывода:
Headers: [ '# Header 1', '## Subheader 1.1', '# Header 2' ] Content blocks: [ '# Header 1\nSome content under header 1\nAnother line of content', '## Subheader 1.1\nContent for subheader 1.1', '# Header 2\nContent under header 2\nMore content under header 2' ]
Такой код легко модифицируется: можно менять условия распознавания заголовков или способ разделения строк, сохраняя общую логику.
o1 preview
Привет! Вот вариант программы на JavaScript, которая отвечает твоим условиям, с учетом гибкости для возможных изменений:
function parseInput(input, separator = '\n') { // Функция для разделения строки на массив подстрок function splitString(str, sep) { return str.split(sep); } // Функция для проверки, является ли строка заголовком function isHeader(line) { return /^#+\s/.test(line); // Проверка на наличие одного или нескольких # и хотя бы одного пробела } // Основная функция для получения двух списков function processInput(input, sep) { const lines = splitString(input, sep); const headers = []; const contentBlocks = []; let currentHeader = null; let currentContent = []; lines.forEach(line => { if (isHeader(line)) { // Если был предыдущий заголовок, сохраняем его с контентом if (currentHeader) { contentBlocks.push(currentHeader + sep + currentContent.join(sep)); } // Начинаем новый блок headers.push(line); currentHeader = line; currentContent = []; } else { // Если строка не заголовок, добавляем её к текущему контенту currentContent.push(line); } }); // Добавляем последний блок контента, если он есть if (currentHeader) { contentBlocks.push(currentHeader + sep + currentContent.join(sep)); } return { headers, contentBlocks }; } // Вызов основной функции return processInput(input, separator); } // Пример использования const inputString = ` # Header 1 Some content under header 1 Another line of content ## Subheader 1.1 Content for subheader 1.1 # Header 2 Content under header 2 More content under header 2 `; const result = parseInput(inputString.trim()); console.log("Headers:", result.headers); console.log("Content blocks:", result.contentBlocks);
Пояснение:
splitString: Функция, которая разделяет входную строку по заданному разделителю (по умолчанию —
\n
). Это позволяет легко изменить способ разделения строк, если изменится разделитель.isHeader: Проверяет, является ли строка заголовком. Использует регулярное выражение, которое ищет строки, начинающиеся с одного или нескольких символов
#
, за которыми следует хотя бы один пробел. Эту проверку можно изменить, если изменятся условия распознавания заголовков.processInput: Основная функция, которая проходит по каждой строке, разделенной на массив. Сохраняет заголовки в один список, а блоки "заголовок + контент" в другой.
Результат: На выходе функция возвращает два массива — список заголовков и список конкатенированных блоков "заголовок + контент".
Пример вывода:
Headers: [ '# Header 1', '## Subheader 1.1', '# Header 2' ] Content blocks: [ '# Header 1\nSome content under header 1\nAnother line of content', '## Subheader 1.1\nContent for subheader 1.1', '# Header 2\nContent under header 2\nMore content under header 2' ]
Такой код легко модифицируется: можно менять условия распознавания заголовков или способ разделения строк, сохраняя общую логику.
talraaash
14.10.2024 08:09Почти... 4.0 написала слишком строгий regex для header "# Header! 1" >> "# Header 1", о1 тоже не все пограничные случаи отлавливает "#Header ## Subheader 1.1" в output даст не то что ожидается.
Не стоит тратить время, на допиливание, если что. Задачу я решил сам, разобравшись в незнакомом мне до этого js за ~час. А цель добиться решения от LLM была, в понимании сможет ли no code мимокрокодил решать прикладные задачи. На данный момент развития технологий ответ для себя я получил.
TerraV
14.10.2024 08:09Примеры батенька, примеры. Выкладывайте тестовый кейс, прогоним и разберемся. Я уверен что код 100% рабочий и покрывает все граничные случаи которые вы явно упомянули. Если у вас в голове сидит кусок неописанной логики, я не экстрасенс чтоб угадывать.
wataru
14.10.2024 08:09Более того, LLM не справляются не только с задачами, которых в интернете нет, но и с достаточно редкими.
Мой пример: надо с помощью WinAPI MediaFoundations проверить, что камера не работает, потому что она занята другим приложением. Все сетки, что я пробовал выдают разыне вариации из перемешанного DirectShow, выдуманных методов MediaFondations или просто интерпретируют любую ошибку, как "камера занята". В 75% случаев код даже не компилировался. Даже после подсказок использовать IMFActivityMonitor - ни одна сетка не справилась.
А ответ в интернете даже есть, правда в считанных по пальцам местах. Документация Microsoft и пара примеров на гитхабе.
А вот задачки с литкода, решение которых растиражированы стонями в интернете, да какие-то сайтики на сверх-популярных фреймворках оно может, да. Оно как трамвай - по построенным путям ездит отлично, но никуда с них свернуть не может. Личный автомобиль/такси оно не заменяет.
gun_dose
14.10.2024 08:09Прежде чем рассуждать не тему, может ли ИИ заменить программиста, нужно понять, что значит "заменить". Давайте представим, например, очень маленькую веб-студию, где есть дизайнер, менеджер и программист. Берём выкидываем прогера, ставим вместо него ИИ. Смогут ли теперь дизайнер и менеджер справиться со всей работой вдвоём без увеличения нагрузки? Думаю, это вопрос риторический
mclander
С - самоуспокоение?
FurySeer
Реалистичный взгляд
rk-helper Автор
Вряд ли самоуспокоение. LLM пишет крутой код для сниппетов, простых задач. Но написать гораздо более сложные вещи, требующие много думать она не может. Мне приходится ускорять инференс ML на работе например - она не понимает, и вряд ли когда-то сможет понять как это работает.
TerraV
Что такое "вещи, требующие много думать"?