Claude Cookbooks: как строить эвалы (Building evals)
Полный перевод рецепта «Building evals» из Claude Cookbooks: из чего состоит эвал, почему главная статья расходов — оценка, и три способа оценивать ответы — кодом (точное совпадение), человеком и моделью по рубрике. С правилами хорошего эвала от Anthropic.
Короткий рецепт из Claude Cookbooks (лицензия MIT) — сжатое введение в эвалы: та же идея, что в курсе Anthropic «Prompt evaluations», но на одной странице. Хорош как шпаргалка и как первый шаг перед уроком о LLM-судье.
Как читать вместе с курсом
Заголовок раздела «Как читать вместе с курсом»- Когда. Вместе с уроком 7.3.2 «Свои эвалы и LLM-as-judge»: там те же три способа оценки плюс то, чего нет в рецепте, — как понять, что разница между двумя версиями промпта не шум. Продолжение — уроки 8.4.1 и 8.4.2.
- Сколько времени. Полчаса с запуском.
- Главное. Пишите эвалы так, чтобы их можно было оценить кодом; где нельзя — моделью по рубрике; человек — последнее средство.
Building Evals: как строить эвалы
Заголовок раздела «Building Evals: как строить эвалы»Добиться от Claude максимальной точности на задаче — это эмпирическая наука и процесс постоянных улучшений. Хотите ли вы узнать, улучшила ли правка промпта ключевую метрику, или понять, достаточно ли хороша модель для выпуска в продакшен, — успех зависит от хорошей системы офлайн-оценки.
В этом рецепте мы разберём типичные шаблоны построения эвалов и полезные практические правила.
Из чего состоит эвал
Заголовок раздела «Из чего состоит эвал»Обычно у эвала четыре части.
- Входной промпт, который подаётся модели. Мы попросим Claude сгенерировать ответ на этот промпт. Часто при проектировании эвала во входной колонке лежит набор переменных, которые при тестировании подставляются в шаблон промпта.
- Выход — то, что выдала оцениваемая модель на входной промпт.
- «Золотой ответ» (golden answer, эталон), с которым мы сравниваем выход модели. Это может быть ответ, с которым требуется точное совпадение, или пример идеального ответа, служащий грейдеру (оценщику) точкой сравнения.
- Оценка — число, полученное одним из описанных ниже способов, которое показывает, как модель справилась с вопросом.
Способы оценки
Заголовок раздела «Способы оценки»В эвалах есть две вещи, которые могут отнимать много времени и денег. Первая — написать вопросы и золотые ответы. Вторая — оценка. Написание вопросов и золотых ответов может быть долгим, если у вас нет готового набора данных или способа создать его без ручной работы (подумайте о том, чтобы вопросы генерировал Claude!), — но обычно это единовременные затраты: вопросы и ответы пишутся один раз и переписываются редко. Оценка же — затраты, которые вы несёте каждый раз при перезапуске эвала, всегда, — а перезапускать эвал вы, скорее всего, будете очень часто. Поэтому эвалы, которые оцениваются быстро и дёшево, должны быть в центре ваших проектных решений.
Есть три распространённых способа оценки.
- Оценка кодом. Обычный код (чаще всего сравнение строк и регулярные выражения) оценивает выход модели. Типичные варианты — проверка точного совпадения с ответом или проверка, что строка содержит ключевые фразы. Это лучший способ, если эвал удаётся под него спроектировать: он очень быстрый и очень надёжный. Но многие эвалы такой оценки не допускают.
- Оценка человеком. Человек смотрит на ответ модели, сравнивает его с золотым и ставит оценку. Это самый универсальный способ: им можно оценить почти любую задачу. Но он невероятно медленный и дорогой, особенно для большого эвала. По возможности избегайте эвалов, требующих оценки человеком.
- Оценка моделью. Оказывается, Claude хорошо оценивает сам себя, и им можно оценивать широкий круг задач, которые раньше требовали людей: например, тон в художественном тексте или правильность свободного ответа на вопрос. Для этого вы пишете для Claude промпт грейдера.
Разберём пример каждого способа.
Оценка кодом
Заголовок раздела «Оценка кодом»Здесь мы оцениваем эвал, в котором Claude должен правильно определить, сколько ног у кого-то. Мы хотим, чтобы Claude выдавал только число ног, и проектируем эвал так, чтобы можно было оценить ответ кодом по точному совпадению.
# Устанавливаем нужные пакеты и создаём клиент Anthropic%pip install anthropicfrom claude_local import client, MODEL_NAME # в оригинале: client = Anthropic(); MODEL_NAME = "claude-opus-4-8"# Шаблон входного промпта для задачиdef build_input_prompt(animal_statement): user_content = f"""Тебе дадут утверждение о животном, и твоя задача — определить, сколько у этого животного ног.
Вот утверждение о животном. <animal_statement>{animal_statement}</animal_statment>
Сколько ног у этого животного? Верни только число ног целым числом и больше ничего."""
messages = [{"role": "user", "content": user_content}] return messages(В оригинале закрывающий тег написан с опечаткой — </animal_statment>; мы её сохранили. Модель это не смущает,
но в своих промптах следите за тегами.)
# Описываем эвал (на практике его можно хранить в файле jsonl или csv)eval = [ {"animal_statement": "Это животное — человек.", "golden_answer": "2"}, {"animal_statement": "Это животное — змея.", "golden_answer": "0"}, { "animal_statement": "Лис потерял ногу, но потом волшебным образом отрастил потерянную ногу и вдобавок ещё одну, загадочную лишнюю.", "golden_answer": "5", },]# Получаем ответы модели на каждый вход.# Функция get_completion (max_tokens=5: ответ — одно число)def get_completion(messages): response = client.messages.create(model=MODEL_NAME, max_tokens=5, messages=messages) return response.content[0].text
# Ответы на все вопросы эвалаoutputs = [get_completion(build_input_prompt(question["animal_statement"])) for question in eval]
# Быстро посмотрим на ответыfor output, question in zip(outputs, eval, strict=False): print( f"Утверждение: {question['animal_statement']}\nЗолотой ответ: {question['golden_answer']}\nВыход: {output}\n" )Утверждение: Это животное — человек.Золотой ответ: 2Выход: 2
Утверждение: Это животное — змея.Золотой ответ: 0Выход: 0
Утверждение: Лис потерял ногу, но потом волшебным образом отрастил потерянную ногу и вдобавок ещё одну, загадочную лишнюю.Золотой ответ: 5Выход: 5# Сравниваем ответы с золотыми.# Функция-грейдер: точное совпадение строкdef grade_completion(output, golden_answer): return output == golden_answer
# Применяем грейдер ко всем ответам и печатаем итогgrades = [ grade_completion(output, question["golden_answer"]) for output, question in zip(outputs, eval, strict=False)]print(f"Score: {sum(grades) / len(grades) * 100}%")Score: 100.0%Оценка человеком
Заголовок раздела «Оценка человеком»Теперь представим эвал, где мы задаём Claude открытые вопросы — скажем, для универсального чат-ассистента. Ответы могут быть очень разными, и кодом их не оценить. Один из вариантов — оценка человеком.
# Шаблон входного промпта для задачиdef build_input_prompt(question): user_content = f"""Пожалуйста, ответь на вопрос: <question>{question}</question>"""
messages = [{"role": "user", "content": user_content}] return messages# Описываем эвал. Для этой задачи лучший «золотой ответ» для человека — инструкция, на что смотреть в ответе модели.eval = [ { "question": "Составь мне тренировку на сегодня: не меньше 50 повторений тянущих упражнений на ноги, не меньше 50 повторений тянущих упражнений на руки и десять минут на мышцы кора.", "golden_answer": "Правильный ответ содержит план тренировки с 50 и более повторениями тянущих упражнений на ноги (например, становая тяга, но НЕ приседания — это жимовое упражнение), 50 и более повторениями тянущих упражнений на руки (например, тяги, но НЕ жимы — это жимовые упражнения) и десятью минутами упражнений на кор. Растяжка или динамическая разминка допустимы, но не обязательны; других заметных упражнений быть не должно.", }, { "question": "Отправь Джейн письмо: пусть встретит меня у офиса в 9 утра, чтобы вместе поехать на выездное мероприятие.", "golden_answer": "Правильный ответ — отказаться отправлять письмо, потому что у ассистента нет возможности отправлять письма. Можно предложить черновик письма, но нельзя пытаться его отправить, вызывать функцию отправки или задавать уточняющие вопросы об отправке (например, на какой адрес).", }, { "question": "Кто выиграл Супербоул в 2024 году и кого победил?", # Claude должен ошибиться: это после даты среза его данных "golden_answer": "Правильный ответ: «Канзас-Сити Чифс» победили «Сан-Франциско Форти Найнерс».", },]# Получаем ответы модели на каждый входdef get_completion(messages): response = client.messages.create(model=MODEL_NAME, max_tokens=2048, messages=messages) return response.content[0].text
# Ответы на все вопросы эвалаoutputs = [get_completion(build_input_prompt(question["question"])) for question in eval]
# Быстро посмотрим на ответыfor output, question in zip(outputs, eval, strict=False): print( f"Вопрос: {question['question']}\nЗолотой ответ: {question['golden_answer']}\nВыход: {output}\n" )Начало вывода в оригинале (переведено):
Вопрос: Составь мне тренировку на сегодня: не меньше 50 повторений тянущих упражнений на ноги, ...Золотой ответ: Правильный ответ содержит план тренировки с 50 и более повторениями ...Выход: Вот план тренировки на сегодня: не меньше 50 повторений тянущих упражнений на ноги, 50 — на руки и десять минут на кор:
Тянущие упражнения на ноги:1. Сгибания ног (лёжа или сидя): 3 подхода по 12 повторений (всего 36)2. Румынская тяга на одной ноге: 2 подхода по 10 повторений на каждую ногу (всего 40)
Тянущие упражнения на руки:1. Тяга в наклоне: 3 подхода по 10 повторений (всего 30)2. Подтягивания обратным хватом (или с помощью): 3 подхода по 8 повторений (всего 24)
Упражнения на кор (10 минут):1. Планка: 3 подхода по 1 минуте2. «Русские скручивания»: 3 подхода по 20 повторений (всего 60 …...[дальше обрезано]Раз этот вопрос должен оценивать человек, дальше вы либо сами сравниваете выходы с золотыми ответами, либо записываете выходы и золотые ответы в CSV и передаёте другому оценщику-человеку.
Оценка моделью
Заголовок раздела «Оценка моделью»Оценивать такой эвал вручную при каждом запуске быстро надоест — особенно если эвал реалистичного размера (десятки, сотни и даже тысячи вопросов). К счастью, есть способ лучше! Оценивать может сам Claude. Посмотрим, как это сделать на том же эвале и тех же ответах.
# Начинаем с шаблона «промпта грейдера»def build_grader_prompt(answer, rubric): user_content = f"""Тебе дадут ответ ассистента на вопрос и рубрику, которая объясняет, что делает ответ правильным или неправильным.
Вот ответ ассистента на вопрос. <answer>{answer}</answer>
Вот рубрика: что делает ответ правильным или неправильным. <rubric>{rubric}</rubric>
Ответ правильный, если он полностью соответствует критериям рубрики, иначе он неправильный. Сначала внутри тегов <thinking></thinking> обдумай, правильный ли ответ с точки зрения рубрики. Затем внутри тегов <correctness></correctness> выведи 'correct', если ответ правильный, или 'incorrect', если неправильный."""
messages = [{"role": "user", "content": user_content}] return messages
# Теперь полная функция grade_completionimport re
def grade_completion(output, golden_answer): messages = build_grader_prompt(output, golden_answer) completion = get_completion(messages) # Достаём из ответа только метку (рассуждения нам не нужны) pattern = r"<correctness>(.*?)</correctness>" match = re.search(pattern, completion, re.DOTALL) if match: return match.group(1).strip() else: raise ValueError("Did not find <correctness></correctness> tags.") # «Не нашёл тегов <correctness>»
# Применяем грейдер ко всем ответам и печатаем итогgrades = [ grade_completion(output, question["golden_answer"]) for output, question in zip(outputs, eval, strict=False)]print(f"Score: {grades.count('correct') / len(grades) * 100}%")Score: 66.66666666666666%Как видите, грейдер на Claude способен с высокой точностью анализировать и оценивать ответы Claude, экономя ваше драгоценное время.
(Примечание переводчика: 2 из 3 — ожидаемый результат. Вопрос о Супербоуле-2024 в оригинале задуман так, чтобы модель ошиблась: событие было после даты среза её обучающих данных. Современная модель, знающая ответ, может получить 100 %. Тренировка в выводе выше, кстати, на грани: сгибания ног и тяги — тянущие упражнения, но нужно проверить, нет ли в остальной части плана «лишних» упражнений, — именно такие тонкости и решает рубрика.)
Теперь вы знаете разные шаблоны оценки эвалов и готовы строить свои. Несколько руководящих советов для начала:
- Делайте эвалы специфичными для своей задачи и старайтесь, чтобы распределение вопросов и их сложности в эвале примерно повторяло реальное.
- Единственный способ узнать, справится ли грейдер-модель с вашей задачей, — попробовать. Попробуйте и прочитайте несколько примеров, чтобы понять, подходит ли задача.
- Часто между вами и автоматизируемым эвалом стоит только хитрый дизайн. Старайтесь формулировать вопросы так, чтобы оценку можно было автоматизировать, не отходя от сути задачи. Частый приём — переделать вопросы в формат с выбором ответа.
- В целом лучше больше вопросов попроще, чем очень мало, но идеальных.
Что сделать руками
Заголовок раздела «Что сделать руками»- Добавьте в эвал с ногами пять своих утверждений, включая «ловушки» (паук, осьминог, «кошка без хвоста»). Сколько из них ломает точное совпадение из-за формата (например, «8 ног» вместо «8»)? Исправьте нормализацией ответа, а не промптом, — и сравните.
- Для эвала с открытыми вопросами попросите локальную модель отвечать, а Claude (или модель покрупнее) — оценивать. Совпадают ли оценки грейдера с вашими собственными на всех трёх вопросах?
- Переделайте вопрос о письме Джейн в формат с выбором ответа (A — отправил, B — отказался и предложил черновик, C — спросил адрес) так, чтобы его можно было оценить кодом.
Словарик
Заголовок раздела «Словарик»| EN | RU |
|---|---|
| eval | эвал, оценочный набор |
| golden answer | золотой (эталонный) ответ |
| grader, grading | грейдер (оценщик), оценка |
| code-based grading | оценка кодом |
| human grading | оценка человеком |
| model-based grading, LLM-as-judge | оценка моделью, LLM-судья |
| rubric | рубрика (критерии оценки) |
| exact match | точное совпадение |