Описание инструмента STT Whisper → Анализ Openai
1. Выбор инструмента для преобразования речи в текст (STT).
Были протестированы следующие инструменты:
Плюсы: хорошее распознавание русской речи, высокая скорость
Минусы: платная, сложность настройки
Плюсы: неплохое распознавание русской речи
Минусы: платная, сложность оплаты(карты не РФ), сложность настройки
API направление не развивается, предоставляют доступ только в индивидуальном режиме.
AssemblyAI был протестирован на собственном сервере. Нет хороших моделей для распознавания русской речи.
На момент теста модель 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), т.к. для этой ОС всегда есть необходимые пакеты ПО.
Общая схема предполагалась следующей:
- Сервер АТС (asterisk) выдает файлы тел. диалогов в директорию на файловом сервере в формате *.wav .
- После чего сервер STT начинает его обработку:
В диалоге есть 2 канала - первый - это операторы ДЦ, второй - клиент. Поэтому мы решили сначала делить аудио файл на два и транскрибировать их отдельно с выводом в 2 отдельных текстовых файла с временными метками. После чего объединяем 2 файла в один.
Для обработки выбрали Python (v3.10) - библиотеки pydub (для обработки аудио) и faster_whisper (для транскрибации).
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.
- Устанавливаем TOR на сервер STT для обработки в ChatGPT, добавляем порт в “/etc/tor/torrc” и перезагружаем TOR “sudo systemctl restart tor”
HTTPTunnelPort 9081
- Для обработки в 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, поэтому систему решили настраивать на нём.
- Первый этап - транскрибация, в таблице MySQL делается отметка о загрузке файла и статус транскрибации изменяется на “process”. После транскрибации звуковой файл перемещается в папку “done”, а текстовый файл в папку “txt”
- Второй этап - система проверяет наличие сформированного текстового файла в папке “txt” и делает отметку в таблице о готовности транскрибации
- Третий этап - система смотрит задания с положительным статусом транскрибации и отправляет текстовые файлы с указанным нами промтом на анализ в ChatGPT. После чего получает ответный проанализированный текстовый файл и сохраняет его в папке “openai”, при этом делая в таблице отметку об отправке в ChatGPT. В случае ошибки система направляет уведомление в Телеграм.
- Четвертый этап - постобработка, например, выделение необходимого ответа из анализа ChatGPT.
- Вывод таблицы с данными. Выводим на веб странице таблицу с с необходимыми нам данными.