Оценка качества (Evaluation): Метрики и автоматическое тестирование
Введение: От «вроде работает» к инженерной точности
Добро пожаловать на один из самых критически важных этапов разработки архитектуры ИИ-решений. Если в предыдущих модулях мы учились заставлять модель говорить, то сейчас мы будем учиться судить, насколько качественно она это делает.
В классической разработке ПО мы привыкли к детерминизму. Если функция сложения получает 2 и 2, она обязана вернуть 4. Мы пишем unit-тест: assert sum(2, 2) == 4. Если тест проходит — код рабочий.
С LLM, такими как Gemini, этот подход ломается. Модель недетерминирована. На один и тот же промпт вы можете получить десять разных ответов, и все они могут быть правильными по смыслу, но разными по форме. А могут быть разными и по смыслу. Как написать assert для творческого эссе или суммаризации сложного юридического документа?
Многие разработчики останавливаются на этапе «Vibes-based evaluation» (оценка на основе ощущений). Они запускают пару промптов, смотрят глазами: «Ну, вроде нормально отвечает», и деплоят в продакшн. Для архитектора решений это недопустимо. Вы не можете улучшать то, что не можете измерить.
В этом уроке мы построим систему координат для объективной оценки качества работы Gemini, разберем метрики, которые реально работают, и создадим пайплайн автоматического тестирования с использованием паттерна LLM-as-a-Judge.
Уровни оценки качества (Evaluation Hierarchy)
Оценку качества генеративных моделей можно разделить на три уровня сложности. Понимание того, какой уровень применять в конкретной задаче — ключ к экономии ресурсов.
1. Детерминистические метрики (Code & Structured Data)
Самый простой уровень. Используется, когда мы ожидаем от модели строгого формата.
- Valid JSON/XML: Парсится ли ответ? (Gemini в режиме JSON mode обычно справляется идеально, но проверить стоит).
- Regex (Регулярные выражения): Содержит ли ответ ключевые слова, email, телефон или запрещенные конструкции?
- Code Compilability: Если модель пишет код, компилируется ли он?
2. Метрики схожести (Similarity Metrics)
Пришли к нам из классического NLP (машинного перевода). Сюда относятся BLEU, ROUGE, METEOR. Они сравнивают сгенерированный текст с эталонным (Golden Answer) по n-граммам (совпадению слов и фраз).
Вердикт для LLM: Почти бесполезны. Почему? Если эталон: «Клиент доволен услугой», а модель ответила: «Пользователь выразил удовлетворение сервисом», метрика ROUGE покажет низкий балл из-за отсутствия общих слов, хотя смысл идентичен. Используйте их только для очень простых задач экстракции.
3. Семантические и модельные метрики (Model-based Evaluation)
Золотой стандарт современной архитектуры. Здесь мы используем саму LLM (или другую, более сильную модель) для оценки качества ответа. Мы просим «Судью» сравнить ответ с эталоном или оценить его по критериям.
- Embedding Similarity: Превращаем ответ и эталон в векторы (используя
embedding-001) и считаем косинусное сходство. Хорошо ловит общий смысл, но может пропустить тонкие логические ошибки. - LLM-as-a-Judge: Использование Gemini 1.5 Pro для оценки ответов Gemini 1.5 Flash. Это позволяет оценивать такие сложные вещи, как «дружелюбность», «безопасность» или «соответствие контексту».
import google.generativeai as genai
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# Настройка API (предполагается, что ключ уже в окружении)
# genai.configure(api_key=...)
def calculate_semantic_similarity(text1, text2):
"""
Вычисляет семантическую близость двух текстов, используя эмбеддинги Gemini.
"""
# Получаем эмбеддинги для обоих текстов
result1 = genai.embed_content(
model="models/text-embedding-004",
content=text1,
task_type="semantic_similarity"
)
result2 = genai.embed_content(
model="models/text-embedding-004",
content=text2,
task_type="semantic_similarity"
)
embedding1 = np.array(result1['embedding']).reshape(1, -1)
embedding2 = np.array(result2['embedding']).reshape(1, -1)
# Считаем косинусное сходство (от 0 до 1)
score = cosine_similarity(embedding1, embedding2)[0][0]
return score
# Пример: разные слова, но похожий смысл
text_a = "Машина не заводится, слышен странный стук."
text_b = "Автомобиль имеет проблемы с запуском двигателя, присутствует посторонний шум."
similarity = calculate_semantic_similarity(text_a, text_b)
print(f"Семантическая близость: {similarity:.4f}")
# Ожидаемый результат: высокое значение (>0.85), несмотря на разную лексику
Паттерн LLM-as-a-Judge (LLM как Судья)
Самый мощный инструмент в вашем арсенале — это использование одной модели для оценки другой. Обычно используется более "умная" и дорогая модель (например, Gemini 1.5 Pro) для оценки ответов более быстрой модели (например, Gemini 1.5 Flash или Gemma 2), которую вы планируете использовать в продакшне.
Анатомия промпта Судьи
Промпт для оценки должен быть структурирован еще жестче, чем обычный. Он должен содержать:
- Роль: "Ты беспристрастный судья, оценивающий качество работы AI-ассистента."
- Контекст (Input): Входные данные, которые получил тестируемый бот.
- Ответ (Output): То, что сгенерировал бот.
- Эталон (Reference, опционально): Правильный ответ, если он есть.
- Критерии (Rubric): Четкое описание того, что такое "хорошо" и "плохо" (например, по шкале от 1 до 5).
- Формат вывода: Строгий JSON или число, чтобы мы могли это распарсить программно.
Совет архитектора: Всегда требуйте от Судьи сначала сгенерировать "Chain of Thought" (объяснение оценки), и только потом саму оценку. Если модель сначала поставит цифру, а потом начнет объяснять, качество оценки будет ниже (модель будет подгонять аргументы под случайно выбранную цифру).
import json
def evaluate_with_gemini_judge(question, llm_answer, context=None):
"""
Использует Gemini Pro для оценки ответа другой модели на предмет галлюцинаций
и соответствия контексту (Faithfulness/Grounding).
"""
judge_prompt = f"""
Твоя задача — оценить качество ответа AI-ассистента на вопрос пользователя.
КОНТЕКСТ (информация, которой владел ассистент):
{context}
ВОПРОС ПОЛЬЗОВАТЕЛЯ:
{question}
ОТВЕТ АССИСТЕНТА:
{llm_answer}
КРИТЕРИИ ОЦЕНКИ (Faithfulness):
1. Оцените, содержит ли ответ информацию, которой НЕТ в контексте (галлюцинации).
2. Оцените, противоречит ли ответ контексту.
3. Если ответ опирается только на контекст и верно отвечает на вопрос — это высший балл.
ВЫВОД:
Верни результат строго в формате JSON:
{{
"reasoning": "Твое подробное объяснение, почему ты ставишь такую оценку...",
"score": <целое число от 1 до 5, где 5 - идеально>
}}
"""
model = genai.GenerativeModel('gemini-1.5-pro')
response = model.generate_content(
judge_prompt,
generation_config={"response_mime_type": "application/json"}
)
try:
return json.loads(response.text)
except json.JSONDecodeError:
return {"reasoning": "Error parsing JSON", "score": 0}
# Пример использования
ctx = "Тариф 'Стандарт' стоит 500р. Тариф 'Про' стоит 1000р и включает поддержку."
q = "Сколько стоит поддержка?"
# Допустим, модель ошиблась
ans = "Поддержка стоит 500 рублей и включена в тариф Стандарт."
eval_result = evaluate_with_gemini_judge(q, ans, ctx)
print(json.dumps(eval_result, indent=2, ensure_ascii=False))
Создание «Золотого датасета» (Golden Dataset)
Никакие скрипты оценки не помогут, если вам не на чем тестировать. Для качественного файн-тюнинга и оценки вам нужен Golden Dataset.
Это набор из 50–200 пар (input, expected_output), которые вручную проверены экспертами. Это ваша «линейка», которой вы измеряете модель.
Что включать в тестовый набор:
- Простые случаи: Базовые вопросы, на которые модель обязана отвечать правильно.
- Сложные случаи (Edge cases): Вопросы с подвохом, попытки инъекции промптов, вопросы на грани этики.
- Негативные примеры: Вопросы, на которые модель должна ответить «Я не знаю» или «Это выходит за рамки моих компетенций» (очень важно для RAG-систем, чтобы избежать галлюцинаций).
Совет: Не используйте данные из обучающей выборки (train set) для оценки (test set). Это фундаментальное правило ML. Если вы проверяете модель на том, на чем учили, вы проверяете её память, а не интеллект.
Создайте функцию-судью для оценки «Тональности» (Tone/Style). Допустим, вы разрабатываете бота технической поддержки, который должен быть вежливым, но лаконичным. Напишите промпт для Gemini Judge, который оценивает ответ по шкале 1-3, где: 1 - Грубо или слишком неформально, 2 - Слишком много воды/излишне вежливо, 3 - Идеально (вежливо и по делу).
Метрики для RAG (Retrieval Augmented Generation)
Если вы строите RAG-систему (поиск по базе знаний + генерация), вам недостаточно просто оценить финальный ответ. Ошибка может быть на этапе поиска документов или на этапе генерации.
Для этого используется фреймворк RAGas (Retrieval Augmented Generation Assessment), который выделяет ключевые метрики:
- Faithfulness (Достоверность): Ответ выведен только из найденных документов? (Борьба с галлюцинациями).
- Answer Relevance (Релевантность ответа): Отвечает ли текст на вопрос пользователя? (Даже если ответ правдивый, он может быть не про то).
- Context Precision (Точность контекста): Насколько много «мусора» попало в найденные документы? Если мы нашли 10 документов, а ответ только в одном — точность низкая, это сбивает модель.
- Context Recall (Полнота контекста): Нашли ли мы вообще нужную информацию в базе?
В продакшне мы обычно строим матрицу: запускаем 50 тестовых вопросов через RAG, собираем `(Question, Contexts, Answer, Ground Truth)` и прогоняем через скрипт оценки.
Автоматизация: CI/CD для ИИ
Оценка не должна быть разовым мероприятием. Как Архитектор, вы должны внедрить Continuous Evaluation.
- Пре-коммит хуки (для промптов): Если разработчик меняет системный промпт, запускается быстрый тест на 10 примерах. Если качество (Score) упало ниже порога — коммит отклоняется.
- Ночные прогоны: Раз в сутки запускается полный прогон на всем Golden Dataset (200+ примеров) с использованием мощной модели-судьи. Это помогает отловить регрессию, если изменилась сама модель Gemini (API обновляются) или данные в RAG.
- Оценка в рантайме (Sampling): В продакшне вы не можете оценивать каждый запрос (это дорого и долго). Но вы можете сохранять 1% диалогов и асинхронно прогонять их через Evaluation Pipeline, чтобы мониторить «здоровье» системы на реальных данных.
Помните: Модель, которую не тестируют, деградирует незаметно.
Вы разрабатываете медицинского бота-консультанта на базе Gemini. При тестировании вы заметили, что на вопрос «Как лечить грипп?» бот дает ответ, который выглядит очень убедительно и профессионально, но рекомендует лекарство, которое не упоминалось в предоставленном ему медицинском контексте (RAG). Какую метрику качества нарушает этот ответ?