Создаем программу искусственного интеллекта, которая воспринимает человеческую речь с Python и OpenAI
Введение
Создать программу искусственного интеллекта, которая воспринимает человеческую речь и отвечает на вопросы, не так сложно, как кажется. Такое чудо можно сотворить за один день с помощью нескольких пакетов 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.
Весь код хранится в этом репозитории.