Voice Agent Testing Without Mic: Automated Eval with Amazon Nova Sonic | AiManual
AiManual Logo Ai / Manual.
14 Июн 2026 Гайд

Automated Voice Agent Testing Without a Microphone: Scaling Evaluations with Amazon Nova Sonic

Узнайте, как тестировать голосовых агентов без микрофона, используя Amazon Nova Sonic. Пошаговый гайд по автоматизации eval-сценариев для CI/CD и регрессионного

Реклама
cliv1

Ты собираешь голосового агента. Собираешь долго. Наконец задеплоил. И тут начинается ад — ручное тестирование. Микрофон, «Алиса, включи музыку», пауза, слушаешь ответ, снова говоришь. 20 сценариев — час времени. А если нужно проверить регрессию после каждого коммита? Руки опускаются.

Я перепробовал всё: запись реальных разговоров, эмуляцию аудиопотоков через PulseAudio, виртуальные микрофоны. Всё это — костыли. Они не масштабируются, падают в CI, требуют физического присутствия железа. А потом я наткнулся на подход, который называется Automated Voice Agent Testing Without a Microphone. И да, это работает. А главное — Amazon Nova Sonic делает это нативным.

Почему ручное тестирование голоса — это пуля в колено

Давай честно: голосовые агенты тестируют так же, как GUI в 2005 году — вручную. Открываешь демо, говоришь в микрофон, смотришь на логи. Один сценарий — 2-3 минуты. 50 сценариев — полтора часа. А если агент начал галлюцинировать на третьем вопросе? Начинаем сначала.

Проблема номер один: человеческий голос невоспроизводим. Ты не сможешь сказать одинаково два раза подряд. Воспроизводимость тестов — святая корова QA — летит в бездну. Проблема номер два: нет триггера для CI. Никто не садится за микрофон при каждом пуше. Итог — регрессии живут в проде, а пользователи слышат странные ответы.

Внимание: если ваш voice agent уже в production и у вас нет автоматизированных тестов — вы играете в русскую рулетку. Один неудачный промпт — и агент начинает отвечать по-китайски. Это не шутка.

Amazon Nova Sonic: единая модель вместо каскада

Если ты читал мою предыдущую статью про Amazon Nova Sonic, ты знаешь главное: эта модель — не просто ASR + LLM + TTS, слепленные скотчем. Она принимает сырые аудиоданные (PCM или Opus в WebSocket) и выдаёт аудиопоток. А значит, мы можем подавать на вход не микрофон, а файл или синтезированную речь. И это меняет всё.

Автоматизация тестирования без микрофона строится на простой идее: берём текстовые сценарии, превращаем их в аудио (любой TTS), отправляем в Nova Sonic через Bedrock Runtime API, получаем аудиоответ, транскрибируем его и сравниваем с эталоном. Всё это — в Python-скрипте, который запускается в GitHub Actions.

Никаких виртуальных аудиоустройств. Никаких Xvfb. Только API. Только код.

Шаг за шагом: как собрать тестовый пайплайн

1 Готовим сценарии и синтезируем речь

Пишем YAML-файл с тестами. Каждый тест — пара (user_input, expected_response). User_input — это фраза пользователя. Мы её синтезируем в аудио с помощью любого TTS. Я использую Amazon Polly (он уже в AWS) или NeuTTS Nano — лёгкий on-device синтезатор.

# scenarios.yaml
tests:
  - id: "greeting"
    user_input: "Привет, какой сегодня курс биткоина?"
    expected_partial: "Биткоин"  # проверяем, что в ответе есть это слово
    
  - id: "fallback"
    user_input: "Расскажи анекдот про хлеб"
    expected_partial: "извините"  # если агент не знает — он должен извиниться

Дальше генерируем WAV-файлы. Это можно сделать прямо в тестовом скрипте:

import boto3
from io import BytesIO

polly = boto3.client('polly')

def synthesize(text: str) -> bytes:
    response = polly.synthesize_speech(
        Text=text,
        OutputFormat='pcm',
        VoiceId='Maxim',      # русский голос
        SampleRate='16000'
    )
    return response['AudioStream'].read()
💡
Используйте PCM 16 кГц — это нативный вход для Nova Sonic. Opus тоже подойдёт, но PCM проще дебажить.

2 Отправляем аудио в Nova Sonic и получаем ответ

Используем Converse API с поддержкой аудио-контента. В Bedrock Nova Sonic умеет принимать и возвращать аудиоблоки. Главное — правильно сформировать сообщение:

import boto3
import json

bedrock = boto3.client('bedrock-runtime', region_name='us-east-1')

def invoke_nova_sonic(audio_bytes: bytes, sample_rate=16000):
    response = bedrock.converse(
        modelId='amazon.nova-sonic-v1:0',
        messages=[
            {
                'role': 'user',
                'content': [
                    {
                        'audio': {
                            'source': {
                                'bytes': audio_bytes,
                                'mediaType': 'audio/pcm'
                            },
                            'sampleRate': sample_rate
                        }
                    }
                ]
            }
        ]
    )
    # Ответ может содержать аудио и/или текст
    assistant_content = response['output']['message']['content']
    # Проверяем, есть ли text
    for block in assistant_content:
        if 'text' in block:
            return block['text']
        elif 'audio' in block:
            # Если вернулось аудио — транскрибируем (или сразу сравниваем по семантике)
            return block['audio']['transcript']
    return ""

Осторожно: Nova Sonic может возвращать аудио даже если ты просишь текст. В документации AWS это описано как особенность мультимодальных моделей. Поэтому всегда проверяй оба поля.

3 Сравниваем ответ с эталоном

Тут есть тонкость: сравнивать посимвольно — глупо. Агент может сказать "Курс биткоина сейчас 59 000 долларов" вместо "Курс биткоина составляет 59000 USD". Нужна семантическая близость или хотя бы частичное совпадение.

Я использую комбинацию: - Partial match — проверяем, что ключевое слово есть в ответе. - Cosine similarity через embeddings (Amazon Nova Embeddings).

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

def semantic_similarity(expected, actual, threshold=0.8):
    emb1 = model.encode(expected)
    emb2 = model.encode(actual)
    sim = cosine_similarity([emb1], [emb2])[0][0]
    return sim >= threshold, sim

Для быстрой проверки в CI достаточно частичного совпадения — это уже отсечёт 90% регрессий.

4 Запускаем в CI/CD

Всё упаковываем в Python-скрипт и кладём в репу. Для CI я использую pytest с параметризацией:

# test_voice_agent.py
import pytest
import yaml

with open('scenarios.yaml') as f:
    scenarios = yaml.safe_load(f)['tests']

@pytest.mark.parametrize('scenario', scenarios, ids=lambda s: s['id'])
def test_voice_agent(scenario):
    audio = synthesize(scenario['user_input'])
    response = invoke_nova_sonic(audio)
    assert scenario['expected_partial'].lower() in response.lower(), \
        f"Ожидали '{scenario['expected_partial']}' в ответе, получили '{response}'"

В .github/workflows/test.yml просто устанавливаем зависимости и запускаем pytest. Вся магия — в работе с Converse API. Никаких микрофонов.

Грабли, на которые я наступил (и ты наступишь)

1. Nova Sonic не всегда возвращает текст

По умолчанию модель может ответить аудио. Если тебе нужен именно текст — добавь в промпт системную инструкцию "Отвечай только текстом". Но даже так иногда приходит аудио. Лови оба случая.

2. Аудио нужно ресемплировать

Polly выдаёт 16 кГц — норм. Но если берёшь синтез из другого источника (например, Vosk), может быть 8 кГц. Nova Sonic ожидает минимум 16 кГц. Используй librosa.resample или sox.

3. Задержки в CI — это боль

Каждый вызов модели занимает 2-5 секунд. 50 тестов — 4 минуты. Не смертельно, но можно ускорить параллелизацией. pytest-xdist спасает.

4. Не все сценарии покрыть текстом

Некоторые сценарии зависят от контекста разговора (несколько реплик). Для них используй историю сообщений (messages с несколькими чередованиями). Я описал это в статье про создание streaming-приложения с Nova Sonic и WebRTC.

Куда копать дальше: регрессионные наборы и метрики

Подход, который я описал, — база. Этого достаточно, чтобы покрыть 80% тестовых сценариев. Но если хочешь серьёзное enterprise-тестирование — посмотри на EVA-Bench 2.0. Там есть готовые бенчмарки с размеченными ответами и метрики вроде Voice Agent Quality (VAQ). Я интегрировал EVA-фреймворк в свой пайплайн — и сразу увидел, что мой агент плохо обрабатывает сложные вопросы с двойным отрицанием.

Для глубокого тестирования AI-агентов в production рекомендую прочитать статью про LangSmith + pytest + Bedrock. Там показано, как логировать каждый вызов и вычислять метрики качества.

💡
Лайфхак: храни эталонные аудиофайлы для каждого теста. Сравнивай не только текст, но и интонацию ответа. Nova Sonic возвращает аудио с эмоциями — если агент раньше отвечал весело, а теперь грубо, это регрессия тона. Используй аудио embedding (например, Wav2Vec2) чтобы ловить такие сдвиги.

Автоматизация тестов без микрофона — не фантастика. Это уже рабочее решение, которое я внедрил в нескольких продакшенах. Amazon Nova Sonic даёт API, который позволяет обойтись без звуковой карты. А значит — вписать тесты голосовых агентов в CI/CD стало реально.

Не повторяй моих граблей: не пытайся эмулировать устройство через ALSA, не заставляй QA сидеть с микрофоном. Просто отправляй PCM в Nova Sonic и проверяй ответ. Быстро, надёжно, без микрофона.

Через два года ни один voice-агент не будет выпущен в прод без pipeline, который гоняет 500+ сценариев автоматически. Те, кто не автоматизирует тестирование сейчас, утонут в багах. А ты — нет.

Подписаться на канал