Введение

Создать программу искусственного интеллекта, которая воспринимает человеческую речь и отвечает на вопросы, не так сложно, как кажется. Такое чудо можно сотворить за один день с помощью нескольких пакетов Python и API.

Вот ответы бота на некоторые вопросы.

Я: Какова численность населения Бразилии?

Бот: По оценкам, население Бразилии составляет более 209 миллионов человек.

Я: Какое на вкус мороженое?

Бот: Большинство видов мороженого на вкус сладкие и сливочные.

Я: Как пользоваться палочками для еды?

Бот: Чтобы использовать палочки для еды, нужно держать по одной в каждой руке. Поместите палочку для еды в доминирующую руку между большим и указательным пальцами и удерживайте ее средним пальцем. Поместите палочку для еды в недоминирующую руку между большим и указательным пальцами и удерживайте ее безымянным пальцем и мизинцем. Чтобы взять еду, используйте палочку для еды в доминирующей руке, чтобы удерживать еду, а затем используйте эту палочку для еды.

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

Что делает эта программа

  • Файл запускается через командную строку, когда пользователь готов задать вопрос.
  • PyAudio позволяет микрофону компьютера улавливать речевые данные.
  • Аудиоданные хранятся в переменной под названием stream, затем кодируются и преобразуются в JSON-данные.
  • JSON-данные поступают в API AssemblyAI для преобразования в текст, после чего текстовые данные отправляются обратно.
  • Текстовые данные поступают в API OpenAI, а затем направляются в движок text-davinci-002 для обработки.
  • Ответ на вопрос извлекается и отображается на консоли под заданным вопросом.

API и высокоуровневый дизайн

В этом руководстве используются два базовых API:

  • AssemblyAI для преобразования аудио в текст.
  • OpenAI для интерпретации вопроса и получения ответа.

Дизайн (высокий уровень)

Этот проект содержит два файла: main и openai_helper.

Скрипт main используется в основном для API-соединения “голос-текст”. Он включает в себя настройку сервера WebSockets, заполнение всех параметров, необходимых для PyAudio, и создание асинхронных функций, необходимых для одновременной отправки и получения речевых данных между приложением и сервером AssemblyAI.

openai_helper  —  файл с коротким именем, используемый исключительно для подключения к OpenAI-движку text-davinci-002. Это соединение обеспечивает получение ответов на вопросы.

Разбор кода

main.py

Сначала импортируем все библиотеки, которые будут использованы приложением. Для некоторых из них может потребоваться Pip-установка (в зависимости от того, использовали ли вы их). Обратите внимание на комментарии к коду ниже:

#PyAudio предоставляет привязку к Python для PortAudio v19, кроссплатформенной библиотеки ввода-вывода аудио. Позволяет микрофону компьютера взаимодействовать с Python
import pyaudio

#Библиотека Python для создания сервера Websocket - двустороннего интерактивного сеанса связи между браузером пользователя и сервером
import websockets

#asyncio - это библиотека для написания параллельного кода с использованием синтаксиса async/await
import asyncio

#Этот модуль предоставляет функции для кодирования двоичных данных в печатаемые символы ASCII и декодирования таких кодировок обратно в двоичные данные
import base64

#В Python есть встроенный пакет json, который можно использовать для работы с данными JSON
import json

#"Подтягивание" функции из другого файла
from openai_helper import ask_computer

Теперь устанавливаем параметры PyAudio. Эти параметры являются настройками по умолчанию, найденными в интернете. Вы можете поэкспериментировать с ними по мере необходимости, но мне параметры по умолчанию подошли отлично. Устанавливаем переменную stream в качестве начального контейнера для аудиоданных, а затем выводим параметры устройства ввода по умолчанию в виде словаря. Ключи словаря отражают поля данных в структуре PortAudio. Вот код:

#Настройка параметров микрофона
#Сколько байт данных приходится на каждый обработанный фрагмент звука
FRAMES_PER_BUFFER = 3200

#Битовый целочисленный формат аудиовхода/выхода порта по умолчанию
FORMAT = pyaudio.paInt16

#Моноформатный канал (то есть нам нужен только входной аудиосигнал, поступающий с одного направления)
CHANNELS = 1

#Желаемая частота в Гц входящего аудиосигнала
RATE = 16000

p = pyaudio.PyAudio()

#Начинает запись, создает переменную stream, присваивает параметры
stream = p.open(
    format=FORMAT,
    channels=CHANNELS,
    rate=RATE,
    input=True,
    frames_per_buffer=FRAMES_PER_BUFFER
)

print(p.get_default_input_device_info())

Далее создаем несколько асинхронных функций для отправки и получения данных, необходимых для преобразования голосовых вопросов в текст. Эти функции выполняются параллельно, что позволяет преобразовывать речевые данные в формат base64, конвертировать их в JSON, отправлять на сервер через API, а затем получать обратно в читаемом формате. Сервер WebSockets также является важной частью приведенного ниже скрипта, поскольку именно он делает прямой поток бесшовным.

#Необходимая нам конечная точка AssemblyAI
URL = "wss://api.assemblyai.com/v2/realtime/ws?sample_rate=16000"
auth_key = "enter key here"

#Создание асинхронной функции, чтобы она могла продолжать работать и отправлять поток речевых данных в API до тех пор, пока это необходимо
async def send_receive():
print(f'Connecting websocket to url ${URL}')
async with websockets.connect(
URL,
extra_headers=(("Authorization", auth_key),),
ping_interval=5,
ping_timeout=20
) as _ws:
await asyncio.sleep(0.1)
print("Receiving SessionBegins ...")
session_begins = await _ws.recv()
print(session_begins)
print("Sending messages ...")
async def send():
while True:
try:
data = stream.read(FRAMES_PER_BUFFER, exception_on_overflow=False)
data = base64.b64encode(data).decode("utf-8")
json_data = json.dumps({"audio_data":str(data)})
await _ws.send(json_data)
except websockets.exceptions.ConnectionClosedError as e:
print(e)
assert e.code == 4008
break
except Exception as e:
assert False, "Not a websocket 4008 error"
await asyncio.sleep(0.01)

return True

async def receive():
while True:
try:
result_str = await _ws.recv()
result = json.loads(result_str)
prompt = result['text']
if prompt and result['message_type'] == 'FinalTranscript':
print("Me:", prompt)
answer = ask_computer(prompt)
print("Bot", answer)
except websockets.exceptions.ConnectionClosedError as e:
print(e)
assert e.code == 4008
break
except Exception as e:
assert False, "Not a websocket 4008 error"

send_result, receive_result = await asyncio.gather(send(), receive())


asyncio.run(send_receive())

Теперь у нас есть простое API-соединение с OpenAI. Если вы посмотрите на строку 44 приведенного выше кода (main3.py), то увидите, что мы извлекаем функцию ask_computer из этого другого файла и используем ее результаты в качестве ответов на вопросы.

Заключение

Это отличный проект для всех, кто не прочь взять на вооружение ту самую технологию, благодаря которой функционируют Siri и Alexa. Его реализация не требует большого опыта в программировании, потому что для обработки данных используется API. 

Весь код хранится в этом репозитории.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Andrew Hershy: Build an Alexa- or Siri-Equivalent Bot in Python Using OpenAI

Предыдущая статьяРазработка макроса Rust для автоматического написания шаблонного кода SQL
Следующая статьяПравило 3-х часов: сколько времени в день отводить для работы