Перейти к содержанию

1.6 Построение и оценка LLM-приложений

Разработка приложений на основе больших языковых моделей (LLM) — это не только грамотная интеграция, но и системная оценка качества, которая охватывает как объективные, так и субъективные аспекты. На практике приходится сочетать метрики точности, полноты и F1‑меры (когда доступны эталонные ответы) с пользовательскими рейтингами и метриками удовлетворённости (CSA), а также учитывать эксплуатационные показатели, такие как стоимость и задержка ответа. Такой набор измерений помогает диагностировать слабые места, принимать обоснованные решения о релизах и целенаправленно улучшать продукт. Типичный путь из разработки в продакшен начинается с простых промптов и небольшого набора данных для быстрых итераций; затем расширяется покрытие, усложняются сценарии, уточняются метрики и критерии качества — при этом не всегда нужна «идеальность», достаточно устойчиво решать целевые задачи в заданных ограничениях по качеству и бюджету. В сценариях высокого риска, где потенциальный вред особенно чувствителен (медицина, правоприменение, финансы), возрастает роль строгой валидации: рандомные выборки и hold‑out‑тесты, проверки на предвзятость и ошибки, а также внимание к этическим и юридическим аспектам — недопущение вреда, объяснимость решений и возможность аудита. Хороший инженерный стиль здесь подчёркивает модульность и быстрые итерации, автоматизацию регрессионных тестов и измерений, осмысленный выбор метрик под бизнес‑цели и обязательный анализ предвзятости и справедливости с регулярным ревью.

Чтобы наладить воспроизводимую оценку, удобно использовать рубрикаторы (rubric) и протоколы оценки: заранее описать критерии качества — релевантность намерению и контексту пользователя, фактическую корректность, полноту охвата и связность/беглость — а также процедуру, шкалы и пороги. В субъективных задачах уместны несколько независимых оценщиков и автоматические проверки согласованности. Там, где это возможно, полезно сравнивать ответы с идеальными (экспертными) — такой «эталон» служит ориентиром и позволяет объективнее судить о качестве. В помощь разработчику — простой каркас окружения и функций вызова модели для воспроизводимых экспериментов и оценок:

import os
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
client = OpenAI()

def fetch_llm_response(prompts, model="gpt-4o-mini", temperature=0, max_tokens=500):
    response = client.chat.completions.create(
        model=model,
        messages=prompts,
        temperature=temperature,
        max_tokens=max_tokens,
    )
    return response.choices[0].message["content"]

Далее можно формализовать оценку по рубрике и раздать весовые коэффициенты, чтобы получать интегральный балл и развёрнутую обратную связь. Ниже представлен шаблон, в котором модель формирует оценку по заданным критериям; разбор результата показан заглушкой и в рабочем проекте должен быть заменён на парсинг фактического формата ответа:

def evaluate_response_against_detailed_rubric(test_data, llm_response):
    """
    Оценивает ответ по критериям: точность, релевантность, полнота, связность.
    Возвращает интегральный балл и развёрнутую обратную связь.
    """
    rubric_criteria = {
        'accuracy': {'weight': 3, 'score': None, 'feedback': ''},
        'relevance': {'weight': 2, 'score': None, 'feedback': ''},
        'completeness': {'weight': 3, 'score': None, 'feedback': ''},
        'coherence': {'weight': 2, 'score': None, 'feedback': ''}
    }
    total_weight = sum(c['weight'] for c in rubric_criteria.values())

    system_prompt = "Оцените ответ агента службы поддержки с учётом предоставленного контекста."
    evaluation_prompt = f"""\
    [Вопрос]: {test_data['customer_query']}
    [Контекст]: {test_data['context']}
    [Ожидаемые ответы]: {test_data.get('expected_answers', 'Н/Д')}
    [Ответ LLM]: {llm_response}

    Оцените ответ на основе точности, релевантности, полноты и связности.
    Предоставьте баллы (от 0 до 10) для каждого критерия и конкретную обратную связь.
    """

    evaluation_results = fetch_llm_response([
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": evaluation_prompt},
    ])

    # Заглушка парсинга — замените на реальный разбор структуры ответа вашей модели
    for k in rubric_criteria:
        rubric_criteria[k]['score'] = 8
        rubric_criteria[k]['feedback'] = "Хорошо по данному критерию."

    overall = sum(v['score'] * v['weight'] for v in rubric_criteria.values()) / total_weight
    detailed = {k: {"score": v['score'], "feedback": v['feedback']} for k, v in rubric_criteria.items()}
    return {"overall_score": overall, "detailed_scores": detailed}

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

def detailed_evaluation_against_ideal_answer(test_data, llm_response):
    criteria = {
        'factual_accuracy': {'weight': 4, 'score': None, 'feedback': ''},
        'alignment_with_ideal': {'weight': 3, 'score': None, 'feedback': ''},
        'completeness': {'weight': 3, 'score': None, 'feedback': ''},
        'coherence': {'weight': 2, 'score': None, 'feedback': ''}
    }
    total = sum(c['weight'] for c in criteria.values())

    system_prompt = "Сравните ответ LLM с идеальным ответом, сосредоточившись на фактическом содержании и соответствии."
    comparison_prompt = f"""\
    [Вопрос]: {test_data['customer_query']}
    [Идеальный ответ]: {test_data['ideal_answer']}
    [Ответ LLM]: {llm_response}
    """

    evaluation_text = fetch_llm_response([
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": comparison_prompt},
    ])

    # Заглушка парсинга
    for k in criteria:
        criteria[k]['score'] = 8
        criteria[k]['feedback'] = "Хорошее соответствие эталону."

    score = sum(v['score'] * v['weight'] for v in criteria.values()) / total
    return {"overall_score": score, "details": criteria, "raw": evaluation_text}

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

Теоретические вопросы

  1. Зачем оценивать ответы LLM и по каким измерениям это следует делать?
  2. Приведите примеры метрик и объясните их роль в разработке.
  3. Как выглядит итеративный переход от разработки к продакшену?
  4. Почему сценарии высокого риска требуют особой строгости, и приведите примеры таких приложений?
  5. Перечислите лучшие практики для старта, итераций и автотестов.
  6. Как автоматизация тестов помогает в процессе разработки?
  7. Почему метрики следует настраивать под конкретную задачу?
  8. Как построить рубрикатор и протоколы оценки?
  9. Какие продвинутые техники оценки применимы и для чего?
  10. Как непрерывная оценка и широкий охват тест-кейсов повышают надёжность системы?

Практические задания

  1. Напишите функцию, которая читает API-ключ из окружения, запрашивает ответ у LLM и измеряет время выполнения и количество использованных токенов.