Голосовая поддержка в XML: Часть 4. Разрабатываем приложение для голосового поиска в Web с помощью VoiceXML (исходники)

Мартин Браун

Введение

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

Подобного рода информацию можно легко представлять в VoiceXML для выполнения голосовых запросов и прослушивания результатов поиска. В данной статье мы создадим приложение для подобного голосового поиска. Кроме того, мы рассмотрим следующие вопросы:

  • Базовый процесс поиска информации в Web
  • Создание обобщенного класса для генерирования элементов VXML-форм
  • Создание грамматических правил VXML, описывающих различные варианты пользовательского ввода
  • Использование поискового интерфейса Yahoo
  • Выполнение Web-запросов с использованием VXML и поискового сервиса Yahoo
  • Выполнение запросов к сервису Yahoo!Local с использованием VXML

Об этой серии

Аудио и, в частности, голосовые сервисы становятся все более популярны в Интернет. В качестве примеров могут служить всевозможные музыкальные ресурсы, а также Web-трансляции, доступные в онлайн. Статьи нашей серии рассказывают о способах совмещения голосовых технологий и XML при разработке таких приложений, как:

  • В первой части: RSS-reader с голосовой поддержкой.
  • Во второй части: календаря с голосовой поддержкой.
  • В третьей части: голосовой программы для работы с блогами и Twitter.
  • В четвертой части: программы для голосового поиска в Yahoo.

Процесс поиска в Web

Поисковый интерфейс включает в себя простое меню, предлагающее выбрать один из двух вариантов поиска : поиска в Web или в справочнике. В последнем случае от пользователя требуется задать не только ключевые слова, но и местоположение, для которого будет осуществляться поиск. В целом последовательность действий представлена на рисунке 1.

Рисунок 1. Алгоритм приложения для поиска в Web
web searching workflow

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

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

Описание грамматических правил ввода

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

Как правило, если приложение ожидает от пользователя ввода какой-либо конкретной фразы, например, "I am sad", то ее можно явно описать в грамматике: (eye am sad).

Для поиска в Web необходима большая гибкость при распознавании пользовательского ввода. Воспринимать произвольный текст приложение по-прежнему не сможет, но будет задавать список ключевых слов для поиска таким образом, что пользователь сможет их повторять в произвольном количестве.

Для этого достаточно объединить список слов в группу, а затем создать правило, распознающее одно или более слово из группы. Например, допустим, надо распознавать слова "java", "apple" или "windows" в произвольном порядке и количестве. Для этого создается следующее правило: Terms [ (java) (apple) (windows) ].

Для распознавания произвольных комбинаций этих слов нам необходимо следующее правило: .Phrase +Terms.

Таким образом, пользователь может вводить любое слово группы, а также повторять его произвольное количество раз в любых комбинациях с другими словами из той же группы. Например, будут корректно распознаваться фразы "java apple" и "java apple windows".

Разумеется, само по себе подобное правило бесполезно, т.к. то же самое можно сделать и другими способами. Однако в него можно добавить другие ключевые слова, отвечающие за логическое связывание и позволяющие лучше контролировать корректность ввода. Например, можно преобразовать наше правило в следующий вид: Terms [ (java) (apple) (windows) (and) (or) ].

Теперь система сможет распознавать такие комбинации, как "java and apple", "java and windows" или даже "java and apple or windows".

Далее, используя подобные правила, мы создадим систему, способную распознавать практически произвольные варианты ввода. Кроме самих правил нам еще понадобится стандартный класс для генерирования фрагментов VXML.

Создание класса, отвечающего за генерирование VXML

Мы создадим обобщенный класс, который будет отвечать за генерирование как отдельных элементов, так и целых фрагментов VXML в соответствии с заранее определенным форматом. Этот класс существенно упростит формирование VXML в нашем приложении, а в сочетании с гибкими грамматическими правилами позволит легко воспринимать и обрабатывать пользовательский ввод.

В будущем вы сможете адаптировать класс WebSearchVXML для многих других VXML-приложений. Это будет несложно сделать, благодаря простым методам, предоставляемых классом для генерирования VXML-фрагментов, необходимых для того или иного приложения. В базовой версии класса сгенерированные фрагменты записываются в стандартный поток вывода (с помощью метода System.out.println()), но это поведение можно легко приспособить под нужды, например, Web-приложения, которому требуется создавать динамические элементы VoiceXML.

Класс предоставляет следующую информацию и методы:

  • Список поддерживаемых слов для поиска в Web
  • Аналогичный список для поиска компаний в Yahoo!Local
  • Список мест для поиска компаний, в частности, городов
  • Метод VXMLPrompt, формирующий блок '<prompt>сообщение</prompt>'
  • Метод VXMLTerms, формирующий блок "field" с вложенным блоком "prompt", а также грамматическим правилом, описывающим набор слов для поиска
  • Метод VXMLOptions, формирующий аналогичный блок "field" с вложенным "prompt", только вместо грамматики используется список элементов "options". Этот метод полезен, когда пользователь должен выбрать один из возможных вариантов, например, выбрать город из списка
  • Метод VXMLHeader, формирующий стандартный заголовок VXML-документа
  • Аналогичный метод VXMLFooter для вывода заключительной секции VXML-документа

Полный код класса приведен в листинге 1.

Листинг 1. Класс для формирования VXML-вывода

                

public class WebSearchVXML {

    String webterms[] = { "java",
                          "apple",
                          "windows",
                          "britney spears",
                          "lindsey lohan"};

    String localterms[] = { "plumber",
                            "restaurant",
                            "electrician",
                            "supermarket"};

    String cities[] = { "Лондон",
                        "Ноттингем",
                        "Манчестер",
                        "Йорк",};

    PrintStream out = System.out;

    public void WebSearchVXML() {
    }

    private void OutputGSLTerms(String[] terms) {

        this.out.println("<grammar type=\"text/gsl\">" +
                           "<![CDATA[\n" +
                           ".Phrase +Terms\n" +
                           "Terms [ ");

        for(int i=0;i<terms.length;i++) {
            this.out.println("(" + terms[i] + ")");
        }

        this.out.println("]\n" +
                           "]]>" +
                           "</grammar>");
    }

    private void OutputOptionTerms(String[] terms) {
        for(int i=0;i<terms.length;i++) {
            this.out.println("<option>" + terms[i] + "</option>");
        }
    }

    public void VXMLPrompt(String prompt) {
        this.out.println("<prompt>" + prompt + "</prompt>");
    }

    public void VXMLTerms(String fieldname,
                          String prompt,
                          String [] terms) {
        this.out.println("<field name=\"" + fieldname + "\">");
        VXMLPrompt(prompt);
        OutputGSLTerms(terms);
        this.out.println("</field>");
    }

    public void VXMLOptions(String fieldname,
                          String prompt,
                          String [] terms) {
        this.out.println("<field name=\"" + fieldname + "\">");
        VXMLPrompt(prompt);
        OutputOptionTerms(terms);
        this.out.println("</field>");
    }

    public void VXMLHeader() {
        this.out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        this.out.println("<vxml version=\"2.1\">");
    }

    public void VXMLFooter() {
        this.out.println("</vxml>");
    }
}

Используя данный класс, вы легко можете формировать VXML-формы для получения голосовой информации от пользователя.

Использование класса для формирования голосовых интерфейсов

Одним из вариантов использования данного класса может быть генерирование VXML-документа для ввода параметров при поиске по местонахождению. Пример показан в листинге 2.

Листинг 2. Формирование простой VXML-формы

                
WebSearchVXML search = new WebSearchVXML();

search.VXMLHeader();
System.out.println("<form>");
System.out.println("<block>");
search.VXMLPrompt("Вас приветствует сервис для поиска в Интернет");
System.out.println("</block>");
search.VXMLTerms("phrase",
    "Введите фразу для поиска в Web по ключевым словам",
    search.webterms);
search.VXMLOptions("location",
    "Выберите город для ограничения результатов поиска",
    search.cities);
System.out.println("<filled><submit name=\"/VXMLSearch/search\" 
namelist=\"phrase location\">");
System.out.println("</form>");
search.VXMLFooter();

Сгенерированный документ VXML приведен в листинге 3.

Листинг 3: Документ VXML

                
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block>
<prompt>Вас приветствует сервис для поиска в Интернет</prompt>
</block>
<field name="phrase">
<prompt>Введите фразу для поиска в Web по ключевым словам</prompt>
<grammar type="text/gsl"><![CDATA[
.Phrase +Terms
Terms [ 
(java)
(apple)
(windows)
(britney spears)
(lindsey lohan)
]
]]></grammar>
</field>
<field name="location">
<prompt>Выберите город для ограничения результатов поиска</prompt>
<option>Лондон</option>
<option>Ноттингем</option>
<option>Манчестер</option>
<option>Йорк</option>
</field>
<filled><submit name="/VXMLSearch/search" namelist="phrase location">
</form>
</vxml>

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

Конечно списки для выбора ключевых слов - это не совсем произвольный текст, но, тем не менее, добавив достаточное количество терминов, можно поддерживать большое количество вариантов для поиска.

Использование поискового интерфейса Yahoo

Используя поисковый интерфейс Yahoo, можно осуществлять поиск информации в практически всех базах данных Yahoo. Этот интерфейс предоставляет удобные методы для формирования различных параметров поиска, а также получать подробные результаты.

Перед тем как начать использовать поисковую систему Yahoo, необходимо зарегистрировать специальный API-ключ. После этого можно скачать SDK для поиска в Yahoo, представляющий собой библиотеку Java™ в виде файла JAR, который содержит все, что необходимо для взаимодействия с поисковым интерфейсом Yahoo.

JAR-файл Yahoo необходимо включить в classpath при компиляции программы, использующей этот API: $ javac -cp yahoo_search-2.0.2.jar LocalSearch.java.

Теперь можно написать простую поисковую функцию, принимающую на вход параметр поиска и генерирующую VXML-фрагмент, перечисляющий найденные результаты.

Поиск в Web

Для запуска поиска в Web необходимо создать экземпляр класса SearchClient(), используя ключ API, полученный при регистрации в Yahoo. Созданный таким образом клиентский объект будет отвечать за выполнение поиска, причем любого типа.

Для выполнения запроса к поисковому сервису необходимо создать объект класса WebSearchRequest() и передать ему строку, задающую параметры поиска.

В ответ на клиентский запрос к поисковому сервису, Yahoo вернет объект типа WebSearchResults, содержащий как обобщенную информацию о самом запросе, так и массив результатов. Каждый результат представляет собой объект, содержащий заголовок страницы, URL и другую информацию, в большинстве своем бесполезную для VoiceXML-приложения. Полностью последовательность действий показана в листинге 4.

Листинг 4. Поиск в Web с помощью программного интерфейса Yahoo

                
public static void DoSearch(String term) {
        SearchClient client = new SearchClient("INSERTKEY");

    WebSearchRequest request = new WebSearchRequest(term);

    try {
        WebSearchResults results = client.webSearch(request);

        System.out.println("<block><prompt>Найдено " +
                           results.getTotalResultsAvailable() +
                           " совпадений для фразы " +
                           term +
                           ". Первые " +
                           results.getTotalResultsReturned() +
                           " результатов таковы :</prompt></block>");

        for (int i = 0; i < results.listResults().length; i++) {
            WebSearchResult result = results.listResults()[i];

            System.out.println("<block><prompt>" +
                               result.getTitle() +
                               ".<break/>" +
                               "</prompt></block>");
        }
    }
    catch (Exception e) {
        System.err.println("An error occurred");
    }
}

Код в листинге 4 выводит один блок "prompt", содержащий обобщенные результаты поиска, например, общее число совпадений, а также один блок на каждую страницу, найденную в базе данных Yahoo для поиска в Web.

Фрагмент VXML, содержащий результаты поиска, показан в листинге 5.

Листинг 5. Результаты поиска в VXML

                
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block><prompt>
Найдено 73600000 совпадений для фразы "britney spears". Первые 10 результатов таковы:
 </prompt></block>
<block><prompt>Britney Spears - The Official
 Site.<break/></prompt></block>
<block><prompt>WORLDOFBRITNEY.COM.<break/>
                                            </prompt></block>
<block><prompt>Britney Spears - britney.com - Jive 
Records.<break/></prompt></block>
<block><prompt>Britney Spears Zone / Britney Divorce, Pregnant, 
Baby Pictures, Pics & 
News.<break/></prompt></block>
<block><prompt>britneyspears.org.<break/></prompt></block>
<block><prompt>WoBPictures.com [100030013].<break/>
                                           </prompt></block>
<block><prompt>Britney Spears - Wikipedia, the free
 encyclopedia.<break/></prompt></block>
<block><prompt>Britney Spears / Music Artist / Videos, News, Photos & 
Ringtones / MTV.<break/></prompt></block>
<block><prompt>Britney Spears.<break/></prompt></block>
<block><prompt>Britney Spears - Yahoo! Music.<break/>
                                            </prompt></block>
<disconnect/>
</form>
</vxml>

Зачастую голосовой браузер смешно озвучивает результаты поиска, в частности, при попытке угадать тип озвучиваемых данных. Например, строка 'WORLDOFBRITNEY.COM' будет озвучена просто как последовательность букв ('W','O', 'R',...), т.к. браузер считает, что это аббревиатура.

В общем случае, данные озвучиваются в соответствии с их типом по мнению голосового браузера. Например, число совпадений будет корректно произнесено, как "семьдесят три миллиона, шестьсот тысяч". К сожалению, точно также будет произнесено и число в заголовке страницы "WoBPictures.com".

Поиск в справочнике Yahoo!Local

При использовании поисковой системы Yahoo для местного поиска требуются два параметра: строка и место поиска. Место может задаваться несколькими способами, поэтому метод, вызываемый для запуска локального поиска, выглядит несколько сложнее, чем в случае использования интерфейса для поиска в Web.

Таким образом, в случае поиска в Yahoo!Local, приложение будет ожидать от пользователя ввода как ключевых слов, так и места, по которому будет осуществляться поиск. Все необходимые параметры будут вводиться голосом через страницу VoiceXML, после чего будет отправлен запрос к поисковому сервису Yahoo. В целом последовательность действий будет иметь следующий вид:

  • Создание объекта класса SearchClient, представляющего собой запрос к сервису Yahoo
  • Задание места поиска с помощью метода setLocation().
  • Задание строки поиска с помощью метода setQuery().

После создания объекта типа SearchClient все остальные действия, а именно: получение и разбор результатов запроса - выполняются точно так же, как и в случае поиска в Web.

Пример выполнения поиска в Yahoo!Local показан в листинге 6.

Листинг 6. Запуск поиска в Yahoo!Local и вывод результатов в виде VoiceXML

                
public static void DoSearch (String term,String location) {
    SearchClient client = new SearchClient("INSERTKEY");

    LocalSearchRequest request = new LocalSearchRequest();
    request.setLocation(location);
    request.setQuery(term);

    try {
        LocalSearchResults results = client.localSearch(request);

        System.out.println("<block><prompt>Найдено " +
                           results.getTotalResultsAvailable() +
                           " совпадений для фразы " +
                           term + " in " + location +
                           ". Первые  " +
                           results.getTotalResultsReturned() +
                           " результатов таковы :</prompt></block>");

        for (int i = 0; i < results.listResults().length; i++) {
            LocalSearchResult result = results.listResults()[i];

            Pattern pattern = Pattern.compile("&");
            Matcher matcher = pattern.matcher(result.getTitle());

            System.out.println("<block><prompt>" +
                               matcher.replaceAll("and") +
                               ".<break/> Телефон: " +
                               result.getPhone() +
                               "</prompt></block>");
        }
    }
    catch (Exception e) {
        System.err.println("Error calling Yahoo! Search Service: " +
                e.toString());
    }
}

Необходимо отметить, что данный класс автоматически заменяет символы амперсанда (&) на слово "and". Большинство VoiceXML-систем корректно работают с символами, требующими преобразования в escape-последовательности в XML/HTML (в частности, & должен превращаться в ∧), но есть и такие, которые отвергают подобные символы, т.к. формально они нарушают корректность VXML-документа с точки зрения спецификации XML. Поэтому необходимо быть внимательным и строго соблюдать формат XML. В данном случае мы произведем необходимые замены с помощью регулярного выражения.

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

Выше было приведено множество примеров полных VXML-документов. Теперь рассмотрим отдельные фрагменты вывода, а именно блоки, описывающие результаты поиска (см. листинг 7).

Листинг 7. Пример полученных результатов поиска

                
<block><prompt>Найдено 14 совпадений для фразы "plumber" в Нью-Йорк. 
Detailing the top 10 results.</prompt></block>
<block><prompt>State Plumbing Inspector.<break/> 
Телефон: (606) 862-1297</prompt></block>
<block><prompt>Hibbitts Brothers Wholesale.<break/> 
Телефон: (606) 864-2256</prompt></block>
<block><prompt>State Plumbing Inspector Paul Ray.<break/> 
Телефон: (606) 862-1297</prompt></block>
<block><prompt>Willard Neeley Plumbing and Htg.<break/> 
Телефон: (606) 864-6203</prompt></block>
<block><prompt>Vanhook Plumbing Htg and Cooling.<break/> 
Phone number: (606) 862-8228</prompt></block>
<block><prompt>Rooter Man of South Eastern Ky.<break/> 
Телефон: (606) 878-1339</prompt></block>
<block><prompt>Kettry Roaden Plumbing.<break/> 
Телефон: (606) 528-3396</prompt></block>
<block><prompt>Prestige Marble.<break/> 
Телефон: (606) 523-9186</prompt></block>
<block><prompt>Herb King Plumbing.<break/> 
Телефон: (606) 364-4534</prompt></block>
<block><prompt>AAA Plumbing.<break/> 
Телефон: (606) 528-0705</prompt></block>

Произношение подобного текста будет далеко от идеала. Например, "Htg" в названии компании "Vanhook Plumbing and Htg." будет произнесено по буквам, т.к. скорее всего голосовой браузер не распознает, что данное слово является сокращением от "heating" (отопление).

Заключение

В статье рассматривалось использование поискового интерфейса Yahoo Search API для выполнения поиска как в Web (базе данных Web-страниц от Yahoo), так и в справочнике компаний (Yahoo!Local). Результаты поиска выводились в виде документа в формате VXML. В итоге получилась система, предоставляющая голосовые сервисы для поиска компаний, например, можно позвонить и запросить список 10 лучших водопроводных компаний Нью-Йорка или же первые 10 сайтов, в которых встречаются слова "java" и "apple". Ключевой здесь является гибкая поддержка различных вариантов голосового ввода, позволяющая делать запросы по отдельным словам, фразам или же комбинировать их в произвольном порядке для получения максимально точных результатов поиска.

Кроме этого мы рассмотрели общий метод генерирования VXML-содержимого, который может использоваться в любой стандартной VoiceXML-системе, предоставляющей возможности голосового выбора.

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


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=17451