В рамках проекта отдела качества мы анализируем работу продающих менеджеров по SLA (Service Level Agreement — соглашение об уровне обслуживания, что описывает параметры предоставляемой услуги). И решили протестировать несколько инструментов speech-to-text для транскрибации звонков. Среди тройки финалистов оказался и яндексовский SpeechSense, о нем и пойдет речь в статье.
Перед запуском
Pre Requisites: macOS, Python 3.10
Документация у SpeechSense Getting Started, мягко говоря, дырявая, и мне потребовалось также продираться через рекомендации саппорта, логи GitHub официального репозитория и бесполезные советы ChatGPT в течение нескольких часов.
Прежде чем последовать докам, создадим виртуальное окружение с той версией Python, на которой, вероятно, и пилят SpeechSense. Настоятельно рекомендую не игнорировать этот шаг:
python3.10 -m venv speechsense_env
Если создавать окружение оператором python / python3 (без указания конкретной версии), будет создано окружение на глобальной версии языкового пакета (у меня 3.11.5, это обронит весь пайплайн). Зайдем в окружение:
source speechsense_env/bin/activate
Чтобы заполнить некоторые обязательные аргументы скрипта подгрузки, нам потребуется создать облачное пространство в Yandex Cloud, подвести к нему SpeechSense как решение и сгенерировать проект с подключением. С этими шагами документация справляется благополучно.
Если вкратце, сервисный аккаунт создается так:
API-ключ тоже несложно получить. Идем по адресу console.yandex.cloud (предварительно авторизовавшись) и генерируем токен:
Клонируем репозиторий:
git clone https://github.com/yandex-cloud/cloudapi
Устанавливаем необходимые библиотеки:
pip3 install grpcio-tools googleapis-common-protos==1.5.10 protobuf==3.20.0
Затем создадим рабочую субдиректорию:
mkdir upload_data
Клонируем гугловскую библиотеку api-common-protos:
git clone https://github.com/googleapis/api-common-protos.git
Благодаря этим пунктам удастся избежать ошибки:
ImportError: cannot import name 'talk_service_pb2' from 'yandex.cloud.speechsense.v1' (unknown location)
Донастроим процедуру передачи файла на облако. Намеренно привожу пример со своими путями до директорий, чтобы не осталось вопросов, как выглядит этот шаг в июле 2024. Обратите внимание на две строки снизу: они отличаются от документации, и только с ними импорт talk_service_pb2 будет успешным:
python3 -m grpc_tools.protoc -I . -I /Users/elenakapatsa/Repositories/cloudapi/api-common-protos
--python_out=/Users/elenakapatsa/Repositories/cloudapi/upload_data
--grpc_python_out=/Users/elenakapatsa/Repositories/cloudapi/upload_data
yandex/cloud/speechsense/v1/.proto
yandex/cloud/speechsense/v1/analysis/.proto &&
На деле же мы задаем настройки gRPC (Google Remote Procedure Call) — способ вызвать тот или иной метод на сервере так, будто это происходит локально, на машине для разработки. Почему при замене путей до .proto на вышеуказанные все начинает работать, не понятно. В документации по-другому.
В случае SpeechSense, мы вызываем методы:
-
загрузки звонка в формате .wav в свое пространство
-
транскрибации звонка
-
анализа звонка по SLA
Если установка библиотек прошла без проблем, команда выше исполнится успешно и мы сможем перейти к самому скрипту подгрузки дорожки в облако. Заходим в рабочую папку:
cd upload_data
И теперь верстаю скрипт launch_me.sh с путем до файла:
python3 upload_grpc.py
--audio-path /Users/elenakapatsa/Repositories/cloudapi/wav_records/843401-b73a483915c1459fa1fdb07c11274a6b.wav
--connection-id "<Идентификатор подключения>"
--key "<Токен>"
Если токен сервисного аккаунта мы уже получили выше, то вот откуда брать connection-id (после создания облака, проекта и подключения):
В сам скрипт upload_grpc.py
закладываем код согласно документации:
import argparse
import json
from typing import Dict
import grpc
import datetime
from yandex.cloud.speechsense.v1 import talk_service_pb2
from yandex.cloud.speechsense.v1 import talk_service_pb2_grpc
from yandex.cloud.speechsense.v1 import audio_pb2
# Для аутентификации с IAM-токеном замените параметр api_key на iam_token
def upload_talk(connection_id: int, metadata: Dict[str, str], api_key: str, audio_bytes: bytes):
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel('api.talk-analytics.yandexcloud.net:443', credentials)
talk_service_stub = talk_service_pb2_grpc.TalkServiceStub(channel)
# Формирование запроса к API
request = talk_service_pb2.UploadTalkRequest(
metadata=talk_service_pb2.TalkMetadata(
connection_id=str(connection_id),
fields=metadata
),
# Формат аудио — WAV
audio=audio_pb2.AudioRequest(
audio_metadata=audio_pb2.AudioMetadata(
container_audio=audio_pb2.ContainerAudio(
container_audio_type=audio_pb2.ContainerAudio.ContainerAudioType.CONTAINER_AUDIO_TYPE_WAV
)
),
audio_data=audio_pb2.AudioChunk(data=audio_bytes)
)
)
# Тип аутентификации — API-ключ
response = talk_service_stub.Upload(request, metadata=(
('authorization', f'Api-Key {api_key}'),
# Для аутентификации с IAM-токеном передавайте заголовок
# ('authorization', f'Bearer {iam_token}'),
))
# Вывести идентификатор диалога
print(f'Dialog ID: {response.talk_id}')
if name == 'main':
parser = argparse.ArgumentParser()
parser.add_argument('--key', required=True, help='API key or IAM token', type=str)
parser.add_argument('--connection-id', required=True, help='Connection ID', type=str)
parser.add_argument('--audio-path', required=True, help='Audio file path', type=str)
parser.add_argument('--meta-path', required=False, help='JSON with the dialog metadata', type=str, default=None)
args = parser.parse_args()
# Значения по умолчанию, если метаданные не указаны
if args.meta_path is None:
now = datetime.datetime.now().isoformat()
metadata = {
'operator_name': 'Operator',
'operator_id': '1111', # Телфин отдает ID в панели статистики
'client_name': 'Client',
'client_id': '2222', # Сюда подставляют ID клиента из нашей CRM
'date': str(now),
'date_from': '2023-09-13T17:30:00.000',
'date_to': '2023-09-13T17:31:00.000',
'direction_outgoing': 'true',
}
else:
with open(args.meta_path, 'r') as fp:
metadata = json.load(fp)
with open(args.audio_path, 'rb') as fp:
audio_bytes = fp.read()
upload_talk(args.connection_id, metadata, args.key, audio_bytes)
На этом страдания завершены. После успешной вгрузки дорожки система отдает ID диалога в терминал:
(speechsense_env) elenakapatsa@kea-macbook-pro upload_data % bash launch_me.sh
Dialog ID: aud6lvar29df8crff2pt
Поделитесь в комментариях, как бы вы доверстали launch_me.sh, чтобы передавать циклом все дорожки в папке. Там еще дополнительные аргументы подавать предстоит (ID оператора, ID клиента).
Настал черед пройти в веб-интерфейс SpeechSense и посмотреть, что же такого нейронка нараспознавала и как она справляется с типичными грехами speech-to-text-инструментов, как то:
-
некорректно распознаваемые имена собственные и редкие слова (наш Инфостарт нередко становится «Инфостаром», «Инфобиржей» и даже «СССР»)
-
некорректная / отсутствуюшая пунктуация, усложняющая службе качества «скорочтение» транскрибации
-
неверно распознанные слова при дефектах речи (например, менеджер прикрыл рукой микрофон) и проч.
В моем проекте SpeechSense сразу отображаются транскрибированные звонки, и само устройство консоли выгодно отличает продукт от других опробованных (T-Bank VoiceKit, Таймлист 1С):
-
А вот и Killer Feature: YandexGPT сам оценивает менеджера по базовым параметрам (вежливость, вовлеченность, раздраженность, успешность и проч.). Если LLM что-то не учла, ей можно отправить фидбэк в догонку, чтобы в следующую итерацию результат улучшился. И дополнительные рубли не тратятся! Такое мы используем.
-
Чего не хватает стандартному промпту YaGPT, доделывается тегами. Теперь можно искать какие угодно события в массивах звонков:
-
Разговор удобно разбит на реплики, каждую из которых можно воспроизвести нажатием одной кнопки.
-
Развитая система аналитики, позволяющая строить отчеты буквально по любому параметру:
-
Изыскивать трудовые ресурсы на интеграцию не нужно: с провайдерами вроде Телфина Яндекс связывается напрямую.
Инструмента, который бы задвинул в угол конкурентов, пока не существует. Есть у продукта Яндекс и недостатки:
-
Подавать звонки лучше в .mp3, чтобы сохранить двухканальность. Иначе не везде голос оператора будет отделен от голоса покупателя.
-
Плохо восстановленная пунктуация. Тот же YandexGPT «внахлест» точно улучшит ситуацию.
Очень напрашивается дообучение на месте, чтобы поправить неверно распознанное имя собственное сразу, прямо в тексте реплики. Наши инженеры отдела качества смиренно дорабатывают транскрибацию вручную и саму оценку по SLA даже в 2024 году. Высокой степени автоматизации все так же не получается.
Кстати, на 10 звонков (длятся в среднем по 6,5 минут) ушло 55 рублей. Тариф для малых оборотов — 1,02 руб. за минуту. Обещают допилить API к концу 2024
Несмотря ни на что, очень хочется похвалить создателей. Несмотря на бессмысленные сложности с развертыванием, SpeechSense оставляет впечатление продуманного, оттестированного и превосходяшего конкурентов решения. «Важно быть счастливым в любом состоянии», даже если простой запуск демки занял слишком много времени.
ссылка на оригинал статьи https://habr.com/ru/articles/828910/
Добавить комментарий