Ноасс: расширение, пресеты и внешняя блокогенерация
Расширение ноасса (ST 1.15.0+)
Ссылка - https://gitgud.io/Monblant/noass
Установка:
• Если есть git: Extensions → Install extension (сверху справа) → Вставить ссылку https://gitgud.io/Monblant/noass
• Если нет git: Скачать zip архив с https://gitgud.io/Monblant/noass → Распаковать в SillyTavern/data/default-user/extensions → переименовать там папку noass-master в noass → Перезагрузить таверну
Обновление: Extensions → Manage extensions (сверху справа) → скролл вниз → кнопка обновить рядом с NoAss
Остальное
Промпты, расширения, карточки и все такое под ноасс - https://rentry.org/noass_extra
Борда: https://ejchan.site/ai
Основы ноасса
Суть метода
NoAss (от "NoAssistant") - метод промптинга для писательства и ролеплея, идея которого заключается в переходе от формата чата юзер-ассистент к формату единой склеенной истории, в которую пользователь вносит правки и добавления (частый кейс - отыгрывает конкретного персонажа). По сути, этот метод в какой-то мере эмулирует Text Completion (особенно если склеенная история в префиле) для Chat Completions режима, который используется для инстракт моделей.
Формирование контекста
Метод требует преобразования контекста на каждой отправке промпта. Положим оригинальный контекст сессии ролеплея выглядел подобным образом (используется разметка ChatML):
Склеить такой контекст можно разными способами. Основных вариантов два:
- Отдать инструкции и экспозицию юзеру и склеить всю чатхистори от роли ассистента (как префил):
- Склеить все от роли юзера:
Каждый из способов имеет свои плюсы и минусы, но их основа в том, что отыгрыш между персонажами теперь происходит внутри истории, а не в чате юзер-ассистент. Сообщения, которые отправляет пользователь вклиниваются в эту единую историю, в данном случае от лица {{user}}.
Стопстроки
Поскольку модель вместо того, чтобы давать ответ в формате сообщения чата, по сути продолжает историю, то теоретически она ее может продолжать вечно или по крайней мере довольно долго. Нам ее нужно где-то останавливать, в случае ролеплея - идеально на том моменте, где начинается ход юзера. Поскольку в примерах выше для персонажей были добавлены префиксы, то можно добавить в вызов модели стопстроку {{user}}:, и она остановится именно там, где нужно.
Помимо нативных стопстрок можно использовать ограничение ответа по токенам/символам (в некоторых случаях подходит, но обрывает историю в рандомном месте, так что нужна постобработка), а также остановку генерации на стороне клиента (например по регексам, можно задавать сложные условия остановки, но эффективно только со стримингом). Также можно обрывать генерацию вручную, если использовать метод для интерактивного написания истории.
Мотивация метода
Контекст сформирован и стопстроки останавливают генерацию где надо. Но собственно зачем?
Проблемы формата чата юзер-ассистент
Ролеплей в формате чата юзер-ассистент сталкивается с несколькими ключевыми проблемами:
- Модель в своем ответе может писать от лица
{{user}}, которого отыгрывает пользователь. Для борьбы с этим обычно приходится использовать запретительные инструкции, которые работают не всегда и присутствие которых неидеально. - Современные модели часто имеют сильный user bias - предвзятость к пользователю, и соответственно к тому персонажу, которого он отыгрывает. Чаще всего в смешении с positivity bias это приводит к тому, что
{{user}}'у даются необоснованные с точки зрения нарратива преимущества (например,{{user}}не может погибнуть), что негативно сказывается на истории. - Ответы модели могут становится одинаковыми по длине либо становится с каждым разом все больше, вне зависимости от контекста сцены. Для противодействия этому обычно приходится использовать промпты с желательной или рандомной длиной ответа.
- Ответы часто заканчиваются закруглениями (риторическими или обычными вопросами), которые по сути являются косвенным обращением к самому пользователю и ломают погружение.
Решения этих проблем в NoAss
Эти проблемы частично или полностью решаются с применением метода NoAss:
- Поскольку генерация образывается по стопстроке, то модель физически не может написать реплику от персонажа
{{user}}. - С точки зрения модели все персонажи истории будут равнозначны априори, поскольку никому не принадлежат. Ту или иную предвзятость они могут получить только исходя из нарратива и промпта.
- Нет непосредственного понятия длины ответа, расстояние между репликами также контролируется нарративом, таким образом фактически создавая динамическую длину ответа, которая зависит непосредственно от контекста.
- Модель пишет в своем ответе длинную историю, которая обрывается по стоптокену, так что закругления остаются в обрезанной части.
По сути мы получаем решения этих проблем чисто сменой форматирования, с нулевым промптингом. Это сильно облегчает основной промптинг, позволяя не нагружать модель лишними инструкциями.
Основные проблемы метода
Несмотря на решение старых проблем, метод сталкивается с проблемами новыми, решение которых требует определенных усилий от пользователя:
- SillyTavern без расширений до сих пор поддерживает этот метод очень ограничено, даже с Post-Processing. Произвольно формировать и склеивать контекст там затруднительно.
- Для основных инструкций нужен другой подход. В обычных пресетах мы устанавливаем инструкции для ответов ассистента и особого статуса {{user}}, здесь же нам нужны инструкции для продолжения истории. Это накладывает ограничения и особенности на то, как мы можем управлять нарративом.
- Поскольку используются стопстроки основанные префиксах персонажей, необходимо переписывать гритинги, чтобы как минимум префиксы там присутствовали.
- Пользователь должен писать в том же стиле, что и модель, поскольку его ответы напрямую вклеиваются в историю. То же касается граммотности и языка.
- Поскольку ответ обрывается, то инфоблоки и другие блоки придется генерировать другими способами. Один из таких способов - использование расширения для внешней блокогенерации, которое будет вызывать отдельные модели для генерации разных блоков.
Расширение NoAss
Поскольку для работы метода нужны некоторые нетривиальные преобразования, то для него было реализовано одноименное расширение для SillyTavern. Оно автоматизирует склейку контекста при отправке промпта модели, а также позволяет производить постпроцессинг промпта.
Основные возможности расширения
- Склеивать историю чата в одно сообщения. Роль этого сообщения можно выбрать.
- Добавлять префиксы/суффиксы для сообщений юзера и ассистента.
- Использовать стопстроки (в том числе регексовые на стороне клиента) и ограничения по количеству символов для обрыва генерации.
- Разрезать склеенную историю не несколько частей по регексу, вставляя между частями разделители
- Обрезать склеенную историю сверху по регексу
Дополнительные возможности можно посмотреть в репозитории расширения.
Минимальный старт
- Включить в
User Settingsв тавернеShow {{char}}: in responsesиShow {{user}}: in responses. Проверить наличие git в системе - если его нет, то установить. - Установить расширение: Extensions → Install extension (сверху справа) → Вставить ссылку https://gitgud.io/Monblant/noass
- Включить расширение: Открыть настройки расширения и включить галку
Enable NoAss. - Импортировать готовые сеты с настройками из существующих пресетов или настроить вручную. Дефолтный сет рассчитан на префиксы вида
**{{char}}:**и имеет стопстроку**{{user}}**.
Пресеты и периферия ноасса:
Поскольку ноасс отличается от обычных методов промптинга, то пресеты под него должны выглядеть по-другому. В частности - задавать базовое форматирование и содержать инструкцию для написания истории. В большинстве пресетов для ноасса также используется такой прием, как Мемо, задающий истории направление и определения через "мягкий" промптинг.
Пресеты под ноасс:
Внешние блоки:
Из-за того, что ответ обрывается с помощью стопстроки, в ноассе затруднительно получать блоки (например инфоблоки) в основном ответе. Поэтому для генерации блоков используется внешняя модель с отдельным промптом и настраиваемым контекстом. Для этого существует расширение ExtBlocks.
Расширение для внешней блокогенерации - https://rentry.org/ext_blocks
Особенности в ноассе
Базовые правила форматирования:
- 1 абзац - 1 префикс. Префикс персонажа -
**char name:** - Под префиксом персонажа могут содержаться
*действия*,"речь"и'мысли'в любом количестве и порядке. - Нарратив (плейнтекст, экспозиция) должен содержать только необходимый минимум действий персонажей. Основные действия должны быть под префиксом.
- Свой ответ должен соответствовать правилам форматирования и быть в меру развернутым.
Гритинг:
- Должен соответствовать правилам форматирования. От гритинга сильно зависит стиль дальнейшего РП.
- Гритинг лучше всего начинать с объявления сцены/акта/начального транзишена, начальной экспозиции (описание сцены, расстановка персонажей и завязка), затем должно идти непосредственное взаимодействие персонажей.
- В гритинг можно включать действия и речь от юзера (здесь нужно ставить его префикс как любому другому персонажу).
- Нежелательно в гритинг вставлять монологи
Мемо:
Рентри ХМЛа про мемо в целом - https://rentry.co/DrunkArcadeExample
TL;DR, важные для РП напоминания компактно хранятся на глубине. Пресет каноничного ноасса предполагает, что в мемо содержатся следующие вещи:
- Ограничения, они же псевдоинструкции - напоминания ассистента самому себе, каким должно быть повествование (форматирование, содержание, персонажи и т.д.)
- Анкеты персонажей - структурированные компактные описания персонажей, содержащие только необходимую информацию. Развернутая информация и предыстория отправляются в системпромпт.
- Планы на историю (опционально) - кратковременное или долговременное представление, как должна выглядеть история, чаще всего идеи/теги
Помимо пресетного мемо можно использовать вставку на глубину через Character's Note / Author's Note для каких-то особых вещей.
Структурные лупы:
С увеличением контекста могут возникать повторяющиеся структуры: одинаковое слово в начале параграфа, одинаковый порядок ответов, одинаковая структура предложений и т.д. Если игнорировать их, то повторяющихся структур будет становиться все больше, и в силу внутриконтекстного обучения возникнет структурный луп. Это ударит по качеству РП: сетка будет стараться повторять один и тот же формат независимо от текущего смысла. Это приведет к тому, что ей придется выдумывать детали из пальца/опускать нужные детали, не менять темп повествования и даже подгонять сам сюжет под форматный луп. Какие методы борьбы со структурными лупами предполагаются в ноассе:
- Редактирование сообщения вручную (эффективно только на ранних этапах лупа)
- Автоматическое переписывание сообщения - это делается с помощью внешнего рерайт блока. Необходима хорошая допсетка/допзапрос.
- Ручной префил - устоявшийся порядок ответов можно разбить заданным префилом с префиксом какого-то персонажа/началом нарратива (в пресете или расширении)
- Думалка (CoT) - с помощью планирования ответа также можно избегать части лупов. В ноассе думалка делается с помощью внешних блоков.
- Смена акта - хороший комбобрейкер, форсируется с помощью префила. Иногда акт меняет и сама сетка.
Старые пасты:
Шизотерминология
> У лоботомитов же дохуя скормленных книг и тд в датасете, так что логично предположить что он может предугадывать и токены экспозиции.
Фуллжоп (fullass) - единый унифицированный нарратив, говоришь сетке писать историю, она и пишет. Ты не вмешиваешься, помимо дефов. Есть предугадывание поворотов, поскольку сетке позволено свои стереотипы о книгах полностью использовать. Качество лежит только на крутости сетки.
Безжоп (noass) - единый неунифицированный нарратив, хуман постоянно толкает историю в другое русло по сравнению с тем, что было бы без его вмешательства. История становится более сложной, предугадывание поворотов работает уже не так хорошо, к тому же дело усложняет префиксное форматирование.
Полужоп (halfass) - мультитурновый унифицированный нарратив. Сетка пишет историю и предлагает тебе варианты развития событий типа CYOA. Ты выбираешь или предлагаешь свой. Большинство поворотов ложится на плечи хумана, но всю историю пишет сетка. Поворотам вдолгую не дает разыграться мультитурн и вмешательство хумана, но резкие повороты вполне имеют место.
Сжоп (ass) - мультитурновый неунифицированный нарратив. Обычный чат, чаще всего сетку ставят на роль нарратора. Ты отыгрываешь {{user}}, сетка отыгрывает все остальное. Тут ограничений на сетку больше всего: мультитурн, запрет писать за юзера, противоречащее форматирование хумана, то же самое постоянное вмешательство хумана. В итоге сетке остается маловато пространства для маневра и приходится топтаться на месте.
1.
Короче смотри, немного оффтоп, но на самом деле клод не просто отвечает на твое сообщение и потом останавливается - он бесконечно генерирует огромную простыню диалога между хуманом и ассистентом. То есть, например ты сказал ему решить пример по матану:
Human: реши пример
Assistant: да, вот решение
и дальше бы продолжил буквально генерировать за тебя (например, написал бы от лица хумана просьбу решить еще один пример), НО, его останавливает вшитый стоп-токен от куктропиков - Human:
Еще проще говоря: Клод - это не Assistant. Клод - это нейронка генерирующая диалог между хуманом и ассистентом.
И вот мы подбираемся к тому самому файнтюну на ассистента, а именно - вместо того чтобы бесконечно генерировать РП/фанфик, генерируется тот самый диалог между хуманом и ассистентом, который, очевидно, даже через маску персонажей будет влиять на аутпут (а отсюда следует соя, лупы, пассивность и прочее что требуется от Assistant'а и на что он тренирован).
И вот безасс решает эту проблему. Теперь диалог идет не между хуманом и ассистентом, а между непосредственно персонажами учатствующими в истории и/или нарратором.
2.
Нифига, смотри как устроен Клод (по крайней мере был до джсонов)
Это просто ЛЛМ, которая ебашит диалог между хуманом и ассистентом. Без стоп токенов она тупо будет писать и за юзера (Оу ноу! Это грех!) и ассистента. Я это всё узнал, когда как раз в профиле и пробовал играть, потому что тогда была дырка со стоп токенами через пробелы и Клод писал за хумана. Без стоп токенов происходит литерали следующее:
Хуман: помоги мне
Ассистент: бла бла бла, помог
Хуман: Спасибо бро!
Это всё пишет просто ЛЛМ, но чтобы юзер мог с ней писать, начинается напяливание совы на глобус. Разрабы сделали хумана стоп токеном. В результате, когда ассистент дописал, его прерывают и не дают писать за хумана, за него пишешь ты. И если ассистент (вот те самые строчки с Ассистент:, а не сферический ассистент на стороне куктропиков. Его на самом деле не существует, он просто фикция и за ассистента пишет ЛЛМ) обучается на то, чтобы давать один конкретный ответ на запрос юзера - он всегда будет пытаться давать один ответ, короткий и с теми самыми "закруглениями", потому что идёт файнтюн на асисистента. Так что как ты проект не отправляй, в своей сути из за файнтюна ассистент всегда будет пытаться отвечать одним сообщением. И в промпте может не быть хумана и ассистента вообще, все эти ограничения это ничто иное, как ограничение со стороны АПИ куктропиков, искусственное. Весь ассистент искуственный, целиком.
3.
>Ну короче пусть сам ХМЛ-шиз все разъяснит опять, тут шиза невиданных масштабов, а я спать пошел
Ну погнали. Я на юшке выше объяснил, как работает хуман ассистент, в чем я убедился на 100% на опыте, когда пердолился с форматом промпта.
Моя задумка простая в своей идее- заменить хумана и ассистента на {{user}}: и {{char}} (Дохуя чаров на самом деле). Но возникает дохуя проблем из за наших любимых соевичков и их АПИ:
- Нам нужно убрать хумана и ассистента, но так не получится из за АПИ куктропиков. Решение - оставляем ассистента и хумана в самом начале, чтобы они рано или поздно утонули в контексте. Литерали топим хуманов и ассистентов
- Нам нужно чтобы сетка за нас не писала - тут изи, просто добавляем стоп токен {{user}}. Ещё раз для понимания, сейчас есть дефолтный стоп токен Human: и мы его просто "подменяем" на юзера, а хуман должен пердеть в самом начале и утонуть в контексте.
- Отсюда и идёт игра в префиле, где Клод пишет один огромный фанфик с подразумеваемой писаниной за юзера. Но стоп токеном мы не даём писать за юзера и пишем сами.
Теперь опять о плюсах, которые это может дать:
- Нет закругления ассистента, потому что он утонет в контексте и потому что он будет Обрыкаться на полуслове, когда хочет написать за юзера.
- Из за того, что он обрывается, возможны короткие ответы, а не шиза с монологами на миллион токенов
- Из за того, что в сути своей это фанфик промпт, ЛЛМ сфокусирована на сторителлинге, без лишней воды
- Префикс ассистента, который и является квинтэссенцией и первопричиной сои начинает работать слабее и слабее, аполоджайзы невозможны, ведь мы не даём пиздеть ни хуману, ни ассистенту, а аполоджайсят только они.
- Это УЖЕ РАБОТАЛО, Я ЭТО ТЕСТИЛ! Но было жутко неудобно из за того, что я там костылил и пердолился, потому что таверна не перепилена была. Щас даже с пердолингом не получится из за джсонов
Из минусов:
- Инструкции при таком подходе надо реализовывать по другому, но я противник инструкций, они мне не нравятся
- Надо делать фанфик промпт
- Таверна пукнет и обмякнет, если так попробовать сделать но её надо переделать
- Куктропики это уже начали фиксить
- Иногда ассистент подтекает, но гораздо реже, проблема этого ебаного соевого файнтюна
- Это всё работало на 2.1 сейчас не ебу, возможно уже пройдена точка невозврата, когда ЛЛМ может только в соевичка ассистента