T
T
Tysharela2022-01-18 07:05:01
Python
Tysharela, 2022-01-18 07:05:01

Python. Creating a voice assistant. Could you help with code optimization?

Since I am working with most libraries for the first time, I would like to hear the opinion of a more or less experienced user on optimization. Ideally, help with replacing the if structure
The code can be viewed on GitHub'e: https://github.com/Tysharela/MyAsisstant

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vindicar, 2022-01-18
@Vindicar

Well, firstly, your task is changing from writing a voice assistant for a fixed set of commands to writing a framework for creating voice assistants. This should be kept in mind, and the assistant should be designed in such a way that it would be more convenient for the programmer (you) to modify it.
Take Flask as an example, or many bot libs that use decorators to register commands. Then the script turns into a set of functions that implement the command logic itself, and the framework/library does the underlying basic things like communicating with the server.
In your case, these basic things would be:
1. The bot's work cycle
2. Detecting and recognizing the user's speech
3. Determining if the user is accessing the bot.
4. Optionally, the synthesis of a response to the user.
Thus, you can hide the subtleties of speech recognition from individual commands - let them work with text!
Conclusion: in the final bot, in addition to the basic things, there will be separate functions-commands. Each command will be marked with a decorator, which will set the text strings to which it should respond.
More or less like this:

@command('привет')  # на какую команду реагировать?
def hello(message: str) -> typing.Optional[str]:
  # параметр - полная текстовая строка, которую мы приняли
  return "и тебе привет" # возвращаемое значение - None, или строка, которую нужно произнести

This will allow you to easily add functionality to the bot, and not build long if-elif-else chains. Now we need to think about how best to implement it.
Since text strings can have variations, or variable parts (for example, what day do you need to know the weather for?), then you should use regular expressions rather than substring search . Then the bot function will look something like this.
@command(r'погода\s+город\s+(.+)')  # строка должна начинаться со слов "погода город", а всё что дальше - мы запоминаем.
def hello(match: re.Match) -> typing.Optional[str]:
  # параметр - результат сопоставления принятой строки с регуляркой
  city = match.group(1)
  weather = get_weather_for(city) # узнаём (как-то) погоду, город выбираем по запомненной строке
  # возвращаемое значение - None, или строка, которую нужно произнести
  return f"Погода в городе {city}: {weather}"

That's all well and good, but how do you implement it? Read about decorators. In short, they are simply functions that accept other functions as input.
An example (simplified) implementation of the command decorator:
import re
# список зарегистрированных через декоратор команд
registered_commands = []

def command(regexp: str):
  def decorator(command_func):
    global registered_commands
    reg = re.compile(regexp, re.I) # компилируем регулярку, чтобы потом она быстрее работала
    item = reg, command_func
    registered_commands.append(item) # добавляем регулярку и команду в список команд
    return command_func # декоратор должен вернуть функцию, которую обработал
  return decorator # возвращаем декоратор для использования

Essentially, the following two codes would then be equivalent:
@command('команда')
def some_func(match):
  pass
# это то же самое что и ниже
decorator = command('команда') # получили функцию-декоратор
# объявили декорируемую функцию
def some_func(match):
  pass
# применили декоратор к функции
some_func = decorator(some_func)

Thus, all the functions that we decorate with @commandwill be automatically collected in the registered_commads list. Then, when processing the next command, we will be able to sort through this list and find the one we need. And then it's simple. You make the work cycle of the bot (the code is abstract, but the idea must be conveyed).
while True:
  voice = record_user_voice() # детектируем и записываем слова пользователя
  text = speech_to_text(voice) # превращаем голос в текст
  if text.startswith('имя бота'): # пользователь должен сказать имя бота, чтобы мы напрасно не реагировали
    text = text[len('имя бота'):].strip() # убираем имя бота из текста
    for regexp, command in registered_commands:
      match = regexp.match(text) # пытаемся сопоставить текст с регуляркой
      if match is not None: # успешно?
        # да
        try:
          response = command(match) # пытаемся выполнить команду
        except Exception as err: # неудачно - сообщаем об ошибке
          print(err)
          response = "Возникла ошибка"
        if response: # если есть что сказать
          say(response) # проговариваем ответ
        break # так или иначе, мы нашли совпадение с командой. Дальше не ищем.
    else: # этот else относится к for ... in registered_commands и сработает если не было break
      say("Я не понял команду") # мы не нашли команду

Something like this.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question