📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Schema-guided reasoning: как заставить LLM быть умнее

Mad Devs35:01

Transcription

[музыка] Разговариваем о всяких новинках и интересных вещах из мира машинного интеллекта. Да, сегодня у нас очень интересный доклад. Технология очень перспективная, э, заставила переосмыслить, в принципе, всё управление лмками и некоторую дорожку наметить, что наконец-то можно поверхл делать инженерные настоящие решения, которые тестируем, верифицируем и так далее. Ну, а более подробно сегодня расскажет наш спикер, ведущий МАЛинженер Александр Брыль. Тебе слово.

Спасибо всем. Привет. Без долгих предисловий поехали. О чём мы поговорим? Поговорим о том, что такое схема Guided Reasoning. Коротко, это то, что помогает моделям ризанить, даже тем моделям, которые для этого не обучены. И много параллельных ключек тоже позволяет делать. Но к этому мы перейдём через несколько шагов. Сначала поговорим про strctри Output. Много поговорим, наверное, а потом поговорим про то, как агенты создаются. То есть это довольно важная тема для нас. А почти всю информацию об этом я взял с сайта Рената Абдулина. Можете, я думаю, мы пошарим презентацию. Можете посмотреть, что там есть. Здесь, собственно говоря, прямо всё по плану. То есть мы сначала поговорим про strctчери output, потом про паттерна SGR, примеры демо, демо в виде агента. И также есть ещё Telegram-канал, в котором тот же Ренат рассказывает, называется М под капотом, рассказывает про всякое практическое применение. И здесь на том же сайте, и в его канале даже есть более, возможно, интересные вещи с практической точки зрения. Это, а, бенчмарки, оценки ЛМА в бизнес-задачах. То есть мы все видим, как каждый день выходят новые модели, они там выбивают какие-то метрики непонятные, неизвестные, а на бизнес-задачах вообще непонятно, как они себя ведут. То есть здесь, а, дана такая сводка. Но это факультативно, это не тема нашего разговора.

Так, про стракширит Output хотел бы напомнить историю небольшую. Дело в том, что в 2017 году, когда изобрели трансформеры, до двадцать второго года они никому нужны не были. Я имею в виду никому, кроме ML инженеров, бизнес вообще не думал об этом. Пока в двадцать втором году Open AI не догадались переменить подход с обучением дотюнивание модели на инструкциях, получив констракт модели, тот самый чат GPT, для того, чтобы пользователи могли работать в более интерактивном, что ли, виде, не промть непосредственно тексты, а именно писать инструкции и получать ответы на свои вопросы. А, и тогда бизнес заинтересовался. Стало интересно, что можно делать. Я помню, что в том же двадцать втором году как раз мне пришло приходилось в том числе писать, генерировать э всеоптимизированные карточки товаров, э, включая HTMльтеги. И с этим были проблемы. То есть тогда уже возникли проблемы с форматированием, не только с HTML, в принципе, с любым форматированием. И возникла потребность с тем, чтобы генерить что-либо JON подобное. начали тюрнить модели на генерацию джесонов и Jon SH. Стало гораздо лучше. Помню последний отчёт добавления страш от Open. А в нём заявлялось, что 99% всех запросов с форматированием объекта выполняются успешно. В принципе, на этом можно было закончить, но они пошли дальше. И на мой взгляд, я точно не помню, может старожилы помнят, хотя это было только год или полтора назад. Первым появился Function calling. А по сути это тоже тюнинг моделей на генерацию Jonх с целью парсинга из пользовательских запросов аргументов для функций. Соответственно, эти функции и были представлены в виде JSON схем. И это очень было похоже на тот же structured output, потому что structured output тоже позволяет нам парсить gonх. А я даже помню, что в документациях также описывалось, как можно получить структурированный выход через function calling, но это уже не принципиально. То есть страх через output, несмотря на то, что, по-моему, без него, в принципе, с лмками работать нельзя, появился относительно недавно. Это ключевой момент. Хотя нет, не ключевое, но неважно.

А прежде чем говорить о непосредственном его применении, надо отметить важную деталь, то что structur output может поднять нам метрики, ээ, бизнес-метрики, о чём я не раз, наверное, сегодня скажу, но он не дистиллирует никакие знания из модели, то есть она не сможет дать нам то, чего она не знает. И это хорошо демонстрируется на примере, который здесь представлен. Сейчас я его опишу. Дело в том, что что structured outp, любые гайденсы к передаваемой в лмку работают по принципу ограниченного адекодирования. То есть, по сути, мы зануляем логиты тех токенов, которые не хотим видеть в нашем ответе. И здесь как раз описан пример того, как мы это делаем. Для этого я взял специально модель потупе, но она, кстати, неплохая, но такая небольшая, на 4 млрд параметров 3, а миниист и прям пытаюсь вытащить из неё рецепт майонеза на грузинском языке. При этом все негрузинские символы, токены зануляются с помощью маски. И мы видим, что модель ожидаемо замкнула, то есть генерится один два одинаковых слова, потом какие-то проценты и больше ничего. Там это дальше продолжалось, я просто не стал всё вставлять. На самом деле, даже с современными более умными моделями просто небольшого размера, м до сих пор у меня бывает, получается такие вот вещи получать, когда модель замыкается и генерит одинаковые токены. в том числе на Strcturit Output.

Ну и непосредственно к применению. Самый простой пример, как мы можем его использовать, я буду заранее не предупредил, я буду рассказывать, как будто вот никто ничего об этом не знает, чтобы было максимально понятно. Хотя, я думаю, многие уже применяли это. А мы просто описываем модель данных, которые хотим получить из аллемки. Например, здесь это календарь eventт с полями. string list стрингов и передаём в качестве responsсформата в endp нашей модели. Ну, в данном случае это Open AI с endpoint response pars. И на выходе мы даже можем не писать никакой системный промт особо, то есть просто извлеки информацию об ивенте. Evвен передаётся в текстформате и дальше из переданного сообщения формируется модель на выходе. То есть не просто строка, не просто JSON, она уже готовая подентик модель.

Пример посложнее - это из моей практики. Почему я его вставил? Потому что он демонстрирует два ключевых момента. Мы можем, во-первых, делать поident модели с поident модели. То есть здесь, э, задача у меня состояла в том, что нужно было генерировать ревью из сырого текста в определённом структуре. Для этого каждое поле представляло из себе вопросну ответную структуру. То есть у меня есть бэкграунд, который состоит из листа вопросов и ответов. Вопросы ответов своей identтик модели, backкграoунд своей поident модели. И второй важный момент - это то, что мы можем описывать дескрипшены, а, к нашим полям. И эти дескрипшены автоматически подтягиваются в каком-то виде в промт. То есть мы даже не обязаны описывать сложные промты. Мы можем каждое поле описать в дескрипшене. В промте оставится только максимальная общая инструкция, и это будет работать.

Ну и последний пример, который, Ой, как я сюда попал. Вот, э, последний пример сокчерита, который хотел показать- это связан как раз по сути со схема Guided, относительно любую, совсем маленькую, конечно, вряд ли получится, а, заставить ризанить, просто передав определённую схему данных. В данном случае мы хотим, чтобы наш GPT4O, который имеет резонин модель, решил математическую задачку. уравнение какое-то. Ну, и а мы описываем схему данных в виде шагов. На каждом шаге модель должна сгенерировать объяснение своих действий и выход на этом действии и потом сгенерировать финальный отпу. Мы видим, что в качестве результата мы получили по сути шаги ризонинга, где модель шаг за шагом решила наше уравнение. А из личного опыта также могу добавить, что просто добавив шаг explanation, полиш, точнее, попросив модель объяснить своё решение, можем, во-первых, взять модель поменьше, и она покажет лучшие результаты, чем модель побольше без этого поля. А, во-вторых, мы также в при описывании схемы данных должны следовать промту. То есть, если мы впрате пишем сначала объясни своё решение, потом сгенерируй финальный ответ, значит ты схема данные должна состоять из explanation и result в этой последовательности. И также ещё хотел, возможно, не очевидный, возможно, кому-то очевидный момент подсветить то, что нужно быть аккуратнее с поддейковалидации. То есть в этих же филдах помимо дескрипшена можно написать а ограничение, например, на длину сгенерированного текста. Но валидация происходится на этапе постпроцессинга. Она никак не влияет на то, как модель генерируют ответственност. То есть мне нужно было в моей задаче получить summary из revюw а строго ограниченной длины 2300 символов и фразы после первой генерации просто обрезались по двумстам и по 300м символам соответственно это, конечно, мне не подходило. Как решать такую задачку, забегая вперёд, это один из паттернов СГР, как раз о котором поговорим чуть позже. А решать можно механически прямо, то есть сгенерировать sumy, допустим, без ограничений, померить функцией Python Skylн наши Sum отправить это всё на повторную генерацию с инструкцией сократить текст, например. Вполне такое может сработать. А можно описать схему данных, которая будет делать это тоже заставлять, которая будет заставлять модель делать то же самое. То есть я описал, а моё саморе с ограничениями в качестве шагов, а в качестве resolution я попросил модель проверить, что фразы не обрываются, и модель сама генерирует меня шаги с моими sumary с промежуточными и меняет их на ходу. Вот. То есть по факту у меня там было один-два, в редких случаях три шага генерации, пока я не получил необрезанные тексты.

А какие плюсы и минусы у в использовании structчери? Самый большой плюс то, что мы контролируем генерацию и повышаем бизнес-метрики. Ну, и в какой-то мере снижаем галлюцинацию. Как я уже говорил, мы при этом рискуем замкнуть модель. То есть модель может выйти в цикл, где генерируют одни и те же токены. опять же, даже с 41 mini, которая вроде относительно недавно вышла, это тоже актуально. Также те, кто много работает со Strctural, могли заметить, что формулировки отличаются. То есть мы можем попросить сгенерировать GSON, казалось бы, с одним и тем же запросом, и он будет отличаться по разнообразию формулировок в себе, чем по сравнению со Strakри Output, потому что там, а, токены, как я говорил, а, ограниченное декодирование и, в принципе, токены такие менее разнообразные. И всё это может в редких случаях даже привести к снижению точности, если нам это важно.

И теперь переходим непосредственно к схема Guided Reasoning. По сути, всё, что нам нужно знать для схемы гай, мы обсудили. Мы просто должны в нашей модели, которую мы передаём в качестве responс формата, описать шаги рининга, чтобы заставить нашу модель ризанить. А мы сами определяем эти шаги. Мы определяем, в какой последовательности они будут выполняться. И это всё приводит к улучшению понимания точности. А здесь пример с сайта как раз-таки, где мы описываем для какой-то задачи, во-первых, нашу антологию, то есть классификацию. Сразу уже предотвращаем галлюцинацию, потому что ограничиваем, а, варианты того, что модель может сгенерировать. И далее шаг за шагом описываем, что должна модель сделать, сгенерировать постепенно. То есть сначала она отвечает на вопрос, это про задача связана с анализом каких-либо документов. А что, подходит ли нам этот документ? Если а что должно быть изменено в документе, соответствует ли документ требованиям? Так, шаг за шагом мы в итоге приходим к какому-то финальному результату. А идея довольно простая, на мой взгляд, но у подхода СГР есть свои паттерны. Каскат только что был описан. прямолинейный шаг за шагом. Роутинг поговорим сейчас. Вот пример роутинга. Роутинг, э, уже близок к применению СГР для прототипирования, для проектирования агентов, потому что этот паттерн предполагает выбор, а, то есть роутинг в какие-либо тулы. Когда мы говорим об агентах, мы чаще всего говорим об мультитулаагентах, соответственно. В этом контексте роутинг предполагает, что мы заставляем модель выбрать ту тулу, которая подходит для выполнения заданной задачи. Для этого мы можем эти тулы описать в виде пайден моделей, то есть отправить email, допустим, обработка имейлов, да? А поиск в базе знаний, создать тикет. И создав таким образом тылы, можем их добавить в качестве, допустим, поля action в respнсформате. И, допустим, и в таком случае на запрос, а, отправить email по адресу, модель может сама сгенерировать ответ примерно такого вида. Здесь он набран вручную, но это может быть ответ модели. То есть она выбрала сама sent email tool и заполнила даже сама сгенерировала для неё. А в практике магентов это называются агентами, по-моему. Не уверен счёт произносишения, но неважно. И последний паттерн цикл, о котором я писал по при описании примера с генерации summary. То есть можем заставить модель самому самой модели генерировать шаги, планирование какое-либо выполнять с заданными ограничениями. То есть вот данный пример, что нам говорит? А в серверной комнате слабая вентиляция и что-то там дет, господи, забыл. Ну неважно. А в общем, защита слабая. И предлагается модели решить, к чему это может привести. И предлагается в виде схемы. То есть задаются ри риск факторы и предлагается сгенерировать такие факторы в размере от двух до четырёх. И мо даль шаг за шагом а генерирует такие ответы.

Преимущество такого подхода в принципе СГР. Мы можем сгенерировать рассуждение chain of thoughts для почти любой модели. То есть если у вас есть ограничения на бюджет или вам необходимо использовать локальную модель, но нет ресурсов, чтобы развернуть большую модель, вы можете взять модель поменьше. и попробовать её зафорсить, повысить её качество ответов. Мы контролируем полностью генерацию, можем проверить, на каком этапе у нас что-либо отвалилось и почему. И всё это приводит к повышению бизнес-метрик.

И теперь перейдём к агентам. Как же строить агентов с помощью СГР? А в общем случае будем рассматривать мультитуOL агента. Ну да, понятно, есть мультиагентные системы, но это, в принципе, расширение мультитулаагента. И когда мы говорим об мультитул-агенте, мы подразумеваем обычно React агента. Мм, для если кто не знает, то React расшифровывается как? А как он расшифровывается? Господи, представляете, я забыл. Кто подскажет, Ром, подскажи. Я забыл, как реакт агент расшифровывать. А, reason. Всё, точно, логично. Reasonг. А, reasoning и акт, то есть действия. А в основе Реактагента лежит, по сути лмка, которая знает набор тулов, который ей предоставлен, и может сама планировать действия. может вызвать одну тулу, посмотреть на результат работы этой тулы, вызвать другую тулу, может собрать цепочки тулов, и всё это конфигурируется, по сути промтом. После того, как все тулы отработали по мнению лмки, она формирует из ответов тулов собственный ответ. А я сейчас даже, наверное, переключусь на Python CД, чтобы продемонстрировать, как просто это всё делается с имеющимися фреймворками. Допустим, будем использовать Нграф, как самый популярный, на мой взгляд, фреймворк. Допустим, у нас есть, э, задача обрабатывать входящие имейлы. Эту задачу я взял, к слову, из демо для СГР, поэтому, но немного упростил, потому что там довольно сложный пример, всё нам не нужно, чтобы продемонстрировать, как это работает. Соответственно, у нас есть, допустим, какие-то продукты, которые мы продаём, курсы по построению искусственного интеллекта, а имейлы, с которыми мы работаем от наших клиентов, и правила. можно добавить правила для этих имейлов типа кому-то давать всё время скидку или кому-то не запрещать покупать курс. И в общем видели реактагента мы описываем тулы в виде Python функций, где описываем аргументы, которые лмка сама парсит из user сообщений. Чем детальнее мы опишем докстрингу, которая также конкатонируется в промт, тем результат работы тулы будет лучше. Ну, а внутри тулы может быть любой Python cod. В данном случае мы просто добавляем то, что распасил распарсила модель в нашу импровизированную database. Вторая первая тула для отправки сообщений, вторая тула для создания правил. И третья тула- просто вытаскивает данные из датабейса для а имейла, который содержится в строке запроса. Мы должны собрать тул-ноду. В принципе, кстати, вариантов собирания это всего более, чем один, но это не принципиально. Выбираем нашу лмку и описываем systemпромт. В данном system промте я по сути задаю два условия, чтобы агент всегда проверял данные кастомера перед тем, как обрабатывать ail, отправлять его. В данном случае больше у меня никаких сейчас тулов других нету. И также я говорю, чтобы SNML всегда был последним действием. И агент создаётся вот несколькими строками кода. Естественно, есть свои расширения. Можно добавлять стейты, можно работать со стейтами, чекпоинтеры для того, чтобы хранить промежуточное состояние. Это всё детали, которые сейчас не интересуют. И проверим, как работает такой агент на трёх тасках. То есть я как пользователь, допустим, говорю ему сначала добавь правила, чтобы запретить покупать курс по определённому имейлу, говорю, а одобрить покупку курса для другого имейла и говорю: "Разберись с имейлом, там кто-то просит купить курс". Ну, соответственно, тот же имейл, которого мы запретили. Посмотрим, как это сработает. В первом случае при отправке этого сообщения первого создать правила. Что происходит? А, эяйка вызывает тулу, как я и сказал. Сейчас посмотрим, чтобы было удобней. Секундочку. А, вызывает тулу get customer data, как я и сказал, get customer data возвращает пустой ответ. Она вызывает тулу, а, создать правила. И туа создаёт правила. И Айка, в конце концов нам возвращает сообщение, что правило добавлено. Смотрим в имейлы. А, в правила, да. Правило добавлено. Следующее, одобряем маску, покупку курса. Всё то же самое. В конце у нас AIL создался. И последнее, а Skynet просит купить курс по созданию искусственного интеллекта. Все те же шаги выполняются, но у нас добавляется сообщение о том, что отправляется сообщение на почту Skynet о том, что доступ запрещён к этому курсу. Всё удачно отработало. Конечно, можно это всё кастомизировать более детальными инструментами. Например, я в промте здесь писал о том, что нужно всегда проверять кастор да, например, правильно, как раз-таки зачем это отдавать накупломки, которая не детерминирована, если мы можем механически это выполнять первым нашим шагом. Это можно действительно делать механически перед, собственно говоря, вызовом Инволка лмки убрать её слов. Но для демонстрации я просто пересоберу граф а нашего агента. Для этого нам нужно задать стейт. К сожалению, это обязательно. В стейте по сути хранятся просто сообщения. Убираем тулу из списка тулов. Собираем новую модель. А также нам понадобится ещё одна отдельная модель для того, чтобы на первом шаге парсить почту и сообщения для того, чтобы вызвать get customer дата. Описываем ноды. В принципе, я не буду, наверное, сейчас на этом сильно останавливаться. Это стандартная нода. То есть это нода для а вызова тулов, это нода для вызова агента непосредственно модели основной. Это нода для того, чтобы проверить, что все тулы отработали. Если они отработали, мы завершаем выполнение. Если не отработали, продолжаем вызывать тулы. И это кастомная нода. Ну, то есть вот предыдущие три, по сути, они во всех сервисах будут одинаковы. А вот четвёртая нода - это кастомная, которую создаю под свою задачу, где я сначала отдельной мкой вытаскиваю Iail сообщения. Для этого есть прот. А потом, а потом, потом, потом делаю invoke toлы get customer dat по задано, то есть вытаскиваю данные избдшки и далее собираю граф, добавляю в него ноды, добавляю рёбра. И визуализация выглядит так. То есть на каждое пользовательское сообщение мы вытаскиваем данные по имейлу. И дальше агент работает в штатном режиме. Аа обновля обнуляем нашу пдшку и проверяем, что все таки точно так же работают. Не буду подробно останавливаться. Единственное, что замечу, что вторым шагом также human message, потому что я так прописал с вытащенными данными из бдшки. нас, а по факту поведение ровно такое же. Все те же эмолы созданы, всё хорошо отработано.

Возвращаемся к презентации. Это ссылка, если мышарим эту презентацию на эту тему. А я хотел отдельно отметить один момент. Этот левый ум может заметить, что при реализации реактагента через фреймворки мы не задаём последовательность действий, мы не проверяем её никаким образом для нашего агента. И сам сама ЛМКА планирует свои шаги. И самое важное, она на основе ответов от тулов корректирует свои шаги. Она не планирует на несколько шагов, она планирует на несколько шагов вперёд, но она может их менять. как это реализовано в виде SGR агента. Как раз за счёт вот этого цикла. То есть мы сами задаём некоторое количество шагов, которые мы выполняем. Да, что же такое? Как вернуться? Вверх что ли промотать? Мы сами задаём количество шагов, которые мы выполняем. А результат работы тулы, если вы помните, мы там говорим о том, чтобы тула спланировала свои дальнейшие шаги, там пять шагов вперёд, но мы не идём по этим шагам. Мы всего лишь результат работы тулы добавляем в контекст ответа и отправляем его на следующем шаге в ту же самую модель, и она планирует всё заново. Это нам позволяет не идти по неправильному маршруту, который мог быть сгенерирован для первого ответа, а адаптивно корректировать свои шаги. Очень здорово.

Какие же плюсы и минусы? Я думаю, минусы все заметили, что всё нужно делать самостоятельно. Это может быть довольно сложно, много инженерной работы, но для меня самый большой плюс от такой работы - это то, что мы можем покрыть тестами практически любой шаг аа нашего агента. И на опыте могу сказать, что постепенно мы приходим к тому, что как будто нужно реализовывать именно в такой парадигме наших агентов, чтобы всегда знать, где мы находимся.

Так, на этом наконец-то всё. готов ответить на вопрос.