English translation is not available yet. Showing Russian content.

Как тестировать агентов на недетерминированность?

Краткий тезис

Агентные системы по своей природе недетерминированы: LLM выводят вероятностные ответы, API|внешние API могут давать разные результаты, время выполнения варьируется. Чтобы тестировать такие системы, нельзя просто сравнивать «ожидаемый результат». Вместо этого применяют детерминированные семена (seed), многократные прогоны (multiple runs), статистические тесты, Монте-Карло симуляции и выделение золотого пути (path|golden path) — детерминированного ядра агента. Цель — оценить разброс метрик и убедиться, что агент стабильно решает задачу в пределах допустимой вариативности.


1. Что такое недетерминированность агента

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

  • LLM с temperature > 0 — вероятностная выборка токенов;
  • Внешние API — задержки, ошибки, нестабильные ответы (например, поисковые системы, базы данных);
  • Тайминги — разное время выполнения может влиять на порядок обработки параллельных вызовов;
  • Рандомизированные алгоритмы — эвристики с random seed (например, выбор инструмента на основе семпла).

Почему это проблема для тестирования Традиционные подходы (юнит-тесты с assert на точный ответ) не работают — ожидаемый результат может отличаться даже при корректном поведении.


2. Зачем тестировать недетерминированность

  • Воспроизводимость багов: если сбой случается не каждый раз, его трудно локализовать без контроля случайности.
  • Надёжность в production: агент должен стабильно давать полезный результат, а не «зависать» на одной из ветвей.
  • Метрики качества: нужно убедиться, что разброс метрик (accuracy, latency) находится в допустимых пределах.
  • Доверие пользователей: если агент сегодня ответил одно, а завтра другое — пользователь перестаёт ему верить.

3. Метод 1: Детерминированный seed (Deterministic seed)

Фиксируем все источники случайности:

  • LLM: установить temperature=0 (жадная декодировка — детерминированно) и фиксировать seed (где поддерживается).
  • Генератор случайных чисел Python: random.seed(42) и numpy.random.seed(42).
  • Прочие библиотеки: torch.manual_seed(42), tf.random.set_seed(42).
import random, numpy as np, torch, os

def set_global_seed(seed=42):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    os.environ["PYTHONHASHSEED"] = str(seed)

# Пример вызова LLM с детерминированным режимом
llm = ChatOpenAI(model="gpt-4", temperature=0, seed=42)

Ограничения:

  • Не все провайдеры LLM поддерживают seed (например, Anthropic Claude в API не принимает seed).
  • Внешние API (поиск, погода) не управляются seed.
  • Результат может меняться при обновлении модели.

Когда использовать: для регрессионных тестов и отладки конкретных сценариев.


4. Метод 2: Многократные прогоны (Multiple runs)

Запускаем агента на одном и том же входе N раз (например, 10-50) и собираем статистику.

Шаги:

  1. Зафиксировать вход (query, контекст, доступные инструменты).
  2. Запустить агента N раз.
  3. Для каждого прогона записать: вывод, время выполнения, использованные инструменты.
  4. Построить распределение метрик.

Метрики для сбора:

МетрикаЧто измеряет
Точность (accuracy)Доля прогонов с корректным конечным ответом (если есть золотой ответ)
Разброс ответов (diversity)Количество уникальных ответов / N
Среднее времяСредняя задержка
Std времениСтабильность времени
% успешных вызововДоля прогонов без ошибок
import statistics

def test_agent_several_runs(agent, query, n=10):
    results = []
    for _ in range(n):
        result = agent.run(query)
        results.append(result)
    # Пример: проверка, что точность >= 80%
    correct = sum(1 for r in results if is_correct(r)) / n
    assert correct >= 0.8, f"Accuracy too low: {correct}"

Ограничения: требует больше времени, не даёт строгой гарантии.


5. Метод 3: Статистические тесты (Statistical tests)

Используем доверительные интервалы и гипотезы:

  • Доверительный интервал для метрики: запускаем N прогонов, считаем среднее и 95% доверительный интервал (через bootstrap или нормальное приближение).
  • Проверка гипотезы: H0 — точность >= порога, H1 — ниже. Если p-value > 0.05, то не отвергаем H0.

Формула bootstrap для среднего (accuracy):

1. Из N наблюдений с заменой генерируем B=1000 выборок.
2. Для каждой считаем среднее.
3. 95% CI — 2.5-й и 97.5-й перцентили этих средних.
import numpy as np

def bootstrap_ci(accuracies, B=1000, alpha=0.05):
    boots = []
    for _ in range(B):
        sample = np.random.choice(accuracies, size=len(accuracies), replace=True)
        boots.append(np.mean(sample))
    lower = np.percentile(boots, 100 * alpha / 2)
    upper = np.percentile(boots, 100 * (1 - alpha / 2))
    return lower, upper

accuracies = [0.9, 0.7, 0.8, 1.0, 0.9, 0.6, 0.95, 0.85, 0.75, 0.9]
ci = bootstrap_ci(accuracies)
assert ci[0] >= 0.7, f"CI lower bound {ci[0]:.2f} < 0.7"

Ограничения: нужно достаточно прогонов (обычно >= 30 для нормального приближения), интерпретация p-value может быть неочевидна.


6. Метод 4: Монте-Карло симуляции (Monte Carlo simulation)

Агент может иметь несколько точек недетерминизма (выбор инструмента, вызов LLM, задержки). Монте-Карло — имитация множества траекторий с разными случайными выборами.

Идея:

  • Заменяем реальные LLM-вызовы на mock с заданной вероятностью успеха/неудачи.
  • Или используем симулятор окружения с заданным распределением задержек.
  • Прогоняем тысячи симуляций, оцениваем вероятность неудачи (risk) или распределение метрик.

Пример: агент делает три шага; на каждом шаге есть 10% шанс ошибки. Какова вероятность, что агент завершит успешно?

import random

def simulate_agent_trajectory(error_prob=0.1):
    steps = 3
    for _ in range(steps):
        if random.random() < error_prob:
            return "fail"
    return "success"

def monte_carlo_estimate(num_simulations=10000):
    successes = sum(1 for _ in range(num_simulations) if simulate_agent_trajectory() == "success")
    return successes / num_simulations

prob_success = monte_carlo_estimate()
print(f"Вероятность успеха: {prob_success:.2%}")

Применение: тестирование отказоустойчивости, изучение "хвостов" (p99 latency), определение необходимого числа повторных попыток.


7. Метод 5: Golden path (золотой путь)

Выделяем детерминированное ядро агента — часть, которая не вызывает LLM или внешние API. Например:

  • Логика выбора инструмента на основе фиксированных правил (if-else).
  • Обработка результатов, форматирование ответа.
  • Безопасные проверки (валидация входов).

Тестирование golden path можно делать обычными юнит-тестами с assert.

Пример: агент сначала вызывает инструмент поиска, потом LLM. Можно отдельно протестировать:

  • Функцию парсинга результатов поиска (детерминированная).
  • Функцию сборки промпта (детерминированная).
  • Функцию пост-обработки ответа LLM (детерминированная).
def parse_search_results(raw: str) -> list:
    # детерминированный парсинг
    return [line.strip() for line in raw.split("\n") if line]

def test_parse_search_results():
    raw = "result1\nresult2"
    assert parse_search_results(raw) == ["result1", "result2"]

Преимущество: недетерминированность изолируется в тонких слоях (LLM, API), а ядро можно тестировать быстро и надёжно.


8. Инструменты для тестирования недетерминированных агентов

ИнструментНазначениеПример использования
pytest + параметризацияМногократный запуск тестов@pytest.mark.parametrize("run", range(5))
hypothesisProperty-based testing, поиск крайних случаев@given(st.integers())
langchain @configurableНастройка seed, temperatureconfig = {"llm": ChatOpenAI(temperature=0, seed=42)}
mlflow / wandbЛогирование распределений метрикwandb.log({"accuracy_mean": acc_mean, "accuracy_std": acc_std})
deepdiffСравнение структурированных выводов (игнорируя небольшие расхождения)from deepdiff import DeepDiff

9. Метрики нестабильности агента

Для количественной оценки недетерминированности вводим:

  • Variance of accuracy: std(accuracy_i) по разным seed или прогонам.
  • Response diversity: количество уникальных финальных ответов / число прогонов.
  • Latency stability: коэффициент вариации (std/mean) времени выполнения.
  • Tool call consistency: для заданного входа агент всегда ли выбирает одинаковый инструмент?
from collections import Counter

def tool_consistency(agent, query, n=10):
    tools_used = []
    for _ in range(n):
        trace = agent.get_trace(query)
        tools_used.append(trace.final_tool)  # предположим
    counts = Counter(tools_used)
    most_common_ratio = counts.most_common(1)[0][1] / n
    return most_common_ratio  # 1.0 = всегда один и тот же инструмент

10. Практическая стратегия тестирования

Рекомендуемый подход в enterprise:

  1. Golden path → 90% кода покрыть обычными юнит-тестами (детерминированная логика).
  2. Seed-тесты → для критических LLM-вызовов зафиксировать temperature=0 и seed, записать эталонные ответы.
  3. Статистические тесты → для end-to-end сценариев: 5-10 прогонов, проверка, что accuracy >= порога с bootstrap CI.
  4. Монте-Карло → для оценки рисков при высокой нагрузке или ненадёжных API.
  5. CI/CD пайплайн:
    • Fast track: golden path (сек.).
    • Slow track: 10 прогонов с seed (минуты).
    • Nightly: 50-100 прогонов с bootstrap (часы).

Пет-проект для закрепления

Задача: Разработать простого агента, который отвечает на вопрос, используя результаты поиска через API (например, DuckDuckGo). Агент недетерминирован из-за разных результатов поиска и LLM температуры. Написать тесты.

Инструменты: Python, pytest, duckduckgo_search, langchain или прямые вызовы OpenAI/Anthropic API, библиотека numpy для статистики.

Шаги:

  1. Реализуйте агента: получает вопрос → вызывает DuckDuckGo → формирует промпт с результатами → LLM генерирует ответ.
  2. Напишите тест test_golden_path — замените поиск и LLM на заглушки (детерминированные).
  3. Напишите тест test_seed_reproducibility — зафиксируйте temperature=0 и seed, проверьте, что ответ совпадает с эталоном.
  4. Напишите тест test_multiple_runs — запустите 10 раз с реальным поиском, убедитесь, что ответ всегда осмыслен (например, содержит ключевые слова).
  5. Напишите тест test_monte_carlo_risk — симулируйте 1000 сценариев, где поиск возвращает ошибку с вероятностью 20% (mock), проверьте, что агент обрабатывает ошибки.

Ожидаемый результат: тесты проходят, вы получаете количественное представление о вариативности агента и можете уверенно его релизить.


Связь с другими вопросами

ВопросТема
781Как спроектировать агентную архитектуру?
782Какие инструменты и функции должен использовать агент?
783Как управлять памятью агента (conversation history)?
784Как обеспечить безопасность и пермишены в агенте?
786Как обеспечить наблюдаемость и отладку агента?

Навигация