Описание инструмента 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.
- Вывод таблицы с данными. Выводим на веб странице таблицу с с необходимыми нам данными.