Как стать автором
Обновить

Голосовой ассистент на Python

Уровень сложностиСредний

В последние годы голосовые ассистенты стали неотъемлемой частью нашей жизни. Они помогают нам управлять своими устройствами, находить информацию и взаимодействовать с различными устройствами всего лишь с помощью голоса. В этой статье мы рассмотрим, как создать собственного голосового ассистента на языке программирования Python.

Голосового ассистента с базовым функционалом можно написать на одном лишь Python, этот язык предлагает простой синтаксис и большое количество библиотек

pip install psutil SpeechRecognition pyttsx3 g4f pycaw
import os
import psutil
import subprocess
import speech_recognition as sr
import pyttsx3
import g4f
import webbrowser
from pycaw.pycaw import AudioUtilities, ISimpleAudioVolume
from pathlib import Path

Кратко расскажу для чего нужна каждая из библиотек

os

Используется для работы с операционной системой

psutil

Поиск и завершение процессов приложений по имени

subprocess

Используется для открытия приложений

speech_recognition

Обеспечивает распознавание речи из аудио

pyttsx3

Преобразование текста в звук

g4f

Подключение Chat GPT 4 без API

webbrowser

Открывает браузер для просмотра страниц

pycaw

Управление звуком в Windows

pathlib

Упрощение управления путями

Далее нужно создать голосовой "движок" для преобразования текста в речь и озвучивание этого текста

engine = pyttsx3.init()

def speak(text):
    engine.say(text)
    engine.runAndWait() 

Что делает код

  • engine = pyttsx3.init() – это создание голосового "движка". Он и будет отвечать за преобразование текста в речь

  • def speak(text): – это определение функции speak, которая принимает текст (например, команду или ответ) и проговаривает его

  • Внутри speak:

    • engine.say(text) – передаёт текст в "движок" для озвучивания

    • engine.runAndWait() – запускает озвучивание и ждёт его завершения, чтобы не прерывать речь

Затем мы приступаем к основным функция ассистента, начиная с функции закрытия приложений

def close_process(process_name):
    found = False
    for proc in psutil.process_iter(['pid', 'name']):
        if proc.info['name'].lower() == process_name.lower():
            proc.kill()
            found = True
    if found:
        speak(f"Процессы {process_name} были закрыты.")
    else:
        speak(f"Процесс {process_name} не найден.")
  1. Что делает код

  • Использует psutil.process_iter(['pid', 'name']) для перебора всех процессов, запущенных в системе, и получает только их pid (идентификатор процесса) и name

  • Сравнивает имя процесса с process_name (название процесса, который хотим закрыть), игнорируя регистр

  • Если процесс найден, proc.kill() завершает его

  • Если хотя бы один процесс с этим именем найден и завершён, программа сообщает, что процессы были закрыты; если нет, сообщает, что таких процессов не обнаружено

    1. Зачем нужен found

  • Переменная found отслеживает, был ли найден и завершён хотя бы один процесс. Это позволяет ассистенту ответить, найдены ли процессы или нет

Функция открытия приложений

# Открытие приложений с поиском по всей файловой системе
def open_application(app_name):
    app_path = None
    search_paths = [Path("C:\\"), Path("D:\\"), Path("E:\\")]

    for path in search_paths:
        for root, dirs, files in os.walk(path):
            dirs[:] = [d for d in dirs if not d.startswith('$') and not d.startswith('.')]
            for file in files:
                if file.lower() == app_name.lower() + ".exe":
                    app_path = os.path.join(root, file)
                    break
            if app_path:
                break
        if app_path:
            break

    if app_path:
        subprocess.Popen([app_path], cwd=os.path.dirname(app_path))
        speak(f"{app_name} открыт.")
    else:
        speak(f"Приложение {app_name} не найдено.")
  1. Что делает код

  • search_paths задаёт диски для поиска

  • os.walk проходит по всем папкам и файлам на каждом диске

  • dirs[:] = [d for d in dirs if not d.startswith('$') and not d.startswith('.')] исключает скрытые и системные папки из поиска, что ускоряет его

  • Сравнивает имя файла с app_name, добавляя .exe

  • Как только файл найден, subprocess.Popen запускает его

2. Почему нужны break

  • break прерывает поиск на том уровне, где найден файл, чтобы ускорить выполнение

Функция поиска в браузере

def search_in_browser(query):
    url = f"https://yandex.ru/search/?text={query}"
    try:
        webbrowser.open(url)
        speak(f"Ищу '{query}' в Яндексе.")
    except Exception as e:
        speak(f"Не удалось открыть браузер для поиска. Ошибка: {e}")
        print({e})

Что делает код

  • Формирует URL для Яндекс-поиска с query

  • Открывает URL через webbrowser.open

  • Если открытие не удалось, ассистент сообщает об ошибке

Функция Chat GPT 4

def gpt(com):
        if not com.strip():
            return

        try:
            answer = g4f.ChatCompletion.create(
                model="gpt-4",
                messages=[{"role": "user", "content": com}],
                stream=True,
            )

            full_response = ""
            for part in answer:
                speak(f"Ответ: {part}")
                if isinstance(part, dict) and 'choices' in part and 'delta' in part['choices'][0]:
                    content = part['choices'][0]['delta'].get('content', '')
                    if content:  # Проверяем, что контент не пустой
                        full_response += content
                        print(f"Накопленный ответ: {full_response}")

            if full_response:
                speak(f"Ответ: {full_response}")

        except Exception as e:
            speak("Произошла ошибка при обращении к GPT. Попробуйте снова.")

Что делает код

  • Проверяет, является ли command пустой строкой после удаления пробелов. Если command пустая, функция завершает работу и ничего не выполняет дальше

  • Отправляет текст команды command в модель GPT-4

Функции включения и выключения звука

def mute_sound():
    sessions = AudioUtilities.GetAllSessions()
    for session in sessions:
        volume = session._ctl.QueryInterface(ISimpleAudioVolume)
        volume.SetMasterVolume(0.0, None)
    speak("Звук выключен.")

def unmute_sound():
    sessions = AudioUtilities.GetAllSessions()
    for session in sessions:
        volume = session._ctl.QueryInterface(ISimpleAudioVolume)
        volume.SetMasterVolume(1.0, None)
    speak("Звук включен.")

Что делает код

  • AudioUtilities.GetAllSessions() получает все аудиосессии (запущенные приложения со звуком)

  • volume.SetMasterVolume(0.0, None) устанавливает громкость в 0.0, чтобы отключить звук

  • SetMasterVolume(1.0, None) устанавливает громкость на 1.0 (максимум), чтобы включить звук

  • После каждого действия ассистент сообщает об изменении громкости

Спящий режим

def sleep_mode():
    speak("Ассистент переходит в спящий режим. Скажите 'проснись' для выхода.")
    recognizer = sr.Recognizer()
    with sr.Microphone() as source:
        while True:
            print("Спящий режим: ожидание команды...")
            try:
                audio = recognizer.listen(source, timeout=1, phrase_time_limit=5)
                command = recognizer.recognize_google(audio, language='ru-RU').lower()
                if 'проснись' in command:
                    speak("Ассистент пробудился.")
                    break
            except sr.UnknownValueError:
                continue
            except sr.WaitTimeoutError:
                continue

Что делает код

  • Постоянно слушает микрофон

  • timeout=1 и phrase_time_limit=5 ограничивают время ожидания

  • Если ассистент услышит "проснись", он выходит из цикла и пробуждается

Функция для распознавания команд

def main():
    speak("Ассистент активирован. Чем я могу помочь?")
    while True:
        command = recognize_command()

        if "закрой" in command:
            app_name = command.replace("закрой", "").strip()
            close_process(str(app_name) + ".exe")
        elif "открой" in command:
            app_name = command.replace("открой", "").strip()
            open_application(app_name)
        elif "выключить звук" in command:
            mute_sound()
        elif "включить звук" in command:
            unmute_sound()
        elif 'поиск' in command:
            query = command.replace('поиск', '').strip()
            if query:
                search_in_browser(query)
            else:
                speak("Не указано, что искать.")
        elif 'спящий режим' in command:
            sleep_mode()
        elif 'стоп' in command:
            speak("Ассистент завершает работу.")
            break
        elif "выключение" in command:
            speak("Вы уверены, что хотите выключить компьютер?")
            of = recognize_command()
            if of == "да":
                shutdown_computer()
        else:
            gpt(command)
  

Что делает код

  • Создаёт recognizer для распознавания речи и захватывает звук через микрофон

  • Использует recognizer.recognize_google для преобразования звука в текст

  • Если текст не распознан (sr.UnknownValueError), ассистент сообщает, что команда не распознана

Основной цикл программы

def main():
    speak("Ассистент активирован. Чем я могу помочь?")
    while True:
        command = recognize_command()

        if "закрой" in command:
            app_name = command.replace("закрой", "").strip()
            close_process(str(app_name) + ".exe")
        elif "открой" in command:
            app_name = command.replace("открой", "").strip()
            open_application(app_name)
        elif "выключить звук" in command:
            mute_sound()
        elif "включить звук" in command:
            unmute_sound()
        elif 'поиск' in command:
            query = command.replace('поиск', '').strip()
            if query:
                search_in_browser(query)
            else:
                speak("Не указано, что искать.")
        elif 'спящий режим' in command:
            sleep_mode()
        elif 'стоп' in command:
            speak("Ассистент завершает работу.")
            break
        elif "выключение" in command:
            speak("Вы уверены, что хотите выключить компьютер?")
            of = recognize_command()
            if of == "да":
                shutdown_computer()
        else:
            gpt(command)
if __name__ == "__main__":
    main()

Что делает код

  • Использует микрофон для получения переменной command

  • Проверяет что сказал пользователь и выполняет задачу

  • Если пользователь произнес что-то другое, то передает этот запрос Chat GPT

Полный код

import os
import psutil
import subprocess
import speech_recognition as sr
import pyttsx3
import g4f
import webbrowser
from pycaw.pycaw import AudioUtilities, ISimpleAudioVolume
from pathlib import Path

def close_process(process_name):
    found = False
    for proc in psutil.process_iter(['pid', 'name']):
        if proc.info['name'].lower() == process_name.lower():
            proc.kill()
            found = True
    if found:
        speak(f"Процессы {process_name} были закрыты.")
    else:
        speak(f"Процесс {process_name} не найден.")

def open_application(app_name):
    app_path = None
    search_paths = [Path("C:\\"), Path("D:\\"), Path("E:\\")]

    for path in search_paths:
        for root, dirs, files in os.walk(path):
            dirs[:] = [d for d in dirs if not d.startswith('$') and not d.startswith('.')]
            for file in files:
                if file.lower() == app_name.lower() + ".exe":
                    app_path = os.path.join(root, file)
                    break
            if app_path:
                break
        if app_path:
            break

    if app_path:
        subprocess.Popen([app_path], cwd=os.path.dirname(app_path))
        speak(f"{app_name} открыт.")
    else:
        speak(f"Приложение {app_name} не найдено.")

def search_in_browser(query):
    url = f"https://yandex.ru/search/?text={query}"
    try:
        webbrowser.open(url)
        speak(f"Ищу '{query}' в Яндексе.")
    except Exception as e:
        speak(f"Не удалось открыть браузер для поиска. Ошибка: {e}")
        print({e})

def mute_sound():
    sessions = AudioUtilities.GetAllSessions()
    for session in sessions:
        volume = session._ctl.QueryInterface(ISimpleAudioVolume)
        volume.SetMasterVolume(0.0, None)
    speak("Звук выключен.")

def unmute_sound():
    sessions = AudioUtilities.GetAllSessions()
    for session in sessions:
        volume = session._ctl.QueryInterface(ISimpleAudioVolume)
        volume.SetMasterVolume(1.0, None)
    speak("Звук включен.")

def sleep_mode():
    speak("Ассистент уходит в спящий режим. Скажите 'проснись' для выхода.")
    recognizer = sr.Recognizer()
    with sr.Microphone() as source:
        while True:
            print("Спящий режим: ожидание команды...")
            try:
                audio = recognizer.listen(source, timeout=1, phrase_time_limit=5)
                command = recognizer.recognize_google(audio, language='ru-RU').lower()
                if 'проснись' in command:
                    speak("Ассистент пробудился.")
                    break
            except sr.UnknownValueError:
                continue
            except sr.WaitTimeoutError:
                continue

def gpt(com):
        if not com.strip():
            return

        try:
            answer = g4f.ChatCompletion.create(
                model="gpt-4",
                messages=[{"role": "user", "content": com}],
                stream=True,
            )

            full_response = ""
            for part in answer:
                speak(f"Ответ: {part}")
                if isinstance(part, dict) and 'choices' in part and 'delta' in part['choices'][0]:
                    content = part['choices'][0]['delta'].get('content', '')
                    if content:  # Проверяем, что контент не пустой
                        full_response += content
                        print(f"Накопленный ответ: {full_response}")

            if full_response:
                speak(f"Ответ: {full_response}")

        except Exception as e:
            speak("Произошла ошибка при обращении к GPT. Попробуйте снова.")
                
def recognize_command():
    recognizer = sr.Recognizer()
    with sr.Microphone() as source:
        print("Слушаю...")
        audio = recognizer.listen(source)
        try:
            command = recognizer.recognize_google(audio, language='ru-RU')
            print("Вы сказали:", command)
            return command.lower()
        except sr.UnknownValueError:
            speak("Не удалось распознать команду.")
            return ""
        except sr.RequestError:
            speak("Ошибка соединения с сервисом распознавания.")
            return ""

def main():
    speak("Ассистент активирован. Чем я могу помочь?")
    while True:
        command = recognize_command()

        if "закрой" in command:
            app_name = command.replace("закрой", "").strip()
            close_process(str(app_name) + ".exe")
        elif "открой" in command:
            app_name = command.replace("открой", "").strip()
            open_application(app_name)
        elif "выключить звук" in command:
            mute_sound()
        elif "включить звук" in command:
            unmute_sound()
        elif 'поиск' in command:
            query = command.replace('поиск', '').strip()
            if query:
                search_in_browser(query)
            else:
                speak("Не указано, что искать.")
        elif 'спящий режим' in command:
            sleep_mode()
        elif 'стоп' in command:
            speak("Ассистент завершает работу.")
            break
        elif "выключение" in command:
            speak("Вы уверены, что хотите выключить компьютер?")
            of = recognize_command()
            if of == "да":
                shutdown_computer()
        else:
            gpt(command)
            
if __name__ == "__main__":
    main()

Хочу поблагодарить всех, кто следил за процессом разработки, и призвать вас к обратной связи. Надеюсь эта статья была для вас полезна.

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.