Администрирование
July 16

Описание инструмента STT Whisper → Анализ Openai

Оглавление:

1. Выбор инструмента для преобразования речи в текст (STT).

Были протестированы следующие инструменты:

  • [x] Yandex STT
  • [x] Google speech
  • [x] Silero STT
  • [x] AssemblyAI
  • [x] VOSK
  • [x] WHISPER

Короткие выводы:

Yandex STT проверена с помощью post запросов.

Плюсы: хорошее распознавание русской речи, высокая скорость

Минусы: платная, сложность настройки

Google speech была проверена в тестовом режиме.

Плюсы: неплохое распознавание русской речи

Минусы: платная, сложность оплаты(карты не РФ), сложность настройки

Silero STT на данное время существует только в виде бота.

API направление не развивается, предоставляют доступ только в индивидуальном режиме.

AssemblyAI был протестирован на собственном сервере. Нет хороших моделей для распознавания русской речи.

VOSK - тестировался на собственном сервере.

На момент теста модель vosk-model-ru-0.10 (2.5G)

Плюсы: высокая скорость обработки, среднее качество распознавания русской речи

Минусы: не было необходимых выходных форматов, среднее качество распознавания русской речи

В итоге остановились на Whisper. У него высокое качество распознавания в больших моделях LARGE и выше. Присутствуют необходимые выходные форматы с временными метками. Но скорость обработки очень низкая при использовании CPU.

Нашли более быстрый форк Faster-whisper:

GitHub - SYSTRAN/faster-whisper: Faster Whisper transcription with CTranslate2

Скорость обработки значительно возросла при сохранении качества.

2. Настройка обработки звуковых файлов - телефонных диалогов.

В качестве ОС решили использовать последний LTS Ubuntu Server (на тот момент 22.04), т.к. для этой ОС всегда есть необходимые пакеты ПО.

Общая схема предполагалась следующей:

  1. Сервер АТС (asterisk) выдает файлы тел. диалогов в директорию на файловом сервере в формате *.wav .
  2. После чего сервер STT начинает его обработку:

В диалоге есть 2 канала - первый - это операторы ДЦ, второй - клиент. Поэтому мы решили сначала делить аудио файл на два и транскрибировать их отдельно с выводом в 2 отдельных текстовых файла с временными метками. После чего объединяем 2 файла в один.

Для обработки выбрали Python (v3.10) - библиотеки pydub (для обработки аудио) и faster_whisper (для транскрибации).

Python скрипт:

from pydub import AudioSegment
from faster_whisper import WhisperModel
import sys
import os

input_name = sys.argv[1]

# Разделение аудиофайла на каналы
audio = AudioSegment.from_wav("/mnt/media/OP/" + input_name +  ".wav")

# Извлечение каналов
left_channel = audio.split_to_mono()[0]
right_channel = audio.split_to_mono()[1]

# Сохранение отдельных каналов во временные файлы
left_channel.export("" + input_name +  "left_channel.wav", format="wav")
right_channel.export("" + input_name +  "right_channel.wav", format="wav")

# Инициализация модели
model_size = "large-v3"
model = WhisperModel(model_size, device="cpu", compute_type="int8")

def transcribe_and_label(channel_file, channel_label):
    segments, _ = model.transcribe(
        channel_file,
        beam_size=5,
        language="ru",
        condition_on_previous_text=False,
        vad_filter=True,
        vad_parameters=dict(min_silence_duration_ms=500),
    )
    labeled_segments = [
        {"start": segment.start, "end": segment.end, "text": segment.text, "channel": channel_label}
        for segment in segments
    ]
    return labeled_segments

# Транскрипция каналов с маркировкой
left_segments = transcribe_and_label("" + input_name +  "left_channel.wav", "Клиент")
right_segments = transcribe_and_label("" + input_name +  "right_channel.wav", "Оператор")

# Объединение и сортировка сегментов по времени начала
all_segments = left_segments + right_segments
all_segments.sort(key=lambda x: x["start"])

# Запись в файл
with open("/mnt/media/OP/" + input_name +  ".txt", "w", encoding="utf-8") as f:
    for segment in all_segments:
        segment_text = "[%.2fs -> %.2fs] (%s channel) %s" % (segment["start"], segment["end"], segment["channel"], segment["text"])
        f.write(segment_text + "\\n")
        print(segment_text)

# Удаление временных файлов (если необходимо)
os.remove("" + input_name +  "left_channel.wav")
os.remove("" + input_name +  "right_channel.wav")

# Функция для удаления временных меток и замены текста
def clean_transcription_file(input_file, output_file):
    with open(input_file, "r", encoding="utf-8") as f:
        lines = f.readlines()

    with open(output_file, "w", encoding="utf-8") as f:
        for line in lines:
            # Удаление временных меток
            cleaned_line = line.split('] ')[1] if '] ' in line else line
            # Замена текста "(Оператор channel)" на "Оператор:"
            cleaned_line = cleaned_line.replace("(Клиент channel)", "Клиент:").replace("(Оператор channel)", "Оператор:")
            f.write(cleaned_line)

# Использование функции для очистки файла
clean_transcription_file("/mnt/media/OP/" + input_name +  ".txt", "/mnt/media/OP/txt/" + input_name +  ".txt")
os.rename("/mnt/media/OP/" + input_name +  ".txt", "/mnt/media/OP/txt_temp/" + input_name +  ".txt")

os.rename("/mnt/media/OP/" + input_name +  ".wav", "/mnt/media/OP/done/" + input_name +  ".wav")

3. Анализ диалога с помощью ИИ.

Далее была поставлена задача по анализированию диалога с помощью ИИ. Мы выбрали для данного решения ChatGPT, как наиболее развитую ИИ технологию. На тот момент использовали модель gpt-4o.

Возникла проблема в доступности API ChatGPT из РФ. Для решения данной проблемы мы использовали собственный прокси сервер в виде локального TOR.

  1. Устанавливаем TOR на сервер STT для обработки в ChatGPT, добавляем порт в “/etc/tor/torrc” и перезагружаем TOR “sudo systemctl restart tor”
HTTPTunnelPort 9081
  1. Для обработки в ChatGPT мы так же решили использовать Python - библиотеки requests и json, напрямую библиотеку openai использовать не получилось из-за обхода ограничений.
import requests
import sys
import os
import json

# Добавляем чтение аргументов из cmd
input_name = sys.argv[1]

# Your OpenAI API key
api_key = "sk-****-*********************************************"

# Пути к вх. и исх. файлам
file_path = f"/mnt/media/OP/txt/{input_name}.txt"
output_file_path = f"/mnt/media/OP/openai/{input_name}.txt"
prompt = "Прошу проверить разговор продавца с клиентом, используя следующий чек-лист-\\nВопрос 1- Продавец поздоровался?\\nВопрос 2- Продавец представился?\\nВопрос 3- Продавец в течении разговора обратился к клиенту по имени не менее 2 раз?\\nВопрос 4- Продавец назвал первичные потребности клиента?\\nВопрос 5- Продавец задавал открытые вопросы, направленные на выявление потребностей клиента в автомобиле?\\nВопрос 6- Продавец предложил клиенту альтернативные варианты автомобилей?\\nВопрос 7- Продавец согласовал с клиентом дату и время следующей встречи или звонка?\\nВопрос 8- Продавец в конце разговора подтвердил номер телефона клиента?\\nВопрос 9- Продавец подвел итоги диалога?\\nВопрос 10- Продавец спросил, есть ли у клиента еще какие-либо вопросы?\\nВопрос 11- Продавец в конце разговора предложил свою электронную визитку или номер телефона?\\nВопрос 12- Продавец поблагодарил клиента за звонок?\\nВопрос 13- Продавец вежливо попрощался?\\nВопрос 14- Какая договоренность была озвучена в итоге разговора?\\nВопрос 15- Продавец проявлял инициативность и заинтересованность в разговоре с клиентом? Сделай оценку так - каждый вопрос - это 1 балл и рассчитай долю положительных ответов."
# Конф. Proxy 
proxies = {
    "http": "<http://localhost:9081>",
    "https": "<http://localhost:9081>",
}

# Читаем содержимое файла
with open(file_path, 'r', encoding='utf-8') as file:
    file_content = file.read()

# Объединяем текст промта и содержимое текст. файла
full_prompt = prompt + file_content

# JSON payload для API запроса
payload = {
    "model": "gpt-4o",
    "messages": [
        {"role": "system", "content": "You are an assistant."},
        {"role": "user", "content": full_prompt}
    ],
    "max_tokens": 1500,
    "temperature": 0.7
}

# Отправляем запрос в OpenAI через прокси
response = requests.post(
    "<https://api.openai.com/v1/chat/completions>",
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
    },
    json=payload,
    proxies=proxies
)

# Проверяем доступность
if response.status_code == 200:
    response_json = response.json()
    response_text = response_json['choices'][0]['message']['content'].strip()

    # Write the response to the output file
    with open(output_file_path, 'w', encoding='utf-8') as output_file:
        output_file.write(response_text)

    print(f"Ответ записан в файл {output_file_path}")
else:
    print(f"Ошибка: {response.status_code}")
    print(response.text)

4. Фронт- и бэкенд для контроля системы

У нас уже был в компании веб сервер, работающий на NGINX+PHP+MySQL, поэтому систему решили настраивать на нём.

  1. Первый этап - транскрибация, в таблице MySQL делается отметка о загрузке файла и статус транскрибации изменяется на “process”. После транскрибации звуковой файл перемещается в папку “done”, а текстовый файл в папку “txt”
  2. Второй этап - система проверяет наличие сформированного текстового файла в папке “txt” и делает отметку в таблице о готовности транскрибации
  3. Третий этап - система смотрит задания с положительным статусом транскрибации и отправляет текстовые файлы с указанным нами промтом на анализ в ChatGPT. После чего получает ответный проанализированный текстовый файл и сохраняет его в папке “openai”, при этом делая в таблице отметку об отправке в ChatGPT. В случае ошибки система направляет уведомление в Телеграм.
  4. Четвертый этап - постобработка, например, выделение необходимого ответа из анализа ChatGPT.
  5. Вывод таблицы с данными. Выводим на веб странице таблицу с с необходимыми нам данными.