newsmode
search
Меню
arrow_back Назад

Your AI Product Needs Evals –

auto_awesomeКраткое саммари

Хамель Хусейн, независимый консультант по AI-продуктам и в прошлом руководитель команды CodeSearchNet (предшественника GitHub CoPilot), утверждает, что почти все неудачные LLM-продукты объединяет одна причина — отсутствие надёжных систем оценки (evals). На примере Lucy, AI-ассистента для риелторов от компании Rechat, он показывает, как систематическая оценка помогает преодолеть плато, когда prompt engineering перестаёт работать и правки превращаются в игру «убей крота». Автор описывает три уровня оценки: Level 1 — быстрые юнит-тесты и ассерты (у Rechat их сотни), Level 2 — человеческая и модельная оценка с логированием трейсов (Rechat использует LangSmith и Metabase), Level 3 — A/B-тестирование для зрелых продуктов. Ключевая мысль: нужно убрать все барьеры на пути к просмотру данных, писать много тестов под свою задачу и не покупать дорогие универсальные инструменты. Хорошая система оценки бесплатно открывает «суперспособности» — fine-tuning, синтез и курирование данных, а также быстрый дебаггинг, поскольку инфраструктура для них во многом совпадает.

Мотивация

Я начал работать с языковыми моделями пять лет назад, когда возглавлял команду, создавшую CodeSearchNet — предшественника GitHub CoPilot. С тех пор я повидал немало удачных и неудачных подходов к созданию LLM-продуктов. Я обнаружил, что у неудачных продуктов почти всегда одна общая первопричина: неспособность построить надёжные системы оценки.

Сейчас я независимый консультант, помогающий компаниям создавать узкоспециализированные AI-продукты. Надеюсь, что внимательное прочтение этой статьи поможет компаниям сэкономить тысячи долларов на консалтинге. Как бы я ни любил зарабатывать деньги, мне неприятно раз за разом наблюдать, как люди совершают одну и ту же ошибку.

В этой статье изложены мои мысли о построении систем оценки для AI-продуктов на базе LLM.

Быстрая итерация == успех

Как и в разработке ПО, успех в AI зависит от того, насколько быстро вы можете итерировать. У вас должны быть процессы и инструменты для того, чтобы:

Оценивать качество (например, тесты). Отлаживать проблемы (например, логирование и инспекция данных). Менять поведение или саму систему (prompt engineering, дообучение, написание кода).

Многие сосредотачиваются исключительно на пункте №3 выше, что не даёт им вывести свои LLM-продукты дальше уровня демо.1 Хорошее выполнение всех трёх активностей создаёт благотворный цикл, отличающий отличные AI-продукты от посредственных (визуализацию этого цикла см. на схеме ниже).

Если вы наладите процесс оценки, все остальные активности станут лёгкими. Это очень похоже на то, как тесты в разработке ПО приносят огромную отдачу в долгосрочной перспективе, несмотря на необходимость предварительных вложений.

Чтобы привязать эту статью к реальной ситуации, я разберу кейс, в котором мы построили систему для быстрого улучшения. Основное внимание я уделю оценке, поскольку это самый критичный компонент.

Кейс: Lucy, AI-ассистент для недвижимости

Rechat — это SaaS-приложение, позволяющее специалистам по недвижимости выполнять самые разные задачи: управлять контрактами, искать листинги, создавать креативные материалы, управлять встречами и многое другое. Идея Rechat в том, что всё можно делать в одном месте, а не переключаться между множеством разных инструментов.

AI-ассистент Rechat по имени Lucy — это образцовый AI-продукт: диалоговый интерфейс, избавляющий от необходимости кликать, печатать и перемещаться по программе. На ранних этапах развития Lucy быстрый прогресс достигался за счёт prompt engineering. Однако по мере расширения охвата Lucy производительность AI вышла на плато. Симптомами этого были:

Устранение одного режима отказа приводило к появлению других — это напоминало игру «убей крота». Видимость эффективности AI-системы по разным задачам была ограничена и сводилась к проверкам «на ощущение». Промпты разрастались в длинные и громоздкие конструкции, пытаясь охватить многочисленные крайние случаи и примеры.

Проблема: как систематически улучшать AI?

Чтобы пробить это плато, мы создали систематический подход к улучшению Lucy, построенный вокруг оценки. Наш подход иллюстрирует схема ниже.

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

Различные компоненты этой системы я разбираю ниже в контексте оценки.

Типы оценки

Строгая и систематическая оценка — важнейшая часть всей системы. Именно поэтому блок «Eval and Curation» выделен жёлтым в центре схемы. Бо́льшую часть времени вам следует тратить на то, чтобы сделать вашу оценку более надёжной и отлаженной.

Стоит рассмотреть три уровня оценки:

Level 1: Юнит-тесты. Level 2: Модельная и человеческая оценка (включая отладку). Level 3: A/B-тестирование.

Стоимость Level 3 > Level 2 > Level 1. Это определяет частоту и способ их выполнения. Например, Level 1 я часто запускаю при каждом изменении кода, Level 2 — с заданной периодичностью, а Level 3 — только после значимых изменений в продукте. Также полезно покрыть значительную часть тестов Level 1, прежде чем переходить к тестам на базе моделей, поскольку последние требуют больше работы и времени на выполнение.

Строгой формулы того, когда вводить каждый уровень тестирования, не существует. Нужно балансировать между быстрым получением обратной связи от пользователей, управлением их восприятием и целями вашего AI-продукта. Это не сильно отличается от баланса, который приходится соблюдать для продуктов в целом.

Level 1: Юнит-тесты

Юнит-тесты для LLM — это ассерты (вроде тех, что вы писали бы в pytest). В отличие от обычных юнит-тестов, эти ассерты стоит организовать так, чтобы их можно было использовать не только в юнит-тестах, но и, например, при очистке данных и автоматических повторах (используя ошибку ассерта для коррекции) во время инференса модели. Важно, чтобы эти ассерты выполнялись быстро и дёшево по ходу разработки приложения, чтобы вы могли запускать их при каждом изменении кода. Если вам трудно придумывать ассерты, критически изучите ваши трейсы и режимы отказа. И не стесняйтесь использовать LLM, чтобы помочь себе с мозговым штурмом ассертов!

Шаг 1: Пишите тесты с ограниченной областью

Эффективнее всего думать о юнит-тестах, разбивая область действия вашей LLM на функции (features) и сценарии. Например, одна из функций Lucy — способность находить листинги недвижимости, которую мы можем разбить на сценарии так:

Функция: Поиск листингов (Listing Finder)

Тестируемая функция — это вызов функции, отвечающий на запрос пользователя найти листинг недвижимости. Например: «Найди, пожалуйста, листинги с более чем 3 спальнями дешевле $2M в Сан-Хосе, Калифорния».

LLM преобразует это в запрос, который выполняется к CRM. Затем ассерт проверяет, что возвращается ожидаемое количество результатов. В нашем наборе тестов есть три пользовательских ввода, которые запускают каждый из приведённых ниже сценариев, а те, в свою очередь, выполняют соответствующие ассерты (это упрощённый пример для наглядности):

Есть также общие тесты, не привязанные к какой-то конкретной функции. Например, вот код одного такого общего теста, который проверяет, что UUID не упоминается в выводе:

const noExposedUUID = message => { // Remove all text within double curly braces const sanitizedComment = message.comment.replace(/\{\{.*?\}\}/g, '') // Search for exposed UUIDs const regexp = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ig const matches = Array.from(sanitizedComment.matchAll(regexp)) expect(matches.length, 'Exposed UUIDs').to.equal(0, 'Exposed UUIDs found') }

Результаты из CRM, возвращаемые в LLM, содержат поля, которые не должны показываться пользователю, — например, UUID, связанный с записью. Наш промпт для LLM указывает модели не включать UUID. Мы используем простой regex, чтобы убедиться (ассертом), что ответ LLM не содержит UUID.

У Rechat сотни таких юнит-тестов. Мы постоянно обновляем их на основе новых отказов, которые наблюдаем в данных, когда пользователи бросают вызов AI или когда продукт развивается. Эти юнит-тесты крайне важны для быстрого получения обратной связи при итерациях над вашей AI-системой (prompt engineering, улучшение RAG и т. д.). Многие со временем перерастают свои юнит-тесты и переходят к другим уровням оценки по мере зрелости продукта, но крайне важно не пропускать этот шаг!

Шаг 2: Создавайте тест-кейсы

Чтобы протестировать эти ассерты, нужно сгенерировать тест-кейсы или входные данные, которые запустят все сценарии, которые вы хотите проверить. Я часто использую LLM для синтетической генерации таких входных данных; например, вот один из промптов, который Rechat использует для генерации синтетических входов для функции, создающей и извлекающей контакты:

Write 50 different instructions that a real estate agent can give to his assistant to create contacts on his CRM. The contact details can include name, phone, email, partner name, birthday, tags, company, address and job. For each of the instructions, you need to generate a second instruction which can be used to look up the created contact. . The results should be a JSON code block with only one string as the instruction like the following: [ ["Create a contact for John (johndoe@apple.com)", "What's the email address of John Smith?"] ]

Используя приведённый выше промпт, мы генерируем тест-кейсы вроде следующих:

[ [ 'Create a contact for John Smith (johndoe@apple.com) with phone number 123-456-7890 and address 123 Apple St.', 'What\'s the email address of John Smith?' ], [ 'Add Emily Johnson with phone 987-654-3210, email emilyj@email.com, and company ABC Inc.', 'What\'s the phone number for Emily Johnson?' ], [ 'Create a contact for Tom Williams with birthday 10/20/1985, company XYZ Ltd, and job title Manager.', 'What\'s Tom Williams\' job title?' ], [ 'Add a contact for Susan Brown with partner name James Brown, and email susanb@email.com.', 'What\'s the partner name of Susan Brown?' ], ]

Для каждого из этих тест-кейсов мы выполняем первый пользовательский ввод, чтобы создать контакт. Затем выполняем второй запрос, чтобы извлечь этот контакт. Если CRM возвращает не ровно 1 результат, мы знаем, что возникла проблема либо при создании, либо при извлечении контакта. Мы также можем запускать общие ассерты — например, тот, что проверяет отсутствие UUID в ответе. Эти тесты нужно постоянно обновлять по мере того, как вы наблюдаете данные через человеческую оценку и отладку. Главное — делать их как можно более сложными, при этом отражающими взаимодействие пользователей с системой.

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

К слову, в отличие от традиционных юнит-тестов, вам не обязательно нужен 100%-й уровень прохождения. Ваш уровень прохождения — это продуктовое решение, зависящее от того, какие отказы вы готовы терпеть.

Шаг 3: Регулярно запускайте и отслеживайте тесты

Есть много способов оркестрировать тесты Level 1. Rechat использует CI-инфраструктуру (например, GitHub Actions, GitLab Pipelines и т. д.) для выполнения этих тестов. Однако инструментарий для этой части рабочего процесса ещё незрел и быстро развивается.

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

Я рекомендую начинать просто и использовать вашу существующую аналитическую систему для визуализации результатов тестов. Например, Rechat использует Metabase для отслеживания результатов своих LLM-тестов во времени. Ниже — скриншот дашборда, который Rechat построил с помощью Metabase:

Этот скриншот показывает распространённость определённой ошибки (показана жёлтым) в Lucy до (слева) и после (справа) того, как мы её устранили.

Level 2: Человеческая и модельная оценка

После того как вы выстроили прочный фундамент из тестов Level 1, можно переходить к другим формам валидации, которые невозможно протестировать одними лишь ассертами. Обязательное условие для проведения человеческой и модельной оценки — логирование ваших трейсов.

Логирование трейсов

Трейс — это концепция, давно существующая в разработке ПО; это лог последовательности событий, таких как пользовательские сессии или поток запроса через распределённую систему. Иными словами, трассировка — это логическая группировка логов. В контексте LLM трейсы часто означают разговоры, которые вы ведёте с LLM. Например, сообщение пользователя, за которым следует ответ AI, за которым следует ещё одно сообщение пользователя, — это пример трейса.

Решений для логирования трейсов LLM становится всё больше.2 Rechat использует LangSmith, который логирует трейсы и позволяет просматривать их в удобочитаемом виде с интерактивной площадкой (playground) для итераций над промптами. Иногда для логирования трейсов нужно инструментировать ваш код. В данном случае Rechat использовал LangChain, который автоматически логирует события трейса в LangSmith за вас. Вот скриншот того, как это выглядит:

Мне нравится LangSmith — он не требует использования LangChain, интуитивно понятен и прост в использовании. Поиск, фильтрация и чтение трейсов — это важнейшие функции для любого решения, которое вы выберете. Я обнаружил, что некоторые инструменты реализуют эти базовые функции некорректно!

Просмотр ваших трейсов

Вы должны устранить любое трение в процессе просмотра данных. Это означает рендеринг ваших трейсов в специфичном для предметной области виде. Я часто обнаруживал, что лучше построить собственный инструмент просмотра и разметки данных, чтобы собрать всю нужную информацию на одном экране. В случае Lucy нам нужно было смотреть на множество источников информации (лог трейса, CRM и т. д.), чтобы понять, что сделал AI. Это как раз тот тип трения, который нужно устранять. В случае Rechat это означало добавление такой информации, как:

Какой инструмент (функция) и сценарий оценивались. Возник ли трейс из синтетического ввода или из реального пользовательского ввода. Фильтры для навигации между разными комбинациями инструментов и сценариев. Ссылки на CRM и систему логирования трейсов для текущей записи.

Я строил разные варианты этого инструмента для каждой задачи, над которой работал. Иногда мне даже приходится встраивать другое приложение, чтобы увидеть, как выглядит взаимодействие с пользователем. Ниже — скриншот инструмента, который мы построили для оценки трейсов Rechat:

Ещё одно проектное решение, специфичное для Lucy: мы заметили, что многие отказы связаны с мелкими ошибками в финальном выводе LLM (формат, содержание и т. д.). Мы решили сделать финальный вывод редактируемым человеком, чтобы можно было курировать и исправлять данные для дообучения.

Такие инструменты можно построить с помощью лёгких фронтенд-фреймворков вроде Gradio, Streamlit, Panel или Shiny менее чем за день. Показанный выше инструмент был построен на Shiny для Python. Кроме того, есть инструменты вроде Lilac, который использует AI для семантического поиска и фильтрации данных, что невероятно удобно для нахождения набора похожих точек данных при отладке проблемы.

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

Как будет рассказано далее, эти размеченные примеры измеряют качество вашей системы, валидируют автоматизированную оценку и курируют качественные синтетические данные для дообучения.

Сколько данных нужно просматривать?

Меня часто спрашивают, сколько данных изучать. На старте следует изучать как можно больше данных. Я обычно читаю трейсы, сгенерированные из ВСЕХ тест-кейсов, и как минимум трейсы, сгенерированные пользователями. Вы никогда не сможете перестать смотреть на данные — бесплатного обеда не бывает. Однако со временем можно сэмплировать данные сильнее, снижая нагрузку.3

Автоматизированная оценка с помощью LLM

Многие вендоры хотят продать вам инструменты, которые якобы устраняют необходимость в том, чтобы человек смотрел на данные. Хорошая идея — чтобы люди периодически оценивали хотя бы выборку трейсов. Я часто обнаруживаю, что «корректность» довольно субъективна, и вам нужно выравнивать модель с человеком.

Стоит отслеживать корреляцию между модельной и человеческой оценкой, чтобы решить, насколько можно полагаться на автоматическую оценку. Более того, собирая от разметчиков критику с объяснением, почему они принимают то или иное решение, вы можете итерировать над оценивающей моделью, выравнивая её с людьми через prompt engineering или дообучение. Однако для выравнивания оценивающей модели я склоняюсь к prompt engineering.

Я обожаю использовать низкотехнологичные решения вроде Excel для итераций по выравниванию модельной оценки с человеческой. Например, я каждые несколько дней отправлял моему коллеге Phillip следующую таблицу для оценивания — для другого кейса, связанного с генератором запросов на естественном языке. Эта таблица содержала следующую информацию:

model response: это предсказание, сделанное LLM. model critique: это критика, написанная (обычно более мощной) LLM о предсказании вашей исходной LLM. model outcome: это бинарная метка, которую критикующая модель присваивает model response как «хорошо» или «плохо».

Затем Phillip заполняет свою версию той же информации — то есть свою критику, исход и желаемый ответ для 25–50 примеров за раз (это столбцы с префиксом «phillip_» ниже):

Эта информация позволяла мне итерировать над промптом критикующей модели, чтобы со временем сделать её достаточно выровненной с Phillip. Это тоже легко отслеживать низкотехнологичным способом в таблице:

Это скриншот таблицы, где мы фиксировали наши попытки выровнять модельную оценку с человеческим оценщиком.

Общие советы по модельной оценке:

Используйте самую мощную модель, которую можете себе позволить. Чтобы хорошо что-то критиковать, часто требуются продвинутые способности к рассуждению. Для критики выводов нередко можно обойтись более медленной, но более мощной моделью по сравнению с той, что вы используете в продакшене. Модельная оценка — это мета-задача внутри вашей более крупной задачи. Вам нужно поддерживать мини-систему оценки, чтобы отслеживать её качество. На этом этапе я иногда дообучал модель (но стараюсь этого не делать). После того как вы привели модельный оценщик в соответствие с человеком, нужно продолжать периодически выполнять упражнения по мониторингу согласия модели и человека.

В этом примере мы использовали согласие между моделью и человеческим оценщиком, потому что наш датасет был примерно сбалансирован (около 50% случаев были отказами). Однако использование сырого согласия в целом не рекомендуется и может вводить в заблуждение при несбалансированных классах. Вместо этого обычно следует измерять precision и recall по отдельности, чтобы получить более точную картину выравнивания вашего судьи.

Больше всего в создании хорошей оценивающей модели мне нравится то, что её критику можно использовать для курирования качественных синтетических данных, чего я коснусь позже.

Level 3: A/B-тестирование

Наконец, всегда полезно проводить A/B-тесты, чтобы убедиться, что ваш AI-продукт стимулирует желаемое поведение или результаты пользователей. A/B-тестирование для LLM не сильно отличается от других типов продуктов. Если вы хотите узнать больше об A/B-тестировании, я рекомендую читать блог Eppo (который создали коллеги, с которыми я раньше работал и которые являются звёздами A/B-тестирования).

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

Оценка RAG

Помимо оценки системы в целом, вы можете оценивать отдельные подкомпоненты вашего AI, такие как RAG. Оценка RAG выходит за рамки этой статьи, но узнать больше об этом можно в статье Jason Liu.

Системы оценки бесплатно открывают суперспособности

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

Дообучение

Rechat устранил множество режимов отказа через дообучение, что было невозможно одним лишь prompt engineering. Дообучение лучше всего подходит для усвоения синтаксиса, стиля и правил, тогда как такие техники, как RAG, снабжают модель контекстом или актуальными фактами.

99% труда, связанного с дообучением, — это сборка качественных данных, покрывающих область действия вашего AI-продукта. Однако если у вас есть надёжная система оценки, как у Rechat, то у вас уже есть мощный движок генерации и курирования данных! Подробнее о процессе дообучения я расскажу в будущей статье.4

Синтез и курирование данных

Чтобы проиллюстрировать, почему курирование и синтез данных достаются почти бесплатно, как только у вас есть система оценки, рассмотрим случай, когда вы хотите создать дополнительные данные для дообучения упомянутого ранее поиска листингов (listing finder). Сначала вы можете использовать LLM для генерации синтетических данных с промптом вроде этого:

Imagine if Zillow was able to parse natural language. Come up with 50 different ways users would be able to search listings there. Use real names for cities and neighborhoods. You can use the following parameters: Output should be a JSON code block array. Example: [ "Homes under $500k in New York" ]

Это почти идентично упражнению по созданию тест-кейсов! Затем вы можете использовать ваши тесты Level 1 и Level 2, чтобы отфильтровать нежелательные данные, которые не проходят ассерты или которые критикующая модель считает неверными. Вы также можете использовать ваши существующие инструменты человеческой оценки, чтобы просматривать трейсы и курировать их для датасета дообучения.

Отладка

Когда вы получаете жалобу или видите ошибку, связанную с вашим AI-продуктом, вы должны уметь быстро её отладить. Если у вас есть надёжная система оценки, у вас уже есть:

База трейсов, по которой можно искать и фильтровать. Набор механизмов (ассерты, тесты и т. д.), которые помогают помечать ошибки и нежелательное поведение. Инструменты поиска и навигации по логам, помогающие найти первопричину ошибки. Например, причиной может быть RAG, баг в коде или плохо работающая модель. Возможность вносить изменения в ответ на ошибку и быстро тестировать их эффективность.

Коротко говоря, инфраструктура, необходимая для оценки, и та, что нужна для отладки, перекрываются невероятно сильно.

Заключение

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

Устраните ВСЁ трение при просмотре данных. Сохраняйте простоту. Не покупайте навороченные LLM-инструменты. Сначала используйте то, что у вас уже есть. Если вы не смотрите на множество данных, вы делаете что-то неправильно. Не полагайтесь на универсальные фреймворки оценки для измерения качества вашего AI. Вместо этого создайте систему оценки, специфичную для вашей задачи. Пишите много тестов и часто их обновляйте. LLM можно использовать, чтобы разблокировать создание системы оценки. Примеры использования LLM: генерация тест-кейсов и написание ассертов; генерация синтетических данных; критика и разметка данных и т. д. Переиспользуйте вашу инфраструктуру оценки для отладки и дообучения.

Я был бы рад услышать от вас, если эта статья оказалась полезной или если у вас есть вопросы. Моя почта: hamel@parlance-labs.com.

Эта статья — адаптация этого разговора, который у меня состоялся с Emil Sedgh и Hugo Browne-Anderson в подкасте Vanishing Gradients. Спасибо Jeremy Howard, Eugene Yan, Shreya Shankar, Jeremy Lewi и Joseph Gleasure за рецензирование этой статьи.

Сноски

Это не означает, что люди ленивы. Многие просто не знают, как настроить системы оценки, и пропускают эти шаги.↩︎ В качестве примеров можно назвать arize, human loop, openllmetry и honeyhive.↩︎ Разумная эвристика — продолжать читать логи, пока не почувствуете, что больше не узнаёте ничего нового.↩︎ Если вам не терпится, я скоро буду преподавать этот курс по дообучению.↩︎