Головна / Статті

Оцінка GenAI та агентів: тестування в одному етапі проти багатоетапного тестування в CI

Порівняйте оцінювання одного етапу на основі слідів дій із оцінюванням кількох етапів за допомогою симулятора, враховуючи золоті стандарти, суддів з ШІ, метрики агентів, контрольні точки інтеграції та сім практик роботи.

Видавець: REACTAPP.TOP8 хв читання1800 слів

Джерела: End-to-End Evals for Gen AI and Agentic AI: A Practitioner’s Guide to Single-Turn and Multi-Turn Evaluation

„Це працює з моїм запитом“ — це не інженерний стандарт. Традиційний тест одиниці проходить або провалюється детерміновано, тоді як генеративні системи надають відкриті, ймовірнісні відповіді. Канал RAG може бути переважно правильним, а агент може виконати завдання так, що це на папері вважається успіхом, але на практиці є небезпечним. Саме тому оцінювання, на відміну від інтуїції чи чат-каналу, повного фраз типу „мені здається все гаразд“, тепер є основою серйозної роботи з машинним навчанням у сфері генеративного ШІ та агентних систем. У наступному розглядається, що потрібно вимірювати, у чому різниця між однокроковим та багатокроковим оцінюванням, які є компроміси кожного з них, а також шаблони Python, які підходять для інтеграції в CI-пайплайни.

Чому оцінювання не є опційним

Три характеристики додатків на основі LLM роблять оцінювання необхідним:

  1. Недетермінізм. Ідентичні запити можуть давати різні результати при різних запусках, версіях моделей чи значеннях температури.
  2. Кумулятивні помилки у агентах. У той час як звичайний чат-бот може помилятися лише один раз, агент поєднує від 10 до 50 викликів моделі, викликів інструментів та рішень. Незначний рівень помилок на кожному кроці призводить до частих проблем у всьому процесі.
  3. Тихі регресії. Зміна запиту, оновлення моделі чи переіндексація бази векторів можуть погіршити якість без видання жодних попереджень.

Оцінювання перетворює вислів „здається краще“ на число, яке можна відстежувати з часом, використовувати для контролю розгортання та порівнювати між версіями.

Два типи оцінювання

Майже кожна платформа, зокрема DeepEval, Ragas, LangSmith, Braintrust та OpenAI Evals, використовує один із двох підходів.

Однокроковий, з використанням сліду

Логіка проста: береться кожен елемент, один раз запускається додаток, оцінюється слід.

Open a test run with trace-level metrics
  for each golden:
    yield golden
    call app with golden.input
    capture trace
    build a test case from the trace
    score test case with metrics
  upload test run (traces + scores)

Золотий зразок — це зразок із позначками: вхідні дані, відповідь, яку він має створити, а також контекст чи виклики інструментів, які мають бути використані. Хід виконання фіксує повну історію одного запуску, включаючи ті фрагменти даних, які були отримані, які інструменти використано, логіку між кроками, час виконання та кількість токенів, а не лише кінцевий текст. Оцінка цього запису, а не лише відповіді, дозволяє визначити етап, у якому виникла проблема. Чи це отримання даних, генерація відповіді чи виклик інструменту на певному етапі?

Багатокроковий, з використанням симулятора

Це складніший та більш реалістичний формат для агентів та чат-асистентів.

def simulate(conversational goldens)
  for each golden:
    loop until expected outcome OR max turns:
      simulator model generates a user turn
      ask app for next reply
      capture assistant turn
    build a ConversationalTestCase from the full transcript
  evaluate(test cases, metrics)   # metrics run concurrently
    -> pass/fail + reason per metric

Тут немає фіксованої пари вхідних та вихідних даних. Модель симулятора виступає у ролі користувача з певною метою, наприклад скасувати підписку чи отримати повернення грошей за певний замовлення, та адаптує кожне повідомлення до відповідей асистента, так само, як це робив би людина. Систему оцінюють за повною траєкторією: чи залишалась вона зосередженою на завданні? Чи правильно використовувала інструменти протягом усіх етапів? Чи досягла мети в кінцевому підсумку? Чи вигадала деталі правил вже на третьому етапі?

Агентам, багатоетапним асистентам та будь-яким системам з пам’яттю чи станом потрібен саме такий підхід. Оцінка за один етап не може виявити, якщо агент суперечить сам собі на четвертому етапі або використовує один і той самий інструмент п’ять разів поспіль у циклі.

Вибір показників

Показники поділяються на чотири групи, і доцільно їх поєднувати, адже жоден з них не розповідає повної історії.

Корисна ментальна модель: метрики однокрокової оцінки дають зображення стану на момент оцінки, тоді як метрики агентської оцінки враховують динаміку змін.

LLM як суддя

Більшість сучасних метрик без використання зовнішніх даних працюють за принципом „LLM як суддя“. Сильна модель, часто інша та більш здатна, ніж модель, яку перевіряють, оцінює результат відповідно до критеріїв та пояснює свою оцінку.

from dataclasses import dataclass

JUDGE_PROMPT = """
You are evaluating an AI assistant's response for FAITHFULNESS.
A response is faithful if every factual claim it makes is supported
by the provided context. It is unfaithful if it adds unsupported
claims, contradicts the context, or fabricates details.
Context:
{context}
Response:
{response}
Return JSON: {{"score": <0.0-1.0>, "reason": "<short explanation>"}}
"""

@dataclass
class TestCase:
    input: str
    actual_output: str
    retrieval_context: list[str]

def faithfulness_metric(test_case: TestCase, judge_llm) -> dict:
    prompt = JUDGE_PROMPT.format(
        context="\n".join(test_case.retrieval_context),
        response=test_case.actual_output,
    )
    result = judge_llm.generate(prompt, response_format="json")
    return {"score": result["score"], "reason": result["reason"], "passed": result["score"] >= 0.7}

У суддів є підводні камені:

  • Упередження через самопреференцію. Моделі оцінюють власні результати або результати у своєму стилі більш схвально. Якщо можливо, використовуйте суддю з іншої родини моделей.
  • Чутливість до запиту. Невеликі зміни у формулюванні критеріїв впливають на оцінки. Тримайте запити до суддів під контролем версій, як код.
  • Витрати та затримки. Використання суддів також коштує грошей. У разі обмеженого бюджету перевіряйте трафік на прикладах, а не оцінюйте все.
  • Упередження через довжину та об’єм відповідей. Судді схильні давати довші, більш впевнені відповіді, незалежно від їхньої правильності. Час від часу калібруйте їхні оцінки за допомогою невеликого набору даних з позначками від людини.

Однокрокова обробка на практиці

Ідеальний набір може складатися лише зі списку словників, які містять вхідні дані та очікуваний результат.

goldens = [
    {
        "input": "What's our refund policy for digital products?",
        "expected_output": "Digital products are refundable within 14 days if unused.",
        "expected_tools": [],
    },
    {
        "input": "Cancel order #48213",
        "expected_output": None,  # open-ended, judged not matched
        "expected_tools": ["lookup_order", "cancel_order"],
    },
]

Виконавець дотримується описаного вище алгоритму: запускає тестовий виконання, проходить кожен тест, запускає додаток, фіксує стеження за виконанням, оцінює результат та завантажує його.

from deepeval import evaluate
from deepeval.metrics import FaithfulnessMetric, AnswerRelevancyMetric
from deepeval.test_case import LLMTestCase

def run_eval_suite(goldens, app_callback, metrics):
    test_cases = []
    for golden in goldens:
        trace = app_callback(golden["input"])   # your traced LLM/RAG app
        test_case = LLMTestCase(
            input=golden["input"],
            actual_output=trace.final_output,
            retrieval_context=trace.retrieved_chunks,
            expected_output=golden.get("expected_output"),
        )
        test_cases.append(test_case)
    results = evaluate(test_cases, metrics)  # runs metrics concurrently
    return results
metrics = [FaithfulnessMetric(threshold=0.7), AnswerRelevancyMetric(threshold=0.7)]
results = run_eval_suite(goldens, my_rag_app, metrics)

Твердження перетворює оцінки на бар’єр.

def assert_eval_gate(results, min_pass_rate=0.9):
    passed = sum(1 for r in results if r.success)
    pass_rate = passed / len(results)
    assert pass_rate >= min_pass_rate, f"Eval pass rate {pass_rate:.2%}
    below gate {min_pass_rate:.0%}"

Підключіть його до потоку PR так само, як підключений pytest. Якщо зміни у запитах чи отриманні інформації знижують рівень точності нижче допустимого порогу, злиття має бути заблоковане, і ніщо не повинно виходити у продакшн таємно.

Багатокрокові діалоги на практиці

Замість фіксованого вхідного даних симулятор отримує профіль користувача та мету, і сам керує розмовою.

class ConversationSimulator:
    def __init__(self, simulator_model, max_turns=8):
        self.simulator_model = simulator_model
        self.max_turns = max_turns

    def simulate(self, golden, chatbot_callback):
        transcript = []
        user_turn = self.simulator_model.generate_opening(golden["scenario"])
        for _ in range(self.max_turns):
            transcript.append({"role": "user", "content": user_turn})
            assistant_turn = chatbot_callback(transcript)
            transcript.append({"role": "assistant", "content": assistant_turn})
            if self.simulator_model.is_goal_met(golden["expected_outcome"], transcript):
                break
            user_turn = self.simulator_model.generate_next_turn(transcript, golden)
        return transcript

Отриману розмову потім оцінюють за допомогою метрик агентства.

from deepeval.test_case import ConversationalTestCase, Turn
from deepeval.metrics import (
    TaskCompletionMetric,
    ToolCorrectnessMetric,
    ConversationCompletenessMetric,
)

def build_conversational_test_case(transcript, tools_called):
    turns = [Turn(role=t["role"], content=t["content"]) for t in transcript]
    return ConversationalTestCase(turns=turns, tools_called=tools_called)
    metrics = [
    TaskCompletionMetric(threshold=0.8),          # did it accomplish the user's goal?
    ToolCorrectnessMetric(threshold=1.0),          # right tools, right args, right order
    ConversationCompletenessMetric(threshold=0.7), # did it address everything raised?
    ]
    test_case = build_conversational_test_case(transcript, tools_called=agent_trace.tool_calls)
    result = evaluate([test_case], metrics)

Типові перевірки на рівні агента включають:

  • Правильність виклику інструменту: правильний інструмент із правильними аргументами у правильному порядку, що є більшим, ніж просто „був викликаний якийсь інструмент“.
  • Ефективність кроків та виявлення циклів: багаторазові виклики одного інструменту без досягнення прогресу.
  • Дотримання ролі та правил: дотримання визначеної персони та обмежень протягом усієї розмови, а не лише на початковому кроці.
  • Відновлення після помилки: розумна спроба повтору після невдалиого виклику інструменту замість вигаданого результату.
  • Збереження контексту: пам’ятати те, що сказав користувач три кроки раніше.

Оцінка компромісів

Однокрокова розмова Багатокрокова розмова
Витрати Один виклик на кожен крок, дешево та швидко
Кожен запуск — це багатокрокова розмова, помножена на кількість етапів та показники Набір даних Великі ідеальні набори є простими для використання — вони можуть походити з журналів подій або бути синтетично створеними Симулятори дозволяють відтворити крайні випадки та агресивних користувачів, яких ніхто не напише вручну Сигнал Чітко розділяє помилки пошуку від помилок генерації, є майже детермінованим для фільтрації в процесі інтеграційних тестів Виявляє кумулюючі несправності, які спричиняють реальні інциденти, та тестує всю систему, включаючи інструменти, механізми оркестрації, стани та захисні механізми Слабкі місця Не виявляє змін, суперечностей та втрати пам’яті між етапами, а також сприяє надмірній адаптації до ідеального набору даних Має вищу дисперсію через додавання випадковостей симулятором, ускладнює дебаггінг через те, що несправність на 6-му етапі може початися вже на 2-му, а оцінка стає складною через довгі транскрипти

Оскільки результати багатокрокових процесів є шумними, їх слід інтерпретувати статистично. Запустіть набір тестів N разів та аналізуйте розподіли, а не один окремий показник.

Ефективні практики

  1. Почніть із невеликого, чистого набору з 30 до 100 прикладів. Шумний набір з 1 000 екземплярів гірший за організований набір з 50, оскільки некоректні мітки псують якість сигналу.
  2. Оцінюйте процес пошуку та генерації окремо в рамках технології RAG. Якість відтворення та контекстуальне запам’ятовування відповідають на різні запитання, а їх об’єднання приховує справжню причину проблем.
  3. Оцінюйте результати за допомогою більш надійної, незалежної моделі та час від часу перевіряйте її показники порівняно з оцінками людських експертів. Вже 20–30 зразків на місяць дозволяють виявити зміни в оцінках.
  • Відстежуйте все. Оцінюйте дані, отримані в результаті відстеження (завантажені документи, виклики інструментів, проміжні кроки), а не лише кінцевий текст. Саме тому збір цих даних має відбуватися перед створенням тест-кейсу.
  • Симулюйте агентів перед розгортанням. Проводьте діалоги з реалістичними персонажами та умовами, які можуть створити проблеми, наприклад, розлючений користувач, неоднозначний запит чи крайній випадок за правилами, ще до розгортання, а не після інциденту.
  • Розглядайте процеси оцінки як живі ресурси. Перезапускайте повний набір тестів після кожної зміни запиту, оновлення моделі чи перебудови індексу, а також встановлюйте пороги успішності в системах CI так само, як і пороги покриття.
  • Слідкуйте за динамікою змін з часом. Одна серія тестів — це лише зразок, а не остаточна істина. Повторюйте критичні набори тестів та відстежуйте тенденції.
  • Висновок

    Оцінка генеративних ШІ спочатку була лише додатковим елементом — коміркою в нотатках, яку хтось запускав перед демонстрацією. Агентні системи вийшли за межі цього підходу. Як тільки програмне забезпечення може діяти кілька разів, викликати інструменти та зберігати стан між кроками, оцінка більше не є частиною процесу контролю якості. Це основний інструмент для аналізу реальної поведінки системи.

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