中文翻译暂不可用,显示俄语原文。
Реализовать test generation для агента
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать test generation для агента
1. Цель задачи
Научиться автоматизировать генерацию тестовых запросов (prompts) для системного тестирования AI-агента с помощью LLM. На основе заранее определённых шаблонов LLM должен генерировать валидные, разнообразные и покрывающие краевые случаи запросы. Ключевой результат рабочий пайплайн, который генерирует 500 уникальных тестовых запросов за 5 минут и может быть интегрирован в CI/CD.
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
| Описание тестируемого AI-агента (назначение, инструменты, ожидаемое поведение) | Документация проекта или открытый аналог (например, LangChain Agent) |
| Шаблоны тестовых запросов (типы, слоты, ожидаемые ответы или действия) | Разработать самостоятельно на основе требований к агенту |
| Примеры ручных тестов (5–10 штук) | Предыдущие тесты, issue трекер, регрессионные кейсы |
| Среда для запуска агента (локально или Docker) | Код агента; если нет – использовать mock-агента |
| LLM API ключ (OpenAI, Anthropic или локальная модель) | Личный аккаунт, облачный сервис или запуск локальной модели |
Если нет реального инструмента — симулируем:
- Создайте простого Python-агента на базе LangChain с 2–3 инструментами (например, calculator, weather, search). Код можно взять из учебных примеров LangChain.
- Определите 5–7 типов запросов (например, вызов одного инструмента, комбинированный запрос, запрос без инструмента, запрос с неверными данными).
- Зафиксируйте ожидаемый формат ответа (строковый или JSON) для каждого типа.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| LLM для генерации | GPT-4o-mini / Claude Haiku / Qwen2.5 (локально) | Генерация тестовых запросов |
| Фреймворк для агента | LangChain / LlamaIndex | Среда тестирования (целевой агент) |
| Разработка | Python 3.11+, Pydantic | Определение шаблонов, валидация, логика |
| Управление промптами | LangChain PromptTemplate / Jinja2 | Шаблонизация запросов к LLM |
| Оптимизация производительности | asyncio, aiohttp, batch API | Параллельная генерация |
| Тестирование | pytest, pytest-asyncio | Прогон сгенерированных запросов |
| CI/CD | GitHub Actions, GitLab CI | Автоматический запуск при мерже |
| Версионирование данных | Git LFS или DVC | Хранение сгенерированных тестов (опционально) |
4. Этапы выполнения
Этап 1: Анализ и подготовка шаблонов (оценка: 1 час)
Действия
-
Инвентаризация типов тестовых запросов
- Выделите 5–7 категорий: single_tool, multi_tool, ambiguous, error_handling, context_only, out_of_scope, adversarial.
- Для каждой категории опишите, какие слоты (переменные) должны подставляться (например,
{tool_name},{argument},{expected_action}). - Запишите ожидаемое поведение агента (какой инструмент вызван, какой ответ).
-
Создание шаблонного файла в формате YAML или JSON:
- category: "single_tool" template: "Сколько будет {num1} {operator} {num2}?" slots: num1: [1, 10, 100, -5, 3.14] operator: ["+", "-", "*", "/"] expected_tool: "calculator" variants: 5 - category: "error_handling" template: "Найди погоду в городе {city}" slots: city: ["", "None", "Нью-Йорк", "НесуществующийГород123"] expected_tool: "weather" variants: 3- Убедитесь, что количество слотов и их возможные значения покрывают краевые случаи (пустые строки, спецсимволы, очень длинные строки).
-
Определите число генерируемых запросов:
- Исходя из цели «500 запросов за 5 минут», распределите количество по категориям (например, 100, 100, 100, 100, 50, 50).
Ожидаемый результат этапа Файл templates.yaml (или templates.json) с минимум 5 категориями и валидными слотами.
Этап 2: Разработка генератора тестов (оценка: 2.5 часа)
Действия
-
Реализуйте класс
TestGenerator, который:- Читает шаблоны;
- Для каждого шаблона итеративно комбинирует значения слотов (декартово произведение или случайная подвыборка с учётом
variants); - Передаёт заполненный шаблон в LLM для парафразирования/вариации (чтобы запросы были разнообразными) или просто использует комбинаторику без LLM, если запросы строгие. В данной задаче используем LLM для генерации разнообразных формулировок того же интеншена.
-
Пример кода (main.py):
from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI import asyncio import yaml from pydantic import BaseModel class Template(BaseModel): category: str template: str slots: dict variants: int expected_tool: str = "" class TestGenerator: def __init__(self, templates_path: str, llm): with open(templates_path) as f: self.templates = [Template(**t) for t in yaml.safe_load(f)] self.llm = llm async def generate_variant(self, template: Template, slot_values: dict) -> str: filled = template.template.format(**slot_values) # Промпт для парафразирования prompt = f"Сгенерируй один естественный вопрос пользователя, который означает то же самое, что и запрос ниже. Не добавляй объяснений.\n\nЗапрос: {filled}" response = await self.llm.agenerate([[prompt]]) return response.generations[0][0].text.strip() async def run(self): tasks = [] for tmpl in self.templates: # Создать комбинации слотов (упрощённо) import itertools keys = list(tmpl.slots.keys()) combos = list(itertools.product(*[tmpl.slots[k] for k in keys])) # Ограничить количество комбинаций import random random.shuffle(combos) used = set() for combo in combos[:tmpl.variants]: slot_dict = dict(zip(keys, combo)) if tuple(combo) in used: continue used.add(tuple(combo)) tasks.append(self.generate_variant(tmpl, slot_dict)) results = await asyncio.gather(*tasks) return results -
Обработка дубликатов:
- Добавьте в
generate_variantпроверку уникальности по эмбеддингам (через sentence-transformers и cosine similarity < 0.95) или просто по exact match.
- Добавьте в
-
Вывод результатов:
Ожидаемый результат этапа Скрипт generate_tests.py, который за 1 прогон генерирует минимум 500 запросов (с учётом параллельности – 100–200 запросов к LLM, остальные – комбинаторные без LLM). Важно уложиться в 5 минут.
Этап 3: Интеграция с тестовым прогоном (оценка: 1.5 часа)
Действия
-
Напишите pytest-фикстуру, которая загружает сгенерированные запросы и подставляет их в тест:
import pytest import json from agent import run_agent # ваш агент @pytest.fixture(scope="session") def test_requests(): with open("test_requests.jsonl") as f: return [json.loads(line) for line in f] @pytest.mark.parametrize("req", test_requests()) def test_agent_response(req): response = run_agent(req["query"]) # Для простоты проверяем, что агент вызвал ожидаемый инструмент assert req["expected_tool"] in response.get("used_tools", []), \ f"Query: {req['query']}. Expected tool {req['expected_tool']}, got {response.get('used_tools')}" -
Добавьте логирование времени выполнения – убедитесь, что прогон 500 тестов не превышает 10 минут (с учётом задержек API агента). Для этого можно использовать pytest-timeout.
Ожидаемый результат этапа Тест pytest test_agent.py -n auto успешно запускается, покрывая 500+ кейсов, и генерируется отчёт.
Этап 4: Оптимизация производительности (оценка: 1 час)
Действия
-
Замените последовательные вызовы LLM на batch-запросы (OpenAI поддерживает batch).
async def batch_generate(self, prompts: list[str]) -> list[str]: # Использовать create_batch или parallel API responses = await asyncio.gather(*[self.llm.agenerate([[p]]) for p in prompts]) return [r.generations[0][0].text.strip() for r in responses] -
Кэшируйте результаты генерации – если шаблоны не менялись, переиспользуйте прошлый файл. Используйте hashlib.md5 от содержимого templates.yaml.
-
Измерьте время после оптимизации. Цель: 500 запросов за <5 минут (при разумном лимите API). Если не достигается – снизьте долю LLM-генерации: пусть LLM генерирует только 10–20% уникальных формулировок, остальное – комбинаторика.
Ожидаемый результат этапа В логах CI видно время генерации < 5 минут.
Этап 5: Документация и автоматизация (оценка: 1 час)
Действия
- Напишите README.md с описанием:
- Как запустить генерацию: python generate_tests.py
- Как запустить тесты: pytest test_agent.py
- Как добавить новый шаблон.
- Создайте Makefile с целями generate,
test,clean. - Добавьте GitHub Action:
name: Test Generation on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dependencies run: pip install -r requirements.txt - name: Generate tests run: python generate_tests.py - name: Run agent tests run: pytest test_agent.py --junitxml=report.xml
Ожидаемый результат этапа Пайплайн в CI зелёный, генерация и прогон занимают <15 минут суммарно.
5. Критерии приемки (Definition of Done)
- Сгенерировано 500 уникальных тестовых запросов (проверка по exact match или эмбеддингам).
- Время генерации и сохранения в файл не превышает 5 минут (замеряется в CI).
- Каждый запрос соответствует хотя бы одному шаблону из templates.yaml.
- Тестовый прогон (pytest) проходит успешно (pass rate ≥ 95%).
- Код генератора покрыт unit-тестами (минимум 2 теста: на уникальность и на корректность шаблонов).
- Процесс документирован (README, Makefile, CI конфиг).
6. Ожидаемый результат
Основные артефакты
generate_tests.py– генератор тестов.- templates.yaml – конфигурация шаблонов (минимум 5 категорий).
- test_requests.jsonl – файл со сгенерированными запросами.
test_agent.py– тест, параметризованный сгенерированными запросами.- Makefile – команды generate,
test. .github/workflows/test_generation.yml– CI пайплайн.
Опционально
requirements.txtсо всеми зависимостями.report.xml– отчёт pytest для интеграции с Allure или другим инструментом.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| LLM генерирует дублирующиеся запросы | Добавить дедупликацию на этапе сохранения (embeddings + cosine similarity < 0.95) |
| Время генерации превышает 5 минут из-за лимитов API | Использовать batch API (OpenAI), уменьшить число LLM-запросов (генерировать через LLM только 10–20% уникальных вариантов, остальные – комбинаторно без LLM) |
| Агент не поддерживает быстрое тестирование (500 запросов могут занять часы) | Использовать mock-версию агента без внешних вызовов (заменять API заглушками) |
| Сложность поддержки шаблонов при изменении агента | Вынести шаблоны в отдельный YAML-файл с документацией полей; добавить тесты на валидность шаблонов (pydantic validation) |
| Промпт для парафразирования даёт неадекватные результаты | Итеративно улучшать промпт: добавить примеры, ограничить длину, указать формат (один вопрос в строке) |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Анализ и подготовка шаблонов | 1 час |
| Этап 2: Разработка генератора | 2.5 часа |
| Этап 3: Интеграция с тестовым прогоном | 1.5 часа |
| Этап 4: Оптимизация производительности | 1 час |
| Этап 5: Документация и CI | 1 час |
| Итого | 7 часов |
Примечание Для первого раза рекомендуется добавить 1–2 часа на отладку и настройку LLM-промптов.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 42 | Методы генерации тестовых данных с помощью LLM |
| 107 | Асинхронная обработка запросов в Python (asyncio) |
| 214 | Параметризованные тесты в pytest |
| 318 | Оптимизация latency при batch-запросах к OpenAI |
| 429 | Работа с YAML-конфигами в Python |
| 531 | CI/CD для AI-проектов (GitHub Actions) |
| 664 | Дедупликация текстов с помощью эмбеддингов |
| 720 | Шаблонизация промптов (PromptTemplate, Jinja2) |
| 801 | Тестирование AI-агентов: best practices |
| 890 | Производительность генерации: замеры и профилирование |
10. Чек-лист самопроверки
- Я проверил, что сгенерированные 500 запросов уникальны (нет exact дубликатов).
- Я замерил время выполнения
generate_tests.pyи убедился, что оно <5 минут. - Я проверил, что каждый запрос из
test_requests.jsonlсоответствует одному из шаблонов (можно протестировать парсингом). - Я запустил
pytest test_agent.py– все тесты прошли (или я задокументировал, какие упали и почему). - Я проверил, что CI-пайплайн зелёный и артефакты (report.xml) создаются.