- BrainTools - https://www.braintools.ru -
Пробовали сделать цифрового сотрудника на LLM?
У вас получилось?
Долго старались?
Насколько он качественный?
Сколько он стоит в обслуживании?
Очень много вопросов возникает к технологии ведения диалогов нейросетями.
Сейчас технология LLM переживает бум. Тот случай, когда предложение порождает спрос само на себя. У бизнеса много запросов на создание ботов для общения с людьми: боты-консультанты, боты-продажники, боты-психологи, боты-планировщики, боты-астрологи и т.д. При всём многообразии запросов и многообразии LLM я не смог найти достойного решения для простой задачи: как заставить бота следовать скрипту? Даже самый недалекий человек легко справляется с задачей, когда у него есть четко прописанная инструкция. Но даже самая умная на сегодняшний день LLM не следует инструкции. И чем длиннее скрипт для сотрудника, тем сложнее заставить нейросеть его соблюдать.
Суть проблемы — слишком широкое окно контекста у LLM. Человек за свою жизнь привык жить со своим маленьким окном контекста, поэтому, приступая к большой задаче, он «ест слона» по частям, выделяя из множества информации именно то, что сейчас ему актуально. LLM же одновременно опирается на всю инструкцию, и на длинных текстах её работа превращается в хаос вместо структурированного диалога. Для решения больших задач делают агентов, которые умеют разбивать задачу на части и решать её поэтапно, как это делает человек. Но количество вызовов нейросети (а соответственно, и стоимость диалога) при этом вырастает в разы, зачастую делая применение LLM нецелесообразным.
При разработке своего бота я столкнулся с узкими рамками борьбы между стоимостью и качеством диалога. Я не мог позволить себе строить сложную агентскую сеть для решения довольно простой задачи – следовать скрипту. И тогда я решил «приземлить» LLM к скрипту более дешевым и эффективным способом, не сжигая лишние токены, а задав структуру сценария жестко в логике [1] бота.
Каких-то вменяемых готовых решений беглый обзор GitHub не выявил. Тогда я решил сделать своего агента «с блэкджеком и дешевыми токенами», который будет следовать четко установленной инструкции. И, поскольку решаемая проблема следования инструкциям типовая для самых разных применений, решать задачу я решил в общем виде. Идею сформулировал так: “Нужна библиотека для построения чат-ботов, которые смогут вести диалог по заранее определенному сценарию. При этом сценарий должен храниться отдельно от кода и быть легко заменяемым.” Движок – отдельно, предметная – область отдельно. Главная идея: Граф ведёт, модель исполняет.
Код можете найти на github: https://github.com/chechestor/tg-statemachine-bot [2]. В репозитории открыт раздел с обсуждениями, добро пожаловать.
В основу были заложены следующие принципы:
Весь диалог разбивается на этапы, нет общения вне этапа диалога.
Этапы объединены в граф с направленными переходами. Из этапа может быть несколько выходов в зависимости от результатов этапа.
У каждого этапа есть определенная цель. Как правило, это получение некоторой информации из ответов собеседника.
Входная и выходная информация каждого этапа организована в именованные переменные. Выходные переменные одних этапов могут быть входными переменными других этапов.
Общение на каждом этапе ведет LLM. Она же принимает решение о необходимости перехода на следующий этап, формируя соответствующий сигнал перехода.
Так в основу архитектуры легли три модуля:
fsm – (Finite State Machine) машина состояний пользователя. В ней хранится граф состояний диалога и текущее состояние пользователя.
vars_memory – память [3] переменных диалога.
conversation_core – собственно LLM-болталка. Оркестратор для fsm и vars_memory.
Деление инструкции по этапам – это основной механизм, который позволяет LLM работать по скрипту любого объема. За счет того, что в конкретный момент времени модели передается только инструкция текущего этапа.
Инструкция для вызова LLM собирается как композиция:
общая роль и правила (common.md);
инструкция текущего этапа;
только те входные переменные, которые для этапа явно прописаны (stage_input);
прогресс этапа: что уже заполнено, чего ещё нет;
доступные сигналы перехода.
Модель на каждом ходу отвечает не свободным текстом, а фиксированным контрактом из четырех полей:
response_message – что сказать пользователю;
fsm_signal– какой переход совершить (или null, если ещё рано);
stage_result– структурированные данные этапа;
stage_summary – саммари при завершении этапа.
Как известно, LLM умеют нарушать инструкции, а иногда и врать. Даже если в инструкции четко описаны все сигналы, допустимые для текущего состояния, модель может выдать несуществующий (я называю это «на основании собственного опыта»). Поэтому в conversation_core вокруг вызова LLM дополнительно введена обвязка, которая при обнаружении нарушений контракта делает перезапросы в модель от роли developer, указывая на проблему и требуя исправить её. К таким ошибкам относятся, например: нарушение JSON-схемы ответа, несуществующие сигналы перехода, отсутствие результатов этапа при попытке перехода. Для связанности диалога я решил не «изобретать велосипед», а воспользоваться готовым решением – ведением цепочки Responses API через previous_response_id. Так даже при переходе между этапами сохраняется адекватность общения без резких смен темы, переспрашиваний и т.п. Одновременно такое решение уменьшает расходы за счет cache-read с более дешевой ставкой. В ходе работы над кодом обнаружилась потребность [4] применять LLM не только для общения, но и для агрегации ранее полученных знаний. Поэтому пришлось выделить отдельный вид этапов – так называемые технические этапы, на которых модель без видимой для собеседника активности «что-то обдумывает внутри себя». Например, когда нужно составить отчет по этапу и сохранить его в переменные.
С переменными всё просто – это обычное хранение значений по ключу, где имя переменной является ключом.
Можно было воспользоваться готовыми БД, но было желание сделать ещё и историю изменений переменных, поэтому сразу заложился на отдельный модуль. Кроме того, не хотелось тащить в проект ещё одну БД.
Основное назначение модуля – ориентироваться по состояниям общего скрипта и предоставлять данные только актуального этапа. Общая схема диалога описана в виде YAML-файла. Объемные инструкции этапов вынесены в отдельную папку /instructions/. YAML и инструкции этапов – этого достаточно для того, чтобы сменить прикладную область бота – сам движок менять не нужно.
Немного о практиках, которые я для себя выработал, фтыкая в проект.
Писать YAML своими руками оказалось максимально скучно и опасно для психики разработчика, а иногда и для физического здоровья окружающих. Поэтому разработку самого YAML сразу стоит поручить вашей любимой LLM. Она с этим справляется отлично.
Но вам надо будет как-то контролировать результат же. Но даже читать YAML своими глазами не советую, утомляет. Чтобы ориентироваться в нейрослопе, пришлось разработал небольшой скрипт /tools/generate_fsm_drawio.py. Скрипт генерирует из YAML диаграмму в формате draw.io, вполне удобную для анализа человеком и контроля результата работы нейросети.
Для тестового периода необходим сбор логов диалогов тестировщиков, чтобы оценивать качество ответов и адекватность поведения [5] бота. Для этого логи собираются в папку /data/chat_logs. Просматривать голый текст глазами оказалось вредно для зрения [6]. Поэтому решил немного «облагородить» логи и разработал небольшой вьюер /tools/view_log.html. Он отображает переписку в виде, похожем на привычный чат, а также выводит спойлеры с системным логом. Удобно смотреть переходы между состояниями. При отладке на локальном ПК можно также включить автообновление, чтобы наблюдать лог в реальном времени.
В репозитории выложен код бота-аналитика. Цель бота – помочь разработчику пет-проекта с постановкой задачи на разработку. Скрипт проводит пользователя по этапам разбора требований, задавая вопросы, которыми начинающие соло-фаундеры часто не имеют привычки задаваться. Этот бот-аналитик не претендует на звание «бота №1» среди аналогичных решений, а приведен лишь как пример применения библиотеки conversation_core.
Итог применения conversation_core получился довольно приятным: на каждое сообщение пользователя приходится примерно 1,03 вызова LLM, что довольно недорого. Бот держит линию диалога, следует скрипту и собирает информацию именно так, как я того и хотел. Он не идеален, есть известные косяки, в том числе для демобота я забил промт инжениринг. Проект есть куда развивать, и, я уверен, обязательно найдется читатель, который лучше меня ориентируется в ботостроении. Буду рад вашим комментариям, отзывам и предложениям.
Автор: chekz
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/33913
URLs in this post:
[1] логике: http://www.braintools.ru/article/7640
[2] https://github.com/chechestor/tg-statemachine-bot: https://github.com/chechestor/tg-statemachine-bot
[3] память: http://www.braintools.ru/article/4140
[4] потребность: http://www.braintools.ru/article/9534
[5] поведения: http://www.braintools.ru/article/9372
[6] зрения: http://www.braintools.ru/article/6238
[7] Источник: https://habr.com/ru/articles/1066298/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1066298
Нажмите здесь для печати.