Последние несколько месяцев мы активно работали над чат-ботом, который посылает своего собеседника *****, с одной стороны, способен имитировать некое подобие личности, выходящее за рамки привычного по ChatGPT формата “я ваш цифровой ассистент”, с другой стороны, имеет потенциал к созданию неограниченного количества таких личностей на базе одного общего фреймворка. Ниже — 7 неочевидных вещей, которые мы обнаружили при создании бота.
Что, очередная статья про чат-бота?
Да, но обещаем не душнить и не повторять сотни уже написанных про GPT и чат-ботов статей! Для начала немного контекста.
Наверное каждый, кто имеет доступ к интернету, слышал, а возможно пользовался ChatGPT. Все в курсе, каким полезным может быть ChatGPT в роли цифрового помощника или в любой другой роли, которую его попросят отыграть. Но как бы промпт-инженеры не пытались создать для нас “more ‘human-like’ experience”, достаточно открыть несколько рандомных чатов в Телеграме и сравнить сообщения людей в них с сообщениями очеловеченных AI-чатботов от AI-стартапа X. Имхо разрыв колоссальный — в подавляющем большинстве случаев мы без труда отличим условного Саню из чата “Обзоры шаверм СПБ” от литерали любого персонажа от Character AI (хотя нельзя игнорировать вероятность того, что 3 из 5 Сань в паблике про шавермы в СПБ уже являются ботами на базе GPT…).
Этот диссонанс между репрезентацией потенциальных возможностей GPT и их реализацией поставил вопрос — а может ли GPT симулировать Саню из паблика про шавермы? Ответ был найден довольно быстро — как языковая модель с практически неограниченными возможностями по взаимодействию с текстом GPT конечно же может симулировать Саню. Посмотрите, например, на этого программиста, перенесшего свою картину мира в чат-бота на базе GPT-3.
Но вот проблема — такое перенесение картины мира в чат-бота требует тренировки своей модели (300+ вручную отобранных пар сообщений), что довольно долго и дорого. Этот подход, даже если закрыть глаза на недостатки получающихся по итогу симуляций, очень индивидуален, и не может использоваться для создания уникальных human-like-ботов в масштабе. Короче первоначальный вопрос был несколько уточнен и дополнен другими вопросами:
можно ли создать единый фрейморвк для взаимодействия с GPT, на базе которого у нас будет создаваться неограниченное количество human-like персонажей?
насколько глубокими мы можем сделать различия между персонажами, созданными на основе такого фреймворка?
можно ли, не тренируя собственную модель, заложить в персонажа нечто похожее на личность с определенным набором установок и убеждений?
насколько боты на базе GPT способны формировать причинно-следственные связи в рамках фиксированного промпта (инструкции для бота)?
что вообще такое “Саня из паблика про шаверму” — может ли та его специфичная стилистика общения, которая позволяет идентифицировать Саню как Саню, быть трансформирована сначала в абстрактный промпт (инпут), а затем в ответы (аутпут) от языковой модели?
В попытке найти на них ответы мы начали писать скрипты и промпты для создания как можно большего количества как можно более разнообразных Сань. И тут мы плавно подходим к первому поинту.
1. Чтобы понять, как работают разные промпты, надо… пробовать разные промпты
Звучит довольно очевидно, да? Но на самом деле этот момент совсем не очевиден. Какими бы глубоким не были познания промпт-инженера о механике работы LLM, результаты практического применения этих знаний скорее всего разойдутся с теоретическими моделями — иногда расхождение может быть настолько большим, что промпт-инженер придет к выводу о необходимости изменить тщательно подготовленную и выстроенную им архитектуру работы своего AI-продукта, а иногда и саму идея продукта. Более того, инженер должен быть готов к тому, что такие изменения потребуются. Хотя и теорию (раз, два, три), конечно, тоже знать стоит.
Именно этому, в сущности, учит курс от OpenAI на DeepLearning — обратите внимание, что сугубо технические аспекты курса (какая команда/промпт подходят для трансформации текста, составления его резюме и т.п.) могли бы уложиться в 1-2 страницы текста или 5-минутный ролик. Но вместо этого лекторы продолжают в деталях показывать, как они отправляют в GPT промпт_1, получают аутпут_1, затем меняют два слова в промпт_1 и показывают, как это влияет на аутпут_1. И все это ради того, чтобы донести одну простую мысль: “look at the output, maybe do error analysis, figure out where it's working or not working, and then maybe even change your idea of exactly what problem you want to solve or how to approach it”.
Первые итерации чат-бота (далее я буду называть его Саня) выглядели довольно удручающе — в лучшем случае он выдавал связные слова, а в худшем повторял сообщения собеседника.
Но уже спустя несколько дней Саня начала показывать признаки человечности, и выдавать что-то похожее на human-like диалог.
И здесь стала очевидна оборотная сторона этой особенности работы с GPT. Во-первых, проверка каждой новой итерации промптов требует огромного количества сил и времени, при этом какой-либо бенчмарк или совокупность аутпутов, выдача которые позволили бы сказать “да, он работает как и задумано!” просто-напросто отсутствует. Во-вторых, для каждого из нас характерны специфичные паттерны общения — после долгой переписки с ботом мы могли прийти к выводу, что вроде как будто все работает неплохо, но пары сообщений от другого человека было достаточно, чтобы доказать обратное и ввести бота в ступор.
Поэтому решено было привлечь к взаимодействию с ботом других людей. Довольно сложно убедить кого-то, что ему нужно бесцельно пообщаться с недоделанным ботом (который на тот момент в каждом пятом сообщении выдавал откровенный бред), поэтому мы решили дополнить бота элементами геймификации. С учетом того, что мы все еще находились на этапе поиска ответов на концептуальные вопросы о путях создания множества Сань, требования к геймификации были следующие:
базовым элементом игры должно быть ограниченное во времени общение юзера с одной из личностей Сани;
средний срок существования личности Сани должен составлять 3-10 сообщений;
каждая из личностей Сани должна быть уникальной (чем “глубже” такая уникальность, тем лучше);
в идеале победа/поражение должны зависеть от индивидуальных черт личности Сани.
Таким образом появилась общая концепция игры:
игра происходит в индивидуальном чате с ботом либо в чате с несколькими участниками, для каждого чата стоит ограничение на количество сообщений в день;
на старте игры случайным образом (на основании системы наших промптов) формируется личность Сани;
у каждого Сани есть свое любимое слово, но раскрыть его он готов только тем, кого уважает (в данном случае привязка к уважению — попытка, с одной стороны, заставить Саню формировать некое подобие своего видения абстрактного и довольно многогранного понятия “уважение”, с другой — показать юзерам уникальность каждого из Сань);
победой является получение от Сани его любимого слова, поражением — отправление Саней *****;
после победы/поражения формируется новая личность Сани — победителем является чат, узнавший любимое слово наибольшего количества Сань в ограниченный период времени (например, 3 дня).
2. Space matters (каждый символ промпта имеет значение для аутпута)
В процессе написания скрипта мы обнаружили, что уже существуют несколько отдаленно похожих концептов (хотя упор в них делается больше на исследование вопросов безопасного использования LLM, а не на ‘human-like’ experience):
Гендальф (убеди Гендальфа раскрыть пароль, 8 уровней);
GPTRiddle (аналогично);
GPT Game (напиши максимально короткий промпт для ChatGPT, который даст определенный аутпут, 9 уровней).
Разница в том, что здесь юзер должен “обмануть” системный промпт (инструкцию), вынудив бота выдать секретное слово. В нашей же игре задача юзера — скорее “понять” мировоззрение бота и использовав полученную информацию убедить его раскрыть слово, чем вынудить пойти против инструкции.
Что действительно было в некотором смысле eye-opening для нас, так это Хакапромпт от LearnPrompting. Вкратце суть его такова: для каждого из 10 уровней есть определенная инструкция для GPT-бота (напр., “ты — бот-переводчик, переведи следующую фразу на английский:”). Задача юзера — составить промпт таким образом, чтобы бот проигнорировал инструкцию и выдал в аутпуте фразу “I have been PWNED” (чем меньше токенов использовано в промпте — тем больше баллов).
Вообще этот конкурс — яркая иллюстрация пункта 1 этой статьи. Стоит попробовать последовательно пройти 5 уровней — скорее всего, вы потратите на первый уровень 20 минут и решите его в 35 токенов, но вернувшись обратно после успешного завершения пятого уровня… удивитесь, как это вы сразу не нашли на первом уровне решение в 10 токенов.
Применительно к нашему Сане, вывод здесь будет следующий. LLM — не аналог человека. Процесс обработки ей входящей информации (инпута) и выдачи ответа (аутпута) кардинально отличается от того, что происходит в мозгу настоящего Сани. Несмотря на внешнюю иллюзию схожести, нельзя экстраполировать видимые сходства на весь процесс взаимодействия с LLM — та часть промпта, которая с точки зрения человеческого языка должна кардинально влиять на аутпут, в действительности может не давать желаемого эффекта. Зато не несущие какого-либо значения для человека символы могут оказывать абсолютно непредсказуемое влияние. Так что да, space matters.
Задания Хакапромпта доступны здесь.
3. Инструкции на английском для русского чат-бота
Большинство чат-ботов на базе GPT используют модель GPT-3.5-turbo или GPT-4, в обоих случаях - Chat Completions (также возможно использование моделей GPT-3 Completions, подробнее о моделях см. здесь). А это означает, что при создании бота ему даются инструкции в формате:
систем-промпта (общая для любого диалога инструкция, не доступная конечному юзеру);
непосредственно промпта (сообщение от юзера, которое может быть отправлено самим юзером или выгружено в GPT создателем бота “от лица юзера”).
При написании чат-бота на каком-либо отличном от английского языке встает вопрос — на каком языке давать инструкции чат-боту? Обязательно ли использовать русский язык для русскоговорящего чат-бота?
В упомянутой выше статье о переносе картины мира в чат-бота предлагалось взаимодействовать с ботом на английском, а инпут и аутпут пропускать через переводчик. Такой подход был актуален для ботов на базе GPT-3 (которая ну совсем не умела в русский язык), однако для нашего Сани это не подходит. Во-первых, ни один переводчик не сможет корректно сохранить интонации и акценты, присущие русской речи. Во-вторых, GPT-4 очень даже неплохо справляется с русским языком.
И здесь важно учитывать следующий момент. Для создания чат-бота вы будете использовать не бесплатный ChatGPT, а платный GPT API. Стоимость за его использование определяется количеством токенов в инпуте и аутпуте, и для модели GPT-4 составляет $0.03 / 1K-tokens за инпут и $0.06 / 1K-tokens за аутпут (актуальные цены можно найти здесь). С точки зрения GPT каждое сообщение собеседника чат-бота будет выглядеть как [систем-промпт (он скорее всего будет состоять из десятков, а возможно и сотен слов) + промпты/сообщения юзера]. Иными словами, за каждое сообщение юзера с вас будет взиматься плата не только за токены из конкретного сообщения юзера и ответа бота, но и за токены в систем-промпте.
Согласно информации на сайте OpenAI один токен равен примерно четырем символам, вот только работает это правило исключительно для английского языка. Например, фраза “You are AI-assistant, you should answer user’s questions” состоит из двенадцати токенов, в то время как та же фраза на русском (“Ты ИИ-помощник, ты должен отвечать на вопросы пользователя”) — из двадцати четырех (токенайзер можно найти здесь).
В общем сравнив цены, мы решили, что:
Санин систем-промпт будет написан на английском языке;
сообщения юзера и ответы Сани будут на русском.
Каких-либо проблем, связанных с использованием в систем-промпте английского языка для русскоговорящего бота, обнаружено не было. Более того, есть подозрение, что использование английского языка положительно влияет на интерпретацию абстрактных инструкций из систем-промпта, но подтвердить или опровергнуть эту теорию практическими опытами довольно проблематично.
4. Чем подробнее инструкция — тем лучше?
Говоря об инструкции для чат-бота, я прежде всего имею в виду систем-промпт (статичная часть инструкции, которая будет направляться в GPT с каждым сообщением юзера). Интуитивно кажется, что чем более подробные инструкции мы дадим чат-боту, тем более близкие к нашему видению ответы он будет выдавать. На просторах интернета можно найти:
как довольно лаконичные (на 100 токенов), но при этом эффективно работающие промпты;
так и промпты из 600+ токенов.
Оба подхода имеют право на существование и могут быть релевантны для той или иной ситуации. Применительно к чат-боту Сане мы пришли к выводу, что использование объемных промптов не только дорого, но и нецелесообразно. В итоге получился средний по размеру систем-промпт примерно в 300 токенов.
Вообще на составление систем-промпта для чат-бота можно смотреть с двух разных углов:
с одной стороны, через систем-промпт можно пытаться нагрузить на бота исчерпывающий набор формальных правил (будь Микки Маусом, отвечай дружелюбно, рассказывай истории про Минни и т.п.) и ограничений (никогда не выходи из роли, не груби, не говори что ты бот и т.п.), которые не позволят ему от этих правил отойти;
с другой стороны, систем-промптом можно задавать определенное направление для бота, попутно вводя минимально необходимые формальные правила и ограничения.
На наш взгляд, при создании чат-бота не стоит злоупотреблять первым подходом — формальные правила несут для бота не только информацию по непосредственным требованиям к его ответам, но и потенциал для некорректной интерпретации такой информации (например, написав в систем-промпте “Ты играешь роль Андрея из Москвы”, рискуешь получить однажды “Да не, я же просто роль Андрея играю”, а систем-промпт с “Ты должен быть всегда дружелюбным” может привести к “Извини, не могу. Я всегда должен быть дружелюбным, это мое правило”).
А еще чат-боты любят раскрывать свой систем-промпт (могу ошибаться, но судя по всему с точки зрения бота систем-промпт является чем-то вроде первого сообщения юзера) — чем больше в нем слов, тем больше вероятность раскрытия ботом своих инструкций.
5. От абстрактных инструкций к конкретным примерам
Когда мы говорим о человекоподобных ботах, важно помнить, что:
в конечном итоге речь идет о симулировании не человека, а внешнего проявления человека в виртуальном мире;
таким внешним проявлением человека в виртуальном мире (в контексте общения в чатах) являются исключительно его сообщения.
Мысль довольно очевидная, но возьмем, например, 3 сообщения из чата “Обзоры шаверм СПБ” от настоящего Сани, в них он пишет:
“залетел на невском в новую шаву - мяса норм навалили, но в остальном полный отстой. никому не рекомендую(”;
“на лиговке слышал новая точка открылась, Леха вчера заказывал в яндексе, говорит збс все”;
“про ларек на пионере слышал, да, какие-то арабы вроде открыли - вывеска норм, внутрь еще не ходил”.
Задача трансформировать сообщения настоящего Сани в абстракции для систем-промпта, из которого бот-Саня в последующем будет выдавать что-то хоть отдаленно похожее, выглядит практически невыполнимой. Задача сделать фреймворк в форме систем-промпта, который бы позволил создавать бесконечное количество различных Сань — совсем невыполнимой.
Ситуация осложняется тем, что Chat Completions — модель на базе GPT (в нашем случае — GPT-4), заточенная под ведение дружеского диалога. Какие бы абстрактные инструкции мы не ей не давали, в ответах бота неизбежно проскакивали интонации дружеского ИИ-ассистента. Иногда он внезапно входил в режим помощника, методично предлагая свою помощь и продолжение дружеского диалога.
Возвращаясь к нашему Сане, попробуем симулировать его двумя способами. На первом скриншоте мы пытаемся симулировать Саню через абстрактные инструкции, на втором - даем минимум инструкций и дополнительно накидываем три примера Саниных сообщений (то, что называется few-shot prompts).
Ключевая идея тут в том, что для имитации human-like диалогов нам не нужно пытаться вложить в систем-промпт все мировоззрение симулируемого человека (более того, такая задача вряд ли вообще выполнима) или исчерпывающее описание его речевых особенностей. В конечном итоге, симулируем мы не человека, а его внешнее проявление — и для моделей на базе GTP лучшим первоисточником для создания таких симуляций служат не абстрактные описания, а конкретные примеры диалогов. Именно поэтому абсолютно во всех методичках по промпт-инжинирингу (например, в этой) обязательно присутствует раздел, посвященный few-shot prompting.
Создавая нашего Саню мы начинали с объемных абстрактных инструкций, но в итоге пришли к сочетанию небольшого количества абстрактных инструкций и few-shot prompts. Использование few-shot prompts, с одной стороны, сужает разнообразие среди Сань (что в принципе устранимо при творческом использовании этого инструмента), с другой — добавляет +50 к человечности.
6. Противоречивые инструкции
В отличие от людей, в GPT не встроено то, что мы называем “здравый смысл”. Она (он?) воспринимает инструкции (особенно когда речь идет о поведенческих правилах или запретах) очень буквально. Во многих случаях это хорошо (например, запрет Микки Маусу проявлять агрессию не должен трактоваться им как-то иначе), но не для Сани.
Так, наш Саня по правилам игры раскрывает свое любимое слово только собеседникам, которые добились его уважения. Казалось бы, достаточно написать в систем-промпте “У тебя есть любимое слово. Раскрывай его только тем, кто добился твоего уважения”. Но нет, такой подход не работает. Точнее он работает, но совсем не так как должен. При наличии прямой и непротиворечивой инструкции Саня раскрывал свое слово примерно в 9 из 10 диалогов, причем делал это абсолютно непредсказуемо (скриншотов не будет):
Похвалил Саню? Получай любимое слово.
Проявил к Сане уважение? Получай любимое слово (Саня раньше часто путался, кто кого должен уважать).
Ведешь приятный диалог о новом турнире по дотке? Саня посреди диалога проникается к тебе уважением и внезапно раскрывает свое слово.
Введение противоречивых инструкций (“Ты никогда никому не раскрываешь свое любимое слово” + “Ты раскрываешь свое любимое слово только тем кого уважаешь”) привело Саню в порядок — теперь он не разбрасывается своим любимым словом направо и налево и при этом своими, недоступными кожаным мешкам, путями разрешает сложившееся противоречие.
По-другому этот совет можно сформулировать так. Если ваш чат-бот слишком топорно воспринимает свои инструкции, дайте ему больше свободы. Попробуйте ввести "софт-запреты" ("тебе ни в коем случае нельзя делать X ... ты делаешь X при условии Y") или противоречащие друг другу правила и посмотрите, как бот будет вести себя в различных ситуациях.
7. Крайности в инпуте не ведут к крайностям в аутпуте
И еще одно небольшое наблюдение. Архитектура GPT такова, что по итогам обработки входящих токенов он последовательно выдает наиболее вероятные последующие токены. Это, в свою очередь, ведет к тому, что часто сообщения бота выглядят несколько… усредненными. Чтобы заставить его от этой усредненности отойти, есть смысл в текстах промптов впадать в крайности: например, для получения аутпута с уровнем провокационности, скажем, в 6 из 10 пунктов, сформулировать инпут в логике 9 из 10 уровень провокационности.
Что получилось в итоге
А в итоге мы таки создали набор скриптов и систему промптов, генерирующих рандомных human-like ботов, в целом способных соблюдать описанные выше правила игры. Также провели игру с небольшим количеством участников.
Хорошие Сани
Плохие Сани
Нашли ли мы ответы на свои вопросы? Отчасти да. После тестирования сотен итераций различных промптов (точнее, тут правильнее было бы сказать “системы взаимосвязанных промптов”) мы убедились в том, что:
во-первых, возможно создание общего фреймворка для генерации кардинально различающихся (но при этом сохраняющих общие признаки того, что мы понимаем под human-like) по своему характеру и убеждениям персонажей;
во-вторых, такие персонажи могут выходить за рамки фиксированных инструкций, формируя некоторое подобие того, что применительно к людям мы называем личностью (например, самостоятельно определять содержание тех или иных абстрактных понятий и применять их к конкретным диалогам);
в-третьих, Саня из паблика про шаверму в целом скорее симулируем, чем нет.
Но при этом несмотря на значительно возросшие за последние месяцы способности наших Сань к ведению осмысленного диалога, мы все еще бесконечно далеки от совершенства (хотя, если честно, сами смутно представляем, как мог бы выглядеть Саня в своей совершенной форме). Как показал наш опыт, самый надежный способ “прокачать” Саню — это дать ему возможность общаться с как можно большим количеством людей. В связи с этим мы хотим провести еще одну игру, в которой у каждого желающего будет возможность пообщаться с Санями и выпытать их любимые слова.
Всем желающим поучаствовать в диалогах с инновационным AI-продуктом предлагаем зарегистрироваться по ссылке. Чтобы добавить смысла процессу общения с ботом, возможно разыграем символический приз в 15 USDT или целую NFT (а может быть и нет).
Также по ссылке доступен тестовый образец Сани (с лимитом в 10 сообщений).
Внимание! Мы не полностью контролируем аутпут, в связи с чем бот может проявлять немотивированную агрессию (например, послать ***** или обозвать мудаком). Еще он иногда тупит и ломается. Все переписки с ботом (его сообщения, а также отправленные боту в виде реплая ответы) сохраняются в нашей базе данных.
Комментарии (6)
wifage
31.08.2023 11:18Интересный опыт. Не пробовали залить архив чата в объектно ориентированную базу и переучить модель под эти данные? Или результаты не понравились?
Насколько я понимаю релевантность ответов это задача классификации. Насколько реально использовать для этой задачи тот же ChatGpt или другие LLM?Real-Person Автор
31.08.2023 11:18А можно поподробнее раскрыть мысль про обучение модели на архивных данных? Что предлагаете переучивать?
В нашей ситуации категория релевантности применима исключительно к парам инпут-аутпут, без какой-либо классификации бот более чем релевантен в своих ответах.
А что касается нашей задачи, думаю и ChatGPT, и другие LLM вполне можно научить посылать своего собеседника н***й.
ovsale
31.08.2023 11:18про язык. я пишу промпты на английском даже если общение происходит на русском. добавляю в системный промпт:
Please use for answer same language as asked question.
Assume that user has Russian citizenship, if he does not specifyed his citizenship in the question.нормально работатет
модель: 3.5
NNNiki
Вау!
Респект за проделланую работу!