Pokračujeme s jazykom VoiceXML

Toto cvičenie je zamerané na ďalšie dôležité funkcionality jazyka VoiceXML, ktoré umožňujú vytvárať kompletné hlasové interaktívne služby.

Pre úspešné napojenie sa na vedomosti, získané v predchádzajúch cvičeniach sa vráťme k Príkladu 4 z cvičenia, kde sme sa naučili získavať vstup od používateľa:

Príklad 4

<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.0">
  <form id="moj_druhy_dialog">
    <field name="navod">
      <prompt> Želáte si vypočuť návod? </prompt>

      <grammar root="main">
        <rule id="main" scope="public">
          <one-of>
            <item>áno</item>
            <item>nie</item>
          </one-of>
        </rule>
      </grammar>

      <filled>
        <if cond="navod=='áno'">
            <goto next="navod.vxml"/>
        </if>    
      </filled>

    </field>
  </form> 
</vxml>

Na tomto príklade sme sa toho veľa naučili:

  • V prvom rade, vidíme ako vyzerá jednoduchá forma (teda vnútro <form> elementu).
  • Naučili sme sa používať vstupné pole, teda <field> element, ktorý nám umožňuje získať vstup od používateľa.
  • Naučili sme sa, že minimálnou súčasťou vstupného poľa <field> musia byť elementy
    • <prompt>, pomocou ktorej sa môžeme používateľa spýtať konkrétnu otázku,
    • <grammar> element, ktorý definuje rečovú gramatiku, vymedzujúcu možné vstupy používateľa
    • <filled> element, ktorý je miestom na spracovania získaného vstupu používateľa.

V uvedenom príklade sme rečovú gramatiku definovali ako vstavanú (built-in), čo znamená, že gramatika je priamo súčasťou VoiceXML skriptu (vtedy hovoríme, že sa jedná o "inline grammar"). Zároveň sme sa však na ďalšom cvičení naučili vytvárať samostatné rečové gramatiky (súbory s príponou .grxml), ktoré môžu obsahovať aj značky pre sémantickú interpretáciu podľa W3C SISR špecifikácie (sémantické tagy).

Používať vstavané, inline gramatiky je často vyslovene nepraktické. Ak si predstavíme napr. službu o cestovných poriadkoch autobusov na Slovensku, kde používateľ musí vybrať jednu z viac ako sedemtisíc zastávok na Slovensku, použitím vstavanej gramatiky, by sa VoiceXML skript takejto služby stal extrémne neprehľadným, dlhým a zle spravovateľným.

Preto sa v praxi oveľa častejšie využívajú externé gramatiky, teda rečové gramatiky, ktoré sú umiestnené mimo VoiceXML skriptu v samostatných súboroch.

Možno si spomínate, že s referovaním externej gramatiky sme sa už stretli. Toto volanie sme použili v cvičení zameranom na sémantickú interpretáciu. Jednalo sa konkrétne o Príklad 3. Pripomeňme si ho:

Príklad 3

<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.0">
  <form id="moj_druhy_dialog">
    <field name="navod">
      <prompt> Želáte si vypočuť návod? </prompt>
      <grammar src="yesno.grxml" type="application/grammar+xml">

      <filled>
        <if cond="navod=='yes'">
           <goto next="navod.vxml"/>
        </if>   

        <if cond="navod=='no'">
           <goto next="nextdialog.vxml"/>
        </if>

      </filled>
    <field>
  </form> 
</vxml>

Volanie externej gramatiky je teda realizované na riadku 6:

<grammar src="yesno.grxml" type="application/grammar+xml">

Z uvedeného vidíme, že element <grammar> sa dá použiť aj pre volanie externej gramatiky, tak, že cestu k súboru s gramatikou definujeme v atribúte src, pričom v atribúte type zadefinujeme typ gramatického formátu (v prípade XML gramatík podľa W3C SRGS 1.0 špecifikácie to bude "application/grammar+xml").

Zdá sa, že náš posledný príklad (Príklad 3) je plne funkčný a môžeme ho bezproblémov používať. Avšak, bezproblémovo bude fungovať iba v prípade, že bude používateľ reagovať tak, ako je to očakávané.

V aplikáciach napísaných v jazyku VoiceXML, môžu počas rozpoznávania vstupu od používateľa nastať nasledujúce udalosti:

  1. Používateľ vysloví slová alebo frázy, ktoré vyhovujú definovanej gramatike a tieto sú rozpoznané s dostatočnou presnosťou.
  2. Používateľ vysloví slová alebo frázy, ktoré vyhovujú definovanej gramatike, avšak tieto sú rozpoznané s nedostatočnou presnosťou.
  3. Používateľ vysloví slová alebo frázy, ktoré sa nenachádzajú v definovanej gramatike, alebo sú definované v inom poradí.
  4. Používateľ nereaguje (ostane ticho) v definovanom časovom intervale.

Rozoberme si teraz situácie 2.-4., o ktorých sme doteraz neuvažovali:

  1. Používateľ vysloví slová alebo frázy, ktoré vyhovujú definovanej gramatike, avšak tieto sú rozpoznané s nedostatočnou presnosťou.

Výsledkom tejto situácie je signalizácia udalosti nomatch, nakoľko si systém nie je istý tým, či to čo bolo rozpoznané je skutočne to čo bolo povedané. Systém sa rozhoduje podľa prednastavenej hodnoty dôveryhodnosti rozpoznaného výsledku (confidence level), ktorý sa môže líšiť pre jednotlivé platformy. Zvyčajne je tento nastavený v rozpätí 50-70%.

  1. Používateľ vysloví slová alebo frázy, ktoré sa nenachádzajú v definovanej gramatike, alebo sú definované v inom poradí.

Táto situácia sa môže stať pomerne často, najmä v prípade, že výzva (otázka systému), realizovaná <prompt> elementom, nie je správne naformulovaná, resp. dôjde k nepochopeniu zo strany používateľa. Ďalším dôvodom, pre ktorý často dochádza k tejto situácií, je príliš obmedzujúca rečová gramatika - teda gramatika, ktorá nezahŕňa všetky potenciálne vyjadrenia používateľa prislúchajúce k danej otázke.

Výsledkom tejto situácie je znova signalizovanie udalosti nomatch, nakoľko používateľovo vyjadrenie nezodpovedá nastavenej rečovej gramatike.

  1. Používateľ nereaguje (ostane ticho) v definovanom časovom intervale.

V dialógových interakciách medzi človekom a strojom môže taktiež dôjsť k situácií, že používateľ nezareaguje vôbec na výzvu systému v definovanom časovom limite resp. vôbec nezareaguje. Tento prípad často nastáva ak je používateľ zaskočený otázkou, alebo jej zodpovedanie vyžaduje dlhšie premýšľanie, resp. ak používateľ vôbec nevie ako má reagovať.

Výsledkom tejto situácie je signalizovanie noinput udalosti, čo značí, že používateľ nereagoval počas stanoveného časového limitu. Dĺžka časového limitu (timeout) je platformovo-špecifická a zvyčajne je to približne 5 sekúnd. Tento interval je možné zmeniť cez atribút timeout <prompt> elementu (iba vo v prípade jeho použitia vo vstupnom poli).

Z uvedených prípadoch už teda vieme, kedy a ako vzniknú udalosti noinput a nomatch. Každú vzniknutú udalosť je potrebné ošetriť, aby sme zabezpečili "zotavenie sa systému z chýb", ktoré vznikli v komunikácií. Najjednoduchším spôsobom je využitie <noinput> a <nomatch> elementu. Alternatívou je využitie <catch> elementu so správne nastaveným atribútom event, napr.: <catch event="noinput"> </catch> alebo <catch event="nomatch"> </catch>.

Pri obsluhe udalostí je potrebné mať na pamäti, že zvolený obslužný mechanizmus má byť nápomocný, aby sa interakcia čo najefektívnejšie zotavila zo vzniknutej udalosti.

Poďme teda upraviť posledný príklad v tomto cvičení (Príklad 3) tak, aby obsahoval mechanizmy obsluhy udalostí noinput a nomatch:

Príklad 4

<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.0">
  <form id="moj_druhy_dialog">
    <field name="navod">

      <nomatch> Povedzte áno ak si želáte vypočuť návod. V opačnom prípade povedzte nie. </nomatch>

      <noinput> 
        <prompt> Prepáčte, systém nezachytil vašu odpoveď </prompt>
        <reprompt/>
      </noinput>

      <prompt> Želáte si vypočuť návod? </prompt>
      <grammar src="yesno.grxml" type="application/grammar+xml">

      <filled>
        <if cond="navod=='yes'">
           <goto next="navod.vxml"/>
        </if>   

        <if cond="navod=='no'">
           <goto next="nextdialog.vxml"/>
        </if>

      </filled>
    <field>
  </form> 
</vxml>

Ako ste si iste všimli, do elementu <field> sme vložili elementy <nomatch> a <noinput>, ktoré sa vykonajú, ak v danom dialógu vzniknú rovnomenné udalosti.

Dialóg, v ktorom nastane nomatch udalosť môže vyzerať nasledovne:

S:  Želáte si vypočuť návod?
U:  čo ja viem    <-- NOMATCH
S:  Povedzte áno ak si želáte vypočuť návod. V opačnom prípade povedzte nie.
U:  áno
...

alebo:

S:  Želáte si vypočuť návod?
U:  áno (confidence = 0.4)  <-- NOMATCH
S:  Povedzte áno ak si želáte vypočuť návod. V opačnom prípade povedzte nie.
U:  áno
...

Dialóg, v ktorom nastane noinput udalosť môže vyzerať nasledovne:

S:  Želáte si vypočuť návod?
U:  (ticho > 5 sekúnd)       <-- NOINPUT
S:  Prepáčte, systém nezachytil vašu odpoveď.
S:  Želáte si vypočuť návod?
U:  nie
...    

Pozn: Obsluhu udalostí môžeme realizovať aj na úrovni formy (priamo vo vnútri <form> elementu), pričom potom bude platiť pre všetky vstupné polia danej formy. Ak umiestnime obsluhu udalostí ešte o jeden level vyššie (document level), teda priamo do <vxml> elementu, každé vstupné pole v danom dokumente, ktoré nedefinuje iné obslužné elementy, bude využívať tie, ktoré sú definované na úrovni dokumentu. Obslužné elementy <nomatch>, <noinput> alebo <catch> môžeme umiestniť aj v ARD dokumente. Vtedy budú platné v celej aplikácií, t.j. vo všetkých VoiceXML dokumentoch napojených na daný ARD (Application Root Document) dokument.

Jazyk VoiceXML definuje ešte aj ďalšie typy udalostí, ktoré môžu nastať počas interakcie s používateľom - help, error a exit. O nich ale niekedy inokedy.

Ako ste už isto vypozorovali z uvedených príkladov, <goto> element nám slúži na presúvanie sa k iným častiam dialógu. Naše poznanie bolo zatiaľ iba povrchné a preto možno neviete, že bez použitia <goto> elementu by sme vedeli tvoriť iba dialógy, ktoré obsahujú jednu výmenu medzi systémom a používateľom, bez ohľadu na to, koľko foriem (<form> elementov) by obsahoval váš VoiceXML skript. VoiceXML interpreter interpretuje vždy iba prvý <form> element v dokumentovom poradí! Ak nie je definovaný žiadny prechod do iného dialógu alebo iného VoiceXML súboru, interakcia končí!

<goto> je preto pre nás životne dôležitý element, ktorý nám umožňuje prechod do:

  • inej položky aktuálneho dialógu (aktuálneho <form> elementu)
  • iného dialógu (iného <form> elementu)
  • iného VoiceXML dokumentu

Prechod k inej položke aktuálneho diálogu často nevyužívame, lebo prechod medzi položkami danej formy vieme realizovať cez naplňanie ich premenných. Napriek tomu, prechod k inej položke v rámci tej istej formy vieme realizovať cez <goto> element nasledovne:

<goto nextitem="get_departure_city"/>

Prechod do iného <form> elementu realizujeme nasledovne:

<goto next="#another_dialog"/>

Prechod do iného VoiceXML súboru sa realizuje takto:

<goto next="C://applications/second_VoiceXML_doc.vxml"/>

Destinácia, do ktorej chceme presunúť vykonávanie našej VoiceXML aplikácie môže byť vyjadrená aj dynamicky, teda pomocou nejakého výrazu, ktorý sa musí vyhodnotiť, aby sme vedeli, kam má interpreter skočiť. V takomto prípade používame namiesto atribútu nextitem atribút expritem a namiesto atribútu next atribút expr. Napr.:

<goto expr="'#' + 'another_dialog'"/>

Takmer v každom kroku dialógu odpoveď používateľa definuje jeho výber, ktorý určuje ďalší smer interakcie. Používateľ si volí, či chce alebo nechce počuť návod, objednať si pizzu, zistiť zostatok na účte, ukončiť aplikáciu a pod. Používateľ vyberá jednu z možností typu, či si želá vypočuť domáce správy, zahraničné správy alebo správy o športe. Vo všetkých týchto prípadoch potrebujeme na základe používateľského vstupu vetviť náš VoiceXML kód a "skočiť" na zodpovedajúce miesto v našej aplikácií. Na tento účel nám slúži <if> element. Vo všeobecnosti je tento element vykonávateľom podmienenej logiky :).

Vetviť náš program ale nevieme hocikde. Element <if> môže byť umiestnený v častiach kódu, ktoré realizujú vykonávateľný obsah (teda v <block> elemente) alebo v elemente, v ktorom spracovávame vstup od používateľa (<filled> element).

Jednoduché použitie <if> elementu sme už taktiež videli v našom Príklade 3 a Príklad 4, kde sme pomocou neho testovali vyjadrenie používateľa, či zodpovedá reťazcu "yes" alebo "no" a na základe toho sme sa presunuli k dialógu s návodom alebo sme pokračovali v základnej (pre nás momentálne neznámej) službe.

<if cond="navod=='yes'">
  <goto next="navod.vxml"/>
</if>   

Element <if> môže byť použitý samostatne, spolu s definovaním druhej možnosti <else/> alebo s viacerými možnosťami realizovanými pomocou <elseif> elementu. Všetky tri prípady použitia ilustrujme na nasledujúcich príkladoch:

Príklad 5

<if cond="basket &gt; 100"> 
  <prompt> V košíku nemôžete mať viac ako <value expr="basket"/> položiek.</prompt> 
</if> 

Príklad 6

<if cond="amount &lt; 29.95"> 
  <prompt> Na vašom účte nie je dostatok finančných prostriedkov na zakúpenie tovaru. </prompt>
<else/> 
  <prompt> Zostatok na vašom účte je <value expr="amount-29.95"/>. </prompt> 
</if> 

Príklad 7

<var name="flavor_code"/>
...

<if cond="flavor == 'vanilla'"> 
  <assign name="flavor_code" expr="'v'"/> 
<elseif cond="flavor == 'chocolate'"/> 
  <assign name="flavor_code" expr="'h'"/> 
<elseif cond="flavor == 'strawberry'"/> 
  <assign name="flavor_code" expr="'b'"/> 
<else/> 
  <assign name="flavor_code" expr="'?'"/> 
</if>

Pri použití výrazov vo vnútri cond atribútu, nie len vrámci <if> elementu nastáva malá komplikácia, ak daný výraz porovnáva hodnotu dvoch premenných alebo premennej a čísla. nakoľko znaky "menší" ("<") a "väčší" (">") sú zároveň symbolmi, ktoré ohraničujú začiatok a koniec XML elementov. Preto namiesto nich musíme používať tzv. "zástupné symboly":

  • znak "menší" ("<") nahrádzame značkou &lt;
  • znak "väčší" (">") nahrádzame značkou &gt;

V poslednom príklade (Príklad 7) sme zaznamenali dva nové elementy <var> a <assign>. Tieto elementy slúžia na prácu s premennými. Áno, aj vo VoiceXML aplikáciach sa nezaobídeme bez premenných. Na predchádzajúcich cvičeniach sme zatiaľ hovorili iba o premenných polí, ktoré vznikajú z name atribútu vstupných polí (napr. <field> elementu), do ktorých sa ukladá rozpoznaný vstup od používateľa (resp. výsledok sémantickej interpretácie tohto vstupu). Avšak, čo je veľmi nemilé :-), tieto premenné zanikajú akonáhle dané pole opustíme. S nimi samozrejme okamžite prídeme aj o to, čo povedal používateľ. My však zvyčajne potrebujeme s daným vstupom ďalej pracovať. Jedinou možnosťou ako túto informáciu zachovať, je prekopírovať ju do inej premennej, ktorá je "viac globálna" a teda platí v celom VoiceXML dokumente, resp. v celej VoiceXML aplikácií. Takéto premenné môžeme vytvoriť pomocou elementu <var>.

Novú premennú môžeme vytvoriť nasledovne:

<var name="flavor_code"/>

Premenná _flavorcode bude vytvorená ako tzv. undefined, teda premenná bez hodnoty.

<var name="phone" expr="'6305551212'"/> 

Premenná phone bude pri vytvorení nainicializovaná textovým reťazcom "6305551212".

<var name="vaha" expr="43"/>

Premenná vaha bude nainicializovaná ako číslo s hodnotou 43.

<var name="y" expr="x+1"/>

Premenná y bude nainicializovaná výsledkom výrazu "x+1".

Ako vyplýva z uvedených príkladov atribút name definuje názov premennej. Atribút expr definuje jej hodnotu.

Okrem mechanizmu vytvorenia premennej, nemenej dôležité bude aj miesto, kde danú premennú vytvoríme. Umiestnenie elementu <var> bude teda definovať aj platnosť danej premennej (scope).

Vytvorená premenná, podľa svojej polohy, môže byť viditeľná (platná) v nasledujúcich leveloch:

  • Session level - na úrovni relácie (s používateľom) existujú iba read-only premenné, ktoré definuje interpeter jazyka VoiceXML. Z úrovne VoiceXML ich nie je možné definovať (je ich však možné zobraziť/použiť).
  • Application level - premenné je možné definovať na úrovni aplikácie, tak, že <var> element umiestníme do tzv. ARD dokumentu (Application Root Document). Potom, takáto premenná bude viditeľná vo všetkých VoiceXML dokumentoch, ktoré sa odkazujú na ARD dokument (cez atribút application elementu <vxml>)
  • Document level - ak premenné vytvoríme priamo vo vnútri <vxml> elementu, budú viditeľné v celom VoiceXML dokumente. Ak však z daného dokumentu skočíme do iného VoiceXML dokumentu (pomocou <goto>), daná premenná sa už nebude dať sprístupniť.
  • Dialog level - premenné môžeme vytvárať aj vo vnútri foriem a menu (<form> a <menu>). Takáto premenná má lokálny charakter a je použiteľná iba vo vnútri danej formy alebo menu.
  • (anonymous) - premenné je možné vytvárať aj vo vnútri ďalších elementov, ktoré sú vnorené vo formách, napríklad vo vnútri <block> alebo <filled> elementov. Tieto sú viditeľné iba lokálne v danom elemente.

Elementy <assign> a <clear> slúžia na priradenie hodnoty konkretnej premennej (<assign>) a na vymazanie hodnôt jednej alebo viacerých premenných (<clear>)

Element <assign> umožňuje premennej priradiť hodnotu. Na to využíva dva atribúty:

  • name atribút definuje názov premennej, ktorej sa má priradiť hodnota
  • expr atribút definuje výraz alebo hodnotu, ktorou sa má naplniť daná premenná.

Použitie je ilustrované na nasledujúcich príkladoch:

<assign name="flavor" expr="'chocolate'"/> 

Do premennej flavor sa vloží text "chocolate".

<assign name="document.mycost" expr="document.mycost+14"/>

Do premennej mycost definovanej na úrovni dokumentu sa vloží výsledok výrazu "document.mycost+14", teda, k premenej mycost sa priráta hodnota 14.

Element <clear> umožňuje vymazať obsah jednej alebo viacerých premenných. Vymazanie obsahu neznamená v prípade čísel nastavenie hodnoty na nulu, alebo v prípade reťazca znakov, vymazanie týchto znakov, ale nastavenie statusu premennej na typ undefined.

<clear> element má jeden povinný atribút namelist, ktorý obsahuje zoznam premenných, ktoré sa majú "resetovať".

Napríklad:

<clear namelist="city state zip"/>

Uvedený príklad vymaže obsah premenných city, state a zip.

V nasledujúcom príklade si ukážeme, jedno z hlavných použití premenných pre uchovanie získaných vstupov od používateľa, ktoré boli získané v rôznych formách. Nasledujúci príklad je jednoduchou službou predpovede počasia. Pozostáva z troch foriem - prvá získava informáciu o meste, pre ktoré chce používateľ získať informáciu o počasí, druhá forma slúži na získanie dňa v týždni a tretia forma sumarizuje získané informácie a pristupuje do databázy pre získanie informácií o počasí (použitím elementu <submit>).

Príklad 8

<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.0">

  <var name="Weather_city"/>
  <var name="Weather_day"/>

  <form id="get_city">
    <block>
      Víta Vás služba Predpovede počasia.
    </block>

    <field name="city">

      <nomatch> Povedzte jedno z okresných miest na Slovensku. </nomatch>
      <noinput> <reprompt/> </noinput>

      <prompt> Pre aké mesto si želáte vypočuť predpoveď? </prompt>
      <grammar src="cities.grxml" type="application/grammar+xml">

      <filled>
        <assign name="Weather_city" expr="city"/>   
        <goto next="#get_day"/>
      </filled>
    <field>
  </form> 

  <form id="get_day">
    <field name="day">

      <nomatch> Vyberte deň, pre ktorý si želáte vypočuť predpoveď počasia. </nomatch>
      <noinput> <reprompt/> </noinput>

      <prompt> Vyberte deň.  </prompt>
      <grammar src="days.grxml" type="application/grammar+xml">

      <filled>
        <assign name="Weather_day" expr="day"/> 
        <goto next="#obtain_weather"/>
      </filled>
    <field>
  </form>

  <form id="obtain_weather">
    <block> 
      <submit next="/servlet/weather" namelist="Weather_city Weather_state"/> 

      <prompt> 
          Predpoveď počasia pre mesto <value expr="Weather_city"/> na <value expr="Weather_day"/> je:
          Bude <value expr="weather_state"/>, <value expr="temperature"/> stupňov celzia.
      </prompt> 

    </block> 
  </form>

</vxml>

Príklad dialógu:

S:  Víta Vás služba Predpovede počasia.
S:  Pre aké mesto si želáte vypočuť predpoveď?
U:  Košice
S:  Vyberte deň.
U:  zajtra
S:  Predpoveď počasia pre mesto Košice na zajtra je: Bude slnečno, 15 stupňov celzia.

V predchádzajúcich a aj tomto cvičení sme sa naučili základy jazyka VoiceXML. Avšak, možnosti, ktoré tento jazyk ponúka je oveľa viac. Pre jeho podrobnejšie štúdium odporúčame niektorý z dostupných tutoriálov na internete, alebo priamo text W3C odporúčania: