Фреймообразные структуры
Фреймообразные структуры позволяют строить вложенные друг в друга фреймы, образующие знания системы. За счет их вложенности система позволяет производить логический вывод и формировать сложные знания на основе более простых.
На основе фреймообразных структур удобно строить интеллектуальные системы верхнего уровня управления роботами (систему управления поведением) на основе диалога с пользователем на естественном языке.
В разработанном компанией «ДинСофт» универсальном программном обеспечении для мобильных роботов фреймообразные структуры являются частью скриптового языка iScript, на основе которого формируется система поведения робота.
В скриптовый язык iScript встроены операторы разбора текстовых запросов на естественном языке. С их помощью можно реализовать сложную систему фреймов (фреймообразную структуру) базы знаний робота . Наличие знаний, а также механизмов логического вывода на их основе, позволяют создать интеллектуальную систему управления поведением робота (стратегический уровень системы управления).
На вход системы фреймов поступают текстовые запросы с системы распознавания речи, различные события, а также команды с центрального сервера управления. Текстовый запрос может быть сгенерирован и самой системой скриптов, что позволяет ей создавать сложные действия из набора простых.
Кроме того, встроенные функции языка позволяют добавлять новые знания в систему, что дает возможность в случае необходимости реализовать функционал самообучения.
Фрейм
Основным элементом языка запросов является фрейм.
Каждый фрейм состоит из словесной предпосылки и программного следствия.
Словесная предпосылка фрейма определяет ключевые слова, по которым он активизируется. Программное следствие является скриптом на языке iScript.
Пример объявления фрейма:
... frame("Как живешь") { PlaySpeech("Хорошо"); } ...
В данном случае «Как живешь» является словесной предпосылкой, а скриптовый код «{ PlaySpeech(…); }» является программным следствием.
Фрейм «Как живешь» может быть активизирован текстовым запросом с фразой: «Как ты живешь?». Причем при запросе активизируется фрейм, в котором больше число активизированных слов.
Синонимы в словесной предпосылке
Словесная предпосылка может содержать слова-синонимы, перечисляемые через знак «|». В этом случае в указанном месте запроса может находиться любое из перечисленных слов. Например:
... frame("Дай шоколад|шоколадку|батончик") { ... } ...
В данном случае фраза «Дай мне этот батончик» активизирует данный фрейм.
Если в качестве синонима используется словосочетание, то слова этого словосочетания следует объединить знаком «&». Например:
... frame("Принеси нож | ножик | чем & порезать") { ... } ...
Таким образом, данный фрейм может быть активизирован как фразой «Принеси нож», так и фразой «Принеси чем порезать мясо».
Синонимом может быть являться весь фрейм целиком. Для создания фреймов-синонимов нужно перечислить фреймы друг за другом, а затем указать их общее программное следствие:
... frame("Как тебя зовут") frame("Как твое имя") frame("Зовут тебя как") { PlaySpeech("Меня зовут Си-Си", "Мое имя Си-Си"); } ...
В данном случае при активизации любого из перечисленных фреймов будет выполнено указанное программное следствие.
Параметры в словесной предпосылке
В словесной предпосылке фрейма могут содержаться параметры, являющимися ссылками на другие фреймы.
Например:
... frame("Принеси <что:Объекты>") { print(TextOf("что"), "\n"); // вывести текст параметра } ...
В данном случае фрейм «Принеси…» имеет параметр с названием «что», который является ссылкой на фреймсет «Объекты». Понятие «фреймсет» будет рассмотрено далее.
Если фреймсет «Объекты» содержит, например, объект «Кубик», то фрейм «Принеси…» может быть активизирован, например, фразой: «А принеси-ка мне тот кубик».
Допускается создание нетиповизированных параметров фрейма. В этом случае параметр вбирает в себя все слова запроса, стоящие от предыдущего до следующего слова словесной предпосылки. Если следующего слова словесной предпосылки нет, то параметр берет на себя все слова до конца фразы.
Например:
... frame("Принеси <n> коробок|коробку|коробки") { var n = TextOf("n"); ... } ...
Таким образом, фрейм может быть активизирован фразой: «Принеси 5 коробок». Причем параметр «n» вбирает все слова между «Принеси» и «коробок». В данном случае параметр будет равен 5.
Фреймсет
Каждый фрейм должен быть вложен во фреймсет. Каждый такой фреймсет определяет группу фреймов одного типа. Каждый фреймсет имеет название и приоритет выполнения.
Фреймсеты подразделяются на командные и объектные.
Командные фреймсеты содержат фреймы, определяющие действия робота. Например «Скажи …», «Принеси …», «Иди…». С них начинается обработка текстового запроса. Командные фреймсеты могут иметь различный приоритет, определяющий порядок выполнения действий, вложенных в него фреймов (выполнить немедленно или поставить в очередь).
Так, например, команды «Иди…», «Принеси…», следует ставить в очередь. А команда «Стой!», очевидно, должна быть выполнена немедленно.
Приоритет фреймсета может быть только положительным. Согласно внутреннему представлению данных, отрицательный приоритет определяет объектные фреймсеты.
Объектные фреймсеты определяют объекты предметной области, описывают их свойства, место, время и т.п. Например, «Стол», «Стул», «Конфета», «Здесь», «На столе», «сейчас», «через 5 минут» и т.п. Фреймы объектных фреймсетов могут быть только параметрами командных фреймов.
Объектные фрейметы не содержат приоритета (точнее их приоритет равен (-1)).
Пример командного фреймсета с приоритетом 2:
frameset("Команды", 2) { frame("Иди") { ... } frame("стой") { ... } ... }
Пример объектного фреймсета:
frameset("Объекты") { frame("кубик|кубика|кубику") { return 1; } frame("коробка|коробки|коробке") { return 2; } }
Далее рассматривается пример структуры фреймов, показывающий их взаимодействие:
// Командный фреймсет с приоритетом 1 frameset("Команды", 1) { frame("Где <что:Объекты>") { ... } frame("Как тебя зовут") frame("как твое имя") { ... } frame("Иди|Подойди <куда:Места>") { ... } } // Объектный фреймсет, перечисляет известные роботу объекты. // Все фреймы данного фреймсета возвращают идентификатор объекта frameset("Объекты") { frame("кубик|кубику|кубика") { return 1; } frame("шар|шару|шарика") { return 2; } } // Объектный фреймсет, перечисляющий понятные роботу места. // Все фреймы данного фреймсета возвращают координаты места frameset("Места") { frame("на & столик | к & столику <n>") { ... } frame("на кухню") { ... } frame("к <obj:Объекты>") {...} } while(1) sync(); // главный бесконечный цикл программы
Как показано в примере, имеются три фреймсета. Только фреймсет «Команды» является командным. Его приоритет 1. В данный фреймсет вложено несколько фреймов.
Фрейм «Где <что:Объекты>» в словесной предпосылке имеет слот с параметром. Параметры обозначаются в треугольных скобках и имеют название параметра (в данном случае «что»), и, опционально, фреймсет, в данном случае «Объекты».
В данном случае активизация фрейма «Где…» произойдет только в том случае, если запрос пользователя на естественном языке будет включать слова из фрейма «Где…», а также из одного из фреймов фреймсета «Объекты».
Например, запрос «А где этот кубик?» активизирует фрейм «Где…» и «Кубик…». Причем параметром «что» фрейма «Где…» будет являться фрейм «Кубик…».
Фреймы «Как тебя зовут» и «Как твое имя» являются синонимами, т.к. выполняют одно и то же программное следствие.
Фрейм «Кубик…» имеет слово с синонимами, перечисленными через знак «|». Любое из слов-синонимов может стоять на данном месте фразы.
Следует отметить, что знак «|» распространяется только на одно слово.
Если синонимом является последовательность слов, как например, у фрейма «на столик…», то все слова данной последовательности должны быть объединены через знак «&».
У фрейма «на столик…» имеется нетиповизированный параметр «n». Если тип параметра не указан, то он вбирает в себя все слова запроса, стоящие от предыдущего до следующего слова словесной предпосылки. Если следующего слова словесной предпосылки нет, то параметр берет на себя все слова до конца фразы.
В данном примере запрос «Иди к столику 5» активизирует фреймы «Иди к…» и «Столик…». Причем, параметром «куда» фрейма «Иди…» будет являться фрейм «Столик…», а параметром n фрейма «Столик…» будет являться текст «5».
Всю мощь фреймовой структуры показывает фрейм «к <obj:Объекты>». Его может активизировать, например, текстовый запрос: «Подойди к кубику». Здесь активизируется фрейм «Иди|Подойди…», параметром «куда» которого будет являться фрейм
«к <obj:Объекты>». Причем параметр «obj» фрейма «к <obj:Объекты%gt;» будет являться фрейм «Кубик».Несложно посчитать, какое количество комбинаторных перечислений было описано достаточно простым способом. Несложно также представить, насколько еще будет упрощено написание модели диалогов, если потребуется увеличить число объектов, число мест, а также число команд, оперирующих с ними, например, «Возьми <что>», «Принеси <что> <откуда>» и т.д.
Текст параметра. Функция TextOf
Программное следствие фрейма может раскрывать текст параметров, указанных в словесной предпосылке фрейма.
Функция TextOf(paramName) возвращает текст параметра, т.е. ту часть запроса, которая активизировала указанный параметр.
В данном случае «paramName» представляет собой строку и названием параметра. Обычно название параметра указываться в виде строковой константы, т.е. записывается в кавычках.
Например:
... frame("Покажи <что:Объекты>") { var objName = TextOf("что"); print(objName); } ...
Если данный фрейм был активизирован фразой «Покажи кубик», то переменная objName примет значение «кубик».
Текст параметра удобно вставлять в часть запроса в функцию LangQuery(…).
Значения параметра. Функция ValueOf
Программное следствие фрейма может раскрывать значение параметров, указанных в словесной предпосылке фрейма. Функция ValueOf(paramName) возвращает значение указанного параметра.
При этом производится выполнение программного следствия фрейма, указанного в качестве параметра. Результат, который возвращает фрейм оператором return, является значением, которое возвращает функция ValueOf.
В данном случае «paramName» представляет собой строку и названием параметра. Обычно название параметра указываться в виде строковой константы, т.е. записывается в кавычках.
Раскрывать значение нетиповизированного параметра фрейма запрещено.
Например: frameset("Команды", 1) { frame("Покажи <что:Объекты>") { var objID = ValueOf("что"); SetBrowserURL("show.php?id=" + objID); } ... } frameset("Объекты") { frame("Торт") { return 1; } frame("Пирожное") { return 2; } frame("Шоколад") { return 3; } } while(1) sync(); // главный бесконечный цикл программы
В данном примере запрос «Покажи пирожное» активизирует фрейм «Покажи…» и фрейм «Пирожное». Причем значением параметра «что» фрейма «Покажи…» будет фрейм «Пирожное».
При вызове функции ValueOf("что") вызывается программное следствие фрейма из параметра «что», т.е. фрейма «Пирожное». При этом программное следствие этого фрейма вернет значение 2.
Таким образом, локальная переменная objID в программном следствии фрейма «Покажи…» будет иметь значение 2.
Запрос из программного кода
Запрос из программного кода обычно применяется для реализации сложных действий из набора простых.
Для создания запроса служит функция LangQuery(…). В качестве параметров этой функции передается текст запроса на естественном языке. Опционально ей можно передать название фреймсета (не обязательно командного), в котором следует искать ответ.
Функция возвращает значение, возвращаемое оператором return в активизированном фрейме.
Например:
frameset("Действия", 1) { frame("Найди <что:Объекты>") { ... return true; } frame("Подойди к <чему:Объекты>") { if (!LangQuery("Найди "+TextOf("чему"))) return false; ... return true; } frame("Возьми <что:Объекты>") { if (!LangQuery("Подойди к "+TextOf("что"))) return false; ... return true; } } frameset("Объекты") { ... } while(1) sync(); // главный бесконечный цикл программы
Последнее значение фреймсета. Местоимения
У каждого фреймсета сохраняется последний активированный фрейм. В каждом фреймсете могут быть расположены специальные фреймы-местоимения, активация которых приводит к активации последнего фрейма из данного фреймсета.
Фрейм-местоимение имеет вместо программного следствия ключевое слово «pronoum». Например:
frameset("Объекты") { ... frame("она|его|им") pronoum; ... }
Пример использования фрейма-местоимения приведен ниже:
frameset("действия", 1) { frame("Найди <что:Объекты>") { print("Ищу объект id:", ValueOf("что"), "\n"); } frame("Возьми <что:Объекты>") { print("Кладу объект id:", ValueOf("что"), "\n"); } } frameset("Объекты") { frame("Кубик") { return 1; } frame("Шарик") { return 2; } frame("он|его") pronoun; // местоимение } while(1) sync(); // главный бесконечный цикл программы
В приведенном примере появляется возможность формировать следующую последовательность фраз:
- «Найди кубик»;
- «Возьми его».
При этом фраза «Возьми его» использует местоимение «его». Важно, что система при этом понимает, о чем идет речь.
В тот момент, когда была сформирована фраза «Найди кубик», был активирован фрейм «Найди…», при этом параметром «что» данного фрейма был фрейм «Кубик». Функцией ValueOf фрейм «Кубик» активизировался. Таким образом, фрейм «кубик» стал последним активизированным фреймом фреймсета «Объекты».
Идущая следом фраза «Возьми его» активирует фрейм «Возьми...», а параметром данного фрейма является фрейм «Он|Его». Т.к. данный фрейм является фреймом-местоимением, то при его активации выполняется программное следствие последнего активированного фрейма фреймсета «Объекты», т.е. фрейма «Кубик».
Значения последнего текста фреймсета из программного кода
Определить последнее значение активизированного фрейма указанного фреймсета можно с помощью функции LastTextOf:
frameset("Действия",1) { ... frame("отвези <что:Объекты> в мойку") { ... } frame("отвези в мойку") { var txt = LastTextOf("Объекты"); LangQuery("отвези "+txt+" в мойку"); } }
Сложносоставные предложения
Фреймообразные структуры позволяют создавать сложносоставные предложения путем создания отдельного командного фреймсета:
frameset("Составные",1) { frame("<действие1:Действия> и <действие2:Действия>") { if (!ValueOf("действие1")) return false; if (!ValueOf("действие2")) return false; return true; } frame("<a1:Действия> <a2:Действия> и <a3:Действия>") { if (!ValueOf("a1")) return false; if (!ValueOf("a2")) return false; if (!ValueOf("a3")) return false; return true; } } frameset("Действия",1) { frame("Возьми <что:Объекты>") { print("Беру ", ValueOf("что"), "\n"); return true; } frame("Раскрась <что:Объекты<") { print("Крашу ", ValueOf("что"), "\n"); return true; } } frameset("Объекты") { frame("кубик") { return 1; } frame("шарик") { return 2; } frame("он|его") pronoun; } while(1) sync();
В примере фреймсет «Составные» содержит два типа предложений, состоящих из двух и из трех составных частей.
Таким образом, фраза «Возьми кубик и раскрась его» активирует фрейм «действие1 и действие2». Следует отметить, что данная фраза может по отдельности активизировать фреймы «Возьми…» и «Раскрась…», но число слов в предложении, активизированное фреймом «действие1 и действие2» значительно больше.
Программное следствие фрейма «действие1 и действие2» приводит к выполнению сначала действия 1 и, если оно выполнилось удачно, переходит к выполнению действия 2.
Создание фреймовой структуры в коде программы
Фреймсеты и фреймы могут создаваться из любого места скриптовой программы, даже внутри функций, циклов и условий:
// фунция создания фреймсета имен function GenNameFrames( english ) { frameset("Объекты") { // условие создание фрейма if (english) { frame("Jonh") { return 1; } frame("Michael") { return 2; } } else { frame("Иван") { return 1; } frame("Михаил") { return 2; } } } } // вызов функции GenNameFrames(false); ...
Следует обратить внимание, что программному следствию фрейма недоступно значение локальных переменных функций.
Если фреймсет с таким именем уже существует, то прошлое реализация фреймсета удаляется.
Временный фреймсет
В языке iScript существует понятие временный фреймсет, который существует в течение 20 секунд после своего создания. Далее фреймсет удаляется.
Такой фреймсет удобно использовать при создании диалогов. Например, если робот задал пользователю какой-либо вопрос и ждет от него ответа. У пользователя есть 20 секунд, чтобы ответить, в противном случае его ответ будет уже не актуален.
Для создания временного фреймсета достаточно создать фреймсет с названием «temp».
Пример:
PlaySpeech("Хотите я расскажу о нашей продукции?"); frameset("temp", 1) { frame("да") { PlaySpeech("Наша продукция самая..."); } frame("нет") { PlaySpeech("Зря"); } }
Программное создание и удаление фреймсетов и фреймов
Создание и удаление фреймсетов и фреймов доступно из кода скриптовой программы с помощью специальных функций.
Функция AddFrame
Функция AddFrame(<frameset>, <словесная предпосылка>,<код>) создает фрейм со словесной предпосылкой <словесная предпосылка> во фреймсете <frameset> с программным следствием, заданного строкой <код>.
Пример:
AddFrame("Объекты", "Яблоко", "return 1;" );
Если в качестве кода используется строка с ключевым словом «pronoun», то создается фрейм-местоимение. Пример программного создания фрейма-местоимения:
AddFrame("Объекты", "он|его", "pronoun");
Данным способом удобно создавать фреймы по элементам базы данных:
// подключение к базе данных MySQL if (mysql_connect()) { // SQL-запрос if (mysql_query("SELECT `id`, `name` FROM objects")) { // разбор результата в виде ассоциированного объекта var row; while(row = mysql_fetch_assoc()) { // создание фрейма во фреймсете «Объекты» // с названием из базы данных, и возвращающего // идентификатор объекта AddFrame("Объекты", row.name, "return "+row.id+";"); } // освободить память от результатов запроса mysql_free_result(); } // закрыть MySQL mysql_close(); } ...
Функция AddFrameset
Функция AddFrameset(<название>, [<приоритет>=-1, [<temp>=false]]) создает фреймсет с названием <название> и приоритетом <приоритет>. Признак <temp> формирует временный фреймсет.
Также временный фреймсет можно создать, придав ему название «temp».
Параметры <приоритет> и <temp> являются необязательными. Приоритет по умолчанию равен (-1), что соответствует объектному фреймсету. Признак временного фреймсета по умолчанию не задан.
Пример создания объектного фреймсета из программного кода:
AddFrameset("Объекты");
Пример создания командного фреймсета с приоритетом 2 из программного кода:
AddFrameset("Действия", 2);
Пример создания временного фреймсета из программного кода:
AddFrameset("Ответы", 1, true);
Следует обратить внимание, что если одноименный фреймсет уже существует, то старое его содержимое сохраняется, но изменяется приоритет и признак временного фреймсета. Причем отчет времени существования временного фреймсета отсчитывается от последнего вызова данной функции.
Функция DeleteFrameset
Функция DeleteFrameset(<название>) удаляет фреймсет с названием <название>, а также все его фреймы.
Например:
DeleteFrameset("Ответы");
Функция EnableFrameset
Функция EnableFrameset(<название>, [<доступность>=true]) управляет доступностью фреймсета с названием <название>. Признак <доступность> управляет доступностью указанного фреймсета. Если <доступность> равна true, то фреймсет доступен, если <доступность> равна false, то фреймсет недоступен.
Если фреймсет не доступен, то активизация его фреймов производиться не будет.
Пример:
EnableFrameset("Объекты", false);
Получение строки нераспознанного запроса
Если текстовый запрос, поступивший на вход системы, не активизировал ни один из фреймов, то формируется событие «* notFound», а текст этого запроса можно получить с помощью функции NotFoundText().