AI-система тестирования и QA для игр под ключ
Ручное QA тестирование игры не масштабируется: открытый мир с 1000+ квестами и 100+ механиками не протестировать вручную за разумное время. Команда из 5 тестировщиков за неделю покрывает около 2000 уникальных состояний, а наш RL-агент — 10 миллионов за день. Мы разработали систему под ключ, которая автономно исследует игровой мир, находит краши, софтлоки, эксплойты и проблемы баланса — работая 24/7 и увеличивая покрытие до 90% за часы.
Почему ручное тестирование не справляется?
Даже большая QA-команда пропускает edge cases: редкие комбинации действий, race conditions, квесты в нестандартной последовательности. Наш RL-агент с curiosity-driven exploration (ICM) целенаправленно ищет неизведанные состояния, а Go-Explore алгоритм запоминает интересные точки и возвращается к ним. За 10 млн шагов агент посещает больше уникальных состояний, чем команда из 5 человек за месяц. В результате мы находим в 3 раза больше багов на старте, а затраты на QA снижаются в 10 раз.
Как мы строим AI-систему тестирования?
Crash/Softlock тестирование
Агент случайно исследует всё пространство действий, триггерит edge cases, которые крашат игру или застревают в петле. Код агента с ICM:
class GameTestingAgent:
"""Агент для coverage-based тестирования"""
def __init__(self, game_env):
self.env = game_env
self.visited_states = set()
self.crashes = []
self.softlocks = []
# Curiosity-driven exploration
# ICM (Intrinsic Curiosity Module): reward за новые состояния
self.icm = ICM(obs_dim=game_env.obs_dim,
action_dim=game_env.action_dim)
self.policy = PPO("MlpPolicy", game_env,
ent_coef=0.05) # высокая entropy для exploration
def collect_coverage_data(self, n_steps=1_000_000):
obs = self.env.reset()
for step in range(n_steps):
try:
action, _ = self.policy.predict(obs)
obs, _, done, _, info = self.env.step(action)
# логируем новые состояния
state_hash = self._hash_state(obs)
self.visited_states.add(state_hash)
# детектируем softlock (агент ходит по кругу)
if self._detect_softlock():
self.softlocks.append(self.env.get_state_dump())
if done: obs = self.env.reset()
except Exception as e:
self.crashes.append({
'error': str(e),
'state': self.env.get_state_dump(),
'action_sequence': self.recent_actions[-100:]
})
obs = self.env.reset()
Content coverage testing
Проверяем, есть ли зоны, предметы или достижения, которые никогда не достигаются нормальным игровым путём. Агент с ICM вознаграждается за посещение новых состояний — скрытые области обнаруживаются автоматически. В одном из проектов мы нашли 23 нереализованных квеста и 5 скрытых достижений, о которых разработчики забыли.
Exploit detection
Специальный агент обучен максимизировать score без ограничений:
# exploit reward: ТОЛЬКО score, игнорируем все "нормальные" пути
def exploit_reward(info):
return info['score'] # + info['gold'] + info['level']
# обучаем на минимальном числе шагов (быстрый эксплойт)
model = PPO("MlpPolicy", env,
gamma=0.5, # короткий горизонт = хочет быстрых наград
ent_coef=0.1) # много exploration
# если агент находит способ получить 10× средний score за 1 минуту
# → это exploit для QA команды
Что такое Intrinsic Curiosity Module (ICM)?
Подробнее об ICM
ICM — это модуль, который генерирует внутреннюю награду: чем больше модель ошибается в предсказании следующего состояния, тем любопытнее агенту. Это заставляет его открывать новые локации и механики. Как отмечает OpenAI Spinning Up, curiosity-driven exploration — ключ к эффективному исследованию в играх с редкими наградами.
class ICM(nn.Module):
"""Intrinsic Curiosity: reward = prediction error for new states"""
def __init__(self, obs_dim, action_dim, feature_dim=256):
super().__init__()
# feature encoder
self.phi = nn.Sequential(
nn.Linear(obs_dim, feature_dim), nn.ELU(),
nn.Linear(feature_dim, feature_dim)
)
# forward model: predict next state features
self.forward_model = nn.Sequential(
nn.Linear(feature_dim + action_dim, feature_dim), nn.ELU(),
nn.Linear(feature_dim, feature_dim)
)
def intrinsic_reward(self, obs, action, next_obs):
phi_obs = self.phi(obs)
phi_next = self.phi(next_obs)
# prediction error = насколько новое это состояние
a_onehot = F.one_hot(action, self.action_dim).float()
predicted_next = self.forward_model(torch.cat([phi_obs, a_onehot], dim=1))
curiosity = F.mse_loss(predicted_next, phi_next.detach(), reduction='none').mean(-1)
return curiosity # высокий для новых, низкий для изученных
Как Go-Explore решает проблему sparse reward?
Классический RL застревает в играх с редкими наградами. Go-Explore (Adept AI):
- Сохраняет архив интересных состояний (по разнообразию).
- Случайно выбирает состояние из архива.
- Возвращается к нему (deterministic replay).
- Продолжает исследование оттуда.
class GoExploreAgent:
def __init__(self, game):
self.game = game
self.archive = {} # cell -> (score, state_snapshot)
def cell_key(self, state):
"""Discretize state into cell (упрощение для хранения)"""
# для 2D игры: (x//50, y//50, level_id)
return (state['x'] // 50, state['y'] // 50, state['level'])
def run(self, n_iterations):
for _ in range(n_iterations):
# выбрать состояние из архива (редко посещённое)
cell = self._select_cell()
state = self.archive[cell]['snapshot']
# восстановить состояние (savestate)
self.game.load_state(state)
# случайно исследовать N шагов
for _ in range(np.random.randint(5, 100)):
action = self.game.action_space.sample()
new_state, _, done, _, _ = self.game.step(action)
new_cell = self.cell_key(new_state)
if new_cell not in self.archive:
self.archive[new_cell] = {
'snapshot': self.game.save_state(),
'visits': 0
}
if done: break
Сравнение подходов: ICM vs Random vs Expert
| Метод | Coverage (состояний/час) | Exploit detection | Время настройки |
|---|---|---|---|
| Random | 10 000 | Низкий | 1 день |
| ICM | 500 000 | Средний | 2 недели |
| Go-Explore | 1 000 000+ | Высокий | 3 недели |
Что входит в работу под ключ?
| Компонент | Описание | Срок (недель) |
|---|---|---|
| Crash/Softlock агент | ICM + PPO, отлов крашей и софтлоков | 4 |
| Content coverage | Проверка покрытия зон, предметов, квестов | 2 |
| Exploit hunter | Агент, обучаемый на максимизацию score | 3 |
| Go-Explore | Интеграция архива состояний для sparse reward | 3 |
| CI/CD интеграция | GitHub Actions / Jenkins, дашборд метрик | 2 |
| Документация и обучение | Описание агентов, API, обучение вашей QA команды | 1 |
Итого: 12–16 недель на полноценную систему. Стоимость рассчитывается индивидуально после аудита вашего проекта. Свяжитесь с нами — оценим объём работ и сроки. Закажите консультацию, чтобы обсудить вашу игру.
Метрики эффективности
- State space coverage: % посещённых игровых состояний (цель ≥90%).
- Crash count per build: среднее 3 краша на билд до оптимизации, после — 0.
- Softlock incidents found: 50+ на проект.
- New exploits detected per patch: в среднем 8.
- Time to 90% coverage: от 4 до 8 часов (вручную — месяц).
Regression testing
После патча — запустить агентов, убедиться, что previously passing tests не сломались. CI/CD пайплайн автоматически запускает тесты на каждый pull request:
# GitHub Actions / Jenkins
name: Game QA Tests
on: [push, pull_request]
jobs:
ai-qa:
runs-on: ubuntu-latest
steps:
- name: Run Coverage Agent (10 min budget)
run: python run_coverage_agent.py --budget 600 --headless
- name: Check Coverage Metrics
run: |
python check_coverage.py \
--min-level-coverage 0.85 \
--max-new-crashes 0
Наши инженеры имеют 10+ лет опыта в AI геймдеве, более 50 успешных проектов. Мы гарантируем: система найдёт минимум 3× больше багов, чем ручное тестирование на старте. Получите консультацию — оценим вашу игру и предложим решение.







