Как AI пишет мне тексты

Одно из популярных мнений относительно AI заключается в том, что искусственный интеллект так хорошо умеет писать тексты, что его можно натренировать в нужном стиле — и он будет за вас писать ну точно, как вы. Я бы назвал это популярным заблуждением — хотя AI в виде GPT-моделей действительно хорошо генерирует тексты, все же натренировать его под чей-то специфический стиль не так уж и просто.

Однако я попробовал. И хочу рассказать, что именно получилось.

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

Однако со временем проблема (для решения) начала вырисовываться. Дело в том, что подавляющее большинство исходных новостей для телеграм-канала существует на английском. Я свободно (и даже быстро) читаю на нем, но несколько раз сталкивался с тем, что упускаю важные детали, а то и не улавливаю суть новости в ходе такого беглого сканирования. Это приводит к лишним тратам времени — перечитать, для верности какие-то фрагменты перевести переводчиком, перепроверить по другим источникам — в общем, это становится работой. Я попробовал воспользоваться AI как раз для проверки — беру текст новости, текст написанного комментария, отдаю в AI (ChatGPT или Claude) с просьбой оценить соответствие комментария новости. И, хотя результат мне понравился, но количество механических движений окончательно подвело к мысли, что вот как раз это надо начинать автоматизировать.

Проверка гипотезы

Прежде чем заниматься разработкой какого-то автоматизированного решения, я решил заняться проверкой гипотезы, что система вообще способна обучиться писать в моем стиле.

Прежде чем продолжать, я хочу сделать небольшой disclaimer. Всё, что я описываю, не является промышленным решением и вы вряд ли сможете так подойти к решению реальных задач. Кроме того, немалая часть моего интереса к решению проблем имеет исследовательский характер. Точно так же, когда AI пишет мне тот или иной код, я часто ограничиваюсь простым копированием кода в проект и копированием ошибки в чат — мне полезнее разобраться, как он разбирается с ошибкой и что в итоге выходит, нежели быстро решить проблему.

Но я не буду здесь описывать то, как программирует AI — будем решать проблему более высокого уровня.

Итак, я хочу каким-то образом натренировать модель, чтобы она писала тексты в моём стиле. Существует несколько разных подходов для этого:

  • fine-tune исходной модели. В этом случае модели даётся датасет, на основании которого модель дообучается, изменяя исходные веса, и результирующая модель обладает знанием для решения специфически ваших задач.
  • использование адаптеров — небольших модулей, которые обучаются под вашу задачу, а в дальнейшем, получив запрос, перезадают его в оригинальную модель, полученный ответ модифицируют в соответствии со своей логикой, и возвращают модели.
  • few-shots learning, когда модели вместе с запросом передается несколько примеров решения аналогичных задач.

Адаптеры я отклонил сразу — мне хотелось использовать модели OpenAI или Anthropic, а адаптеры требуют доступа к самой модели. Fine-Tune доступна для OpenAI — можно натренировать, например, gpt-4o. Anthropic не позволяет использовать fine-tune, но настойчиво рекомендует использовать few-shots learning. Поэтому естественно было попробовать два самых простых для реализации варианта и сравнить результат.

С few shots learning было просто — десяток пар «Текст новости — комментарий к ней» можно набрать вручную, промпт составить несложно, тем более, что у Anthropic есть даже оптимизатор для них.

С fine tune была проблема. Дело в том, что модели требуется датасет в размере нескольких сотен примеров. Если с комментариями у меня всё было в порядке — архив телеграм-канала составляет около 5 тысяч текстов, — то с текстами новостей сложнее, они находятся в интернете и это еще полбеды. Главная беда заключается в том, что они часто находятся за пейволлом, и их невозможно достать через публичные сервисы или каким-то локальным роботом. Так что в проекте появился подпроект — сбор обучающей выборки.

Вот тут настало время подключить AI — мы начали писать робота, который из архива канала возьмет несколько сотен URL-адресов источников, сходит моим настольным браузером по этим адресам, выделит контент новости и сохранит его в базу. Не могу сказать, что это было просто — например, сначала AI написал робота, который запустил Chrome в headless-режиме, сбросил все куки — разумеется, что везде он получил либо сообщение о подписке, либо капчу, поскольку в таком режиме Chrome сообщает, что им управляет робот. После исправления всех таких проблем решение получилось не очень стабильным, но за выходные робот всё же набрал около 700 проиндексированных страниц. Еще один, написанный AI, скрипт подготовил обучающую выборку и я отправил результат в OpenAI.

Скажу сразу — результат был сомнительным. Даже технические параметры — training loss и validation loss — показывали, что модель либо не сбалансирована, либо переобучается. Когда же я попробовал дать ей новость для комментария, результат выглядел совершенно не моим, более напоминающим какого-то залихватского ютубера.

Результат работы Claude 3.5 с десятком примеров был намного качественнее. Возможно, fine-tune с парой тысяч примеров дал бы результат получше, но проблема сбора выборки меня сильно смущала. Кроме того, возникала бы и проблема дообучения при добавлении новых примеров.

Таким образом, было понятно — выбираем Claude, даём ему примеры, получаем ответ. Остаётся это автоматизировать.

Автоматизируем процесс

Итак, процесс вручную изначально выглядел так — я читаю новость, хочу написать про нее, перехожу в телеграм-клиент, пишу комментарий, добавляю ссылку, нажимаю «отправить». Добавляем использование AI и получаем достаточно сложную последовательность:

  1. Я читаю текст новости в браузере. Мне надо написать к ней комментарий.
  2. Я копирую текст новости в качестве промпта в Claude 3.5 Sonnet, где также заданы несколько примеров парами “news/comment”, и прошу написать комментарий к новости.
  3. Полученный комментарий я модифицирую по своему усмотрению.
  4. Готовый текст комментария и исходный текст новости я отправляю в ChatGPT для оценки соответствия комментария и новости. (Это нужно для проверки, что я не выбросил ничего важного и что этого же не сделал Claude).
  5. В зависимости от результата я либо редактирую комментарий, либо отправляю его в телеграм-канал в формате “текст комментария ссылка”.

Claude довольно быстро соорудил React-приложение, которое позволяло ввести текст новости и ввести комментарий. И мы начали наращивать функциональность. Отправить текст новости вместе с примерами в API Claude оказалось самым простым. Но я же хотел автоматизировать — то есть надо было автоматически извлечь текст новости.

Так появилось расширение у браузера, которое извлекало текст новости и отправляло его в React-приложение. Это в итоге стало отдельным проектом, где в итоге мы пришли к использованию библиотеки Readablity и прописыванию отдельной обработки для некоторых сайтов.

Потом мы вернулись к React-приложению. Оно уже умело получить текст новости и URL, отправить примеры и запрос в Claude, получить комментарий. На следующем шаге мы добавили запрос к GPT-4o для оценки результата.

Совершенно незаметно, чуть ли не по своей инициативе Claude, писавший код, добавил функциональность управления примерами. Правда, тут мы немного застряли — приложение регулярно отказывалось удалять примеры при добавлении новых. Это выглядело нерациональным — на самом деле, примерно после 12-15 примеров эффективность увеличения числа примеров резко падает, а передавать 20 примеров стоит дополнительных денег. Но через какое-то время мы это побороли.

Улучшать можно бесконечно — я параллельно предложил v0.dev нарисовать интерфейс для приложения и получилось модно и современно. Выложив приложение в интернет — чтобы не зависеть от локально запущенного приложения, — логичным было добавить авторизацию. Добавить отправку комментария прямо в телеграм-канал было просто частью изначального замысла, а вот копирование его в буфер обмена я добавил, чтобы немедленно постить заметку в веб-зеркало канала.

Итог

В общем, после примерно месяца использования с периодическим исправлением багов и неудобств процесс выглядит так:

  • я читаю новость на сайте и решаю написать комментарий.
  • нажимаю кнопку на панели браузера, она открывает новую вкладку с приложением, где в исходном табе открыт исходный текст новости.
  • после нажатия кнопки «Generate comment» проходит некоторое время и в другом табе появляется заготовка комментария.
  • я правлю комментарий — обычно удаляю часть мелких деталей и добавляю пару фраз реально своего комментария.
  • после нажатия кнопки «Evaluate comment» в третьем табе появляется оценка соответствия комментария новости. Сначала GPT замечал, что автор не выдерживает строгого тона, когда я указал, что автору свойственен иронический подход, он стал жаловаться, что в комментариях недостаточно иронии.
  • если мне всё нравится, я нажимаю кнопку «Send to Telegram» и копирую текст в буфер обмена для отправки в веб-зеркало.
  • если мне кажется, что комментарий характерен для моего стиля, я нажимаю кнопку «Save example» и приложение сохраняет текущую пару в файл примеров. Их можно просмотреть в четвертом, последнем, табе, удалить какой-то из них, если хочется.

Есть еще один способ доработки модели — дать ей возможность поискать по истории постов, чтобы писать комментарий не только исходя из текста новости и примеров стиля, но и учитывая предыдущие комментарии на похожую тему или с упоминанием компаний или личностей. Модели это умеют — вызывать функции, в том числе поиск, и использовать результат. Поиск мы уже написали, остаётся только подключить его к приложению. Что интересно — AI при теоретическом обсуждении проявляет немалый энтузиазм и уверяет, что качество комментариев заметно повысится.

Но мне пока и так хорошо.