Tipps zur VoiceXML Programmierung
Inhalt
1 Grundgerüst für VoiceXML Dialoge
1.1 Grundgerüst DTMF-Dialog
1.2 Grundgerüst ASR-Dialog
2 Events in VoiceXML
3 Caching & Fetching
4 Promptausgabe und -kontrolle
5 Beeinflussung des Dialogblaufs
6 <record>
7 ASR-spezifische Themen
7.1 Confidence level
7.2 N-Best Spracherkennung
1 Grundgerüst für VoiceXML Dialoge
Die nachfolgenden Beispiele für einen standardisierten DTMF-Dialog bzw. ASR-Dialog können direkt kopiert und weiterverwendet werden.
Bitte beachten Sie, dass diese Beispiele nur ein Grundgerüst darstellen und ohne weitere Anpassungen an den rot markierten Stellen
nicht lauffähig sind.
1.1 Grundgerüst DTMF-Dialog
Das folgende DTMF-Beispiel kann zum Beispiel dazu verwendet werden, bestehende DTMF-Dialoge 'VoiceXML-fähig' zu machen.
Jeweils eine VoiceXML DTMF-Form ersetzt dabei die einzelnen DTMF-Menüs.
Mit <goto> Tags bewegt man sich zwischen den einzelnen Menüs.
Obwohl es sich dabei um eine statische, also zur Laufzeit unveränderliche Dialogschritte handelt, können Änderungen schnell
implementiert werden.
<?xml version="1.0"?>
<vxml version="2.0"?>
<form id=" FormName ">
<field name="eingabe">
<prompt>
<audio src="
URI der Audiodatei
">
Alternativer TTS-Text falls Audiodatei nicht vorhanden
</audio>
</prompt>
<option dtmf="1" value="
Eindeutiger Rückgabewert "/>
<option dtmf="2" value=" Eindeutiger Rückgabewert "/>
<option dtmf="3" value=" Eindeutiger Rückgabewert "/>
<option dtmf="
Ungültige Taste " value="ungueltig"/>
<!-- usw. -->
<!-- Timeout -->
<noinput>
<!-- Ansage wiederholen -->
<reprompt/>
</noinput>
<filled>
<if cond="eingabe == ' Rückgabewert '">
<!-- Handling für definierte Tasten -->
<elseif cond="eingabe == ' Rückgabewert '"/>
<!-- Handling für definierte Tasten -->
<elseif cond="eingabe == 'ungueltig'"/>
<!-- Handling für ungültige Tasten -->
<else/>
<!-- Handling für nicht definierte Tasten -->
</filled>
</field>
</form>
</vxml>
Hinweise zum Codebeispiel:
-
Die Form sollte mittels id=... unbedingt einen eindeutigen Namen erhalten.
Das ermöglicht 'Sprünge' zwischen mehreren Forms.
-
Die Definition des <audio> Tags ist nicht vorgeschrieben, der blau markierte Text reicht,
wenn keine vorgefertigten Audiodateien vorliegen, normalerweise aus.
Die Konstruktion mit <audio> vereinfacht jedoch die spätere Umstellung auf Audiodateien.
-
Dem value Attribut des <option> Tags können beliebige, jedoch
eindeutige Werte zugeordnet werden. Diese werden später in den <if> Statements der
<filled> Sektion wieder abgefragt.
-
Um ungültige Tasten explizit abzufangen, verwendet man die Zeile:
<option dtmf="
Ungültige Taste " value="ungueltig"/>
wobei Ungültige Taste die Werte '0' - '9', '*' oder '#' annehmen kann.
1.2 Grundgerüst ASR-Dialog
<?xml version="1.0"?>
<vxml version="2.0"?>
<form id=" FormName ">
<field name="eingabe">
<prompt>
<audio src="
URI der Audiodatei
">
Alternativer TTS-Text falls Audiodatei nicht vorhanden
</audio>
</prompt>
<grammar>
Inline Grammardefinition
</grammar>
<!-- Keine Eingabe abfangen -->
<noinput>
<reprompt/>
</noinput>
<!-- Falsche Eingabe abfangen-->
<nomatch>
<reprompt/>
</nomatch>
<filled>
<if cond="eingabe == ' Rückgabewert '">
<!-- Handling für definierte Tasten -->
<elseif cond="eingabe == ' Rückgabewert '"/>
<!-- Handling für definierte Tasten -->
<elseif cond="eingabe == 'ungueltig'"/>
<!-- Handling für ungültige Tasten -->
<else/>
<!-- Handling für nicht definierte Tasten -->
</filled>
</field>
</form>
</vxml>
2 Events in VoiceXML
VoiceXML unterstützt ein Eventkonzept, bei dem man -angelehnt an JAVA- Events auslösen (throw) und Events abfangen (catch) kann.
Ein VoiceXML System besitzt bereits eingebaute Events, die entweder von der Spracherkennung ausgelöst werden können (nomatch, noinput,...),
als auch vom VoiceXML Interpreter (semantic, throw, exit).
Beispiel:
<throw event="aufhoeren"/>
<catch event="aufhoeren">
<prompt> Auf Wiederhoeren. </prompt>
<disconnect/>
<catch/>
Verschiedene Events sind in VoiceXML aufgrund ihrer häufigen Verwendung bereist vordefiniert, wie z.b.:
- noinput
- nomatch
- help
- exit
Die Definition von <noinput> ist somit gleichbedeutend mit <catch event="noinput">.
Ein <catch> Element besitzt die Atrribute <cond> und
<count>. Das ermöglicht, die Ausführung an Bedingungen zu knüpfen (cond), oder je nach Häufigkeit des Aufrufs
unterschiedlich zu reagieren (count). Insbesondere das count Attribut wird gerne als De-Eskalation bei Falscheingaben genutzt. Dabei wird
jeweils eine unterschiedliche Information gespielt, die je nach Häufigkeit exaktere Informationen gibt.
<noinput count="1">
<prompt> Ich habe Sie nicht gehoert. </prompt>
<noinput/>
<noinput count="2">
<prompt> Ich habe Sie erneut nicht gehoert. </prompt>
Sie koennen Ihren Kontostand abfragen oder eine Ueberweisung ausführen.
<noinput/>
<noinput count="2">
<prompt> Ich habe Sie wiederrum nicht gehoert.
Sagen Sie 'Kontostand', 'Ueberweisung' oder 'zurück'. </prompt>
<noinput/>
Events können den Scope form, document, oder application
besitzen. Es wird, abhängig vom Ort des Aufrufs, jeweils das vom Scope gleichwertige oder nächst höherliegende
<catch> ausgeführt.
3 Caching & Fetching
Böse Zungen behaupten, www bedeutet nicht World Wide Web, sondern Warten Warten Warten. Dieses Manko hat sich
zwar in letzter Zeit durch die große Vernetzung grundlegend verbessert, jedoch muß auch der VoiceXML-Entwickler Laufzeiten berücksichtigen,
da VoiceXML auf dem http Protokoll aufbaut.
VoiceXML Dokumente selbst sind in der Regel nicht sehr groß, so daß diese wenigen Kilobytes meistens schnell übertragen werden.
Kritischer wird das schon bei Anfragen, die serverseitige Skriptsprachen mit Datenbankabfragen erfordern.
Die zeitkritischste Komponenete ist jedoch das Laden von Audiodateien, da diese erfahrungsgemäß relativ groß sind.
VoiceXML berücksichtigt von Beginn an diese Umstände. Es wurde ein Pool von Policies definiert, die ein
Zwischenspeichern (Caching) bereits geladener sowie das Laden (Fetching) neuer Daten ermöglicht. Diese Policies können mittels
<property> verschiedene Scopes (application, document usw.) besitzen, oder explizit als
Attribut bei verschiedenen Tags verwendet werden.
Caching
Beim Caching von Dokumenten und Dateien werden folgende Attribute verwendet:
Maxage bezeichnet die Zeit in Sekunden, während der ein Dokument aus dem Cache genommen wird, ohne es erneut über das Web zu laden.
Maxstale bezeichnet die Zeit in Sekunden, während der nach dem Ablauf von Maxage ein Dokument dennoch aus dem Cache genommen wird.
Fetching
Beim Laden von Dokumenten und Dateien werden folgende Attribute verwendet:
Fetchtimeout bezeichnet die Zeitraum in Sekunden, während dessen auf eine angeforderte Resource gewartet wird. Ist die Zeit
abgelaufen, wird ein Timeout Event ausgelöst.
Fetchaudio bezeichnet die Audiodatei, die während des Wartens auf eine angeforderte Resource gespielt wird.
Sobald die Resource geladen und vorhanden ist, wird die Audiodatei abgebrochen.
Fetchhint zeigt an, wie Resourcen geladen werden: Erst wenn sie benötigt werden (safe), oder bereits beim Parsen des
Dokumentes (prefetch).
Beispiel:
<audio src="http://www.irgendwo.de/Hallo.wav" maxage="0" fetchtimeout="3s" fetchhint="safe"/>
Die Audiodatei wird, wenn sie im Dialog benötigt wird (fetchhint=safe), auf jeden Fall (maxage=0) neu von der angegebenen
URI geladen. Wenn die Datei 3 Sekunden (fetchtimeout=3s) nach Anforderung nicht empfangen wurde, wird ein Fehler-Event ausgelöst.
4 Promptausgabe und -kontrolle
Prompts werden in VoiceXML nicht unmittelbar ausgegeben, sondern werden mitels eines Queueing-Mechanismus
zwischengespeichert, und erst zu einem definierten Zeitpunkt ausgegeben.
Ein VoiceXML Interpreter befindet sich bei der Interpretation einer VoiceXML Datei entweder im Zustand 'Warten auf Eingabe' oder
im Zustand 'Abarbeitung'.
Im Zustand 'Abarbeitung' (z.b. <block>, <filled> ) werden Promptausgaben
grundsätzlich in die Queue gesendet.
Die Prompts werden erst aus der Queue entnommen und abgespielt, wenn
- der Zustand 'Warten auf Eingabe' erreicht ist (z.b. <field>,
<initial>)
- ein <exit> oder <transfer> erreicht ist (=> Flush unplayed prompts)
- bei Verwendung des fetchaudio Attributs in (z.b. in <goto> oder <submit>)
Im Normalfall befindet sich man somit bereits in der nächsten Eingabeschleife, sobald man ein Prompt hört.
Dazu ein Codebeispiel:
<?xml version="1.0"?>
<vxml version="2.0">
<form>
<block>
<prompt>
Sie befinden sich in einem Block Element.
Obwohl hier gar keine Eingabe definiert wurde, können Sie jederzeit 'ja' oder 'nein' sagen.
Das liegt daran, dass sich der Dialog bereits bis zum Field Element voranbewegt hat.
Und dort ist die Eingabe von 'ja' oder 'nein' erlaubt.
</prompt>
</block>
<field name="field1" type="boolean">
<prompt>
Sie befinden sich jetzt im Field Element.
Erst hier wird die Eingabe von 'ja' oder 'nein' definiert.
Wenn Sie Barge In genutzt haben, dürften Sie dieses Prompt nicht hören.
</prompt>
<filled>
. . .
</filled>
</field>
</form>
</vxml>
Bevor Prompts gespielt werden, muß der Interpreter darüber hinaus auch die Konditionen cond und
count berücksichtigen, und einzelne oder mehrere Prompts nicht abspielen.
5 Beeinflussung des Dialogblaufs
Die Abarbeitung eines VoiceXML Dokumentes ist in der VoiceXML Spezifikation vorgegeben. Der
Form Interpretation Algorithm (FIA) steuert den Ablauf einer jeden Form. Jedes Tag besitzt ein name Attribut,
welches im undefinierten Fall ein noch auszuführendes Tag kennzeichnet. Der FIA setzt dieses Attribut, nachdem das Tag abgearbeitet wurde.
Indem man dem Attribut einen Namen gibt, erhält man selbst Kontrolle über den Ablauf, denn durch die Tags
<assign> und <clear> kann man Attribute nach Belieben setzen und löschen.
Ein Beispiel für die Steuerungskontrolle des FIA ist der Mixed Initiative Dialog.
<initial>
Von wo nach wo möchten Sie fliegen ?
<initial>
<field name="abflug">
<prompt>
Von wo möchten Sie abfliegen ?
</prompt>
. . .
</field>
<field name="ankunft">
<prompt>
Wohin möchten Sie fliegen ?
</prompt>
. . .
</field>
Die Slots 'abflug' und 'ankunft' werden in Abhängigkeit von der Aussage des Anrufers gefüllt. Der FIA setzt dabei entsprechend die
name Attribute der Field Tags.
Sagt der Anrufer z.B. nur 'von Hamburg', so wird nur das jeweilige Field 'abflug' gesetzt, das Field 'ankunft' bleibt undefiniert.
Der FIA erkennt, daß das zweite Field 'ankunft' noch undefiniert ist, und führt dieses separat aus.
<assign>
<assign> weist nicht nur Variablen, sondern auch Tags mit einem name Attribut
einen Wert zu, so daß diese übergangen wird.
Beispiel:
<block>
<assign name="field1" expr="'gefuellt'"/>
<block>
<field name="field1">
<prompt>
Wird nicht ausgeführt
</prompt>
. . .
</field>
<clear>
<clear> ist vergleichbar mit einem <goto nextitem=>, also dem Sprung
innerhalb einer Form.
Beispiel:
<form>
<block name="test1">
Hallo
<block>
<block name="test2">
Herzlich Willkommen.
<!-- Test1 nochmal ausführen -->
<clear namelist="test1"/>
. . .
</block>
</form>
<block>
<block> ist ein Tag zum Kapseln von VoiceXML Tags, bei denen keine Eingabe erforderlich ist.
Unter Verwendung von
<assign>,
<clear>,
<goto>,
<submit>,
<if> innerhalb eines Blocks kann man den Ablauf eines Dialoges steuern.
<goto>
<goto> wird hauptsächlich zum Navigieren zwischen Forms verwendet, da dort der FIA nicht greift.
Wie bei <clear> kann <goto> aber auch zum Sprung innerhalb einer Form verwendet werden.
Frage:
In Welcher Reihenfolge wird der folgende Dialog abgearbeitet ?
<form>
<block name="eins"> <goto nextitem="drei"/> </block>
<block name="zwei"> ... </block>
<block name="drei"> <goto nextitem="vier"/> </block>
<block name="vier"> ... </block>
<block name="fuenf"> ... </block>
</form>
Ergebnis: Block eins => Block drei => Block vier => Block zwei => Block fuenf
Der Sprung von Block1 nach Block3 bedeutet nicht, daß Block2 übersprungen wird, Block2 wird lediglich später ausgeführt.
Unter Verwendung von <clear> ist eine Endlosschleife sehr einfach realisierbar (wenn auch absolut sinnlos):
<form>
<block name="eins"> <goto nextitem="drei"/> </block>
<block name="zwei"> <clear namelist="eins"/> </block>
<block name="drei"> ... </block>
</form>
6 <record>
Das <record> Element dient, wie der Name schon andeutet, zur Aufnahme. Als einfaches Beispiel für die Verwendung von
<record> sei hier ein Anrufbeantworter genannt, der Aufnahmen speichert und bei Bedarf wieder abspielt. Oftmals wird
<record> mit dem Mitschneiden einer Spracherkennung verwechselt. Dem ist nicht so: <record> und <field> sind in VoiceXML verschiedene, voneinander unabhängige Elemente. Das Mitschneiden einer Eingabe
funktioniert nur über die plattforminternen Loggingmechanismen, oder aber bei Verwendung von VoiceXML 2.1 ().
Das <record> Element ist ein Field item und wird wie folgt verwendet.
<form id ="Aufnahme">
<record name="message" beep="true" maxtime="30s" finalsilence="4000ms" dtmfterm="true" type="audio/x-wav">
<prompt timeout="3s">
Ich bin derzeit nicht zuhause, hinterlassen Sie einfach eine Nachricht nach dem Ton.
</prompt>
<filled>
<submit next="..." enctype="multipart/form-data" method="post" namelist="message"/>
</filled>
</record>
</form>
Die <record>-Parameter im Einzelnen:
Mit dem Parameter beep kann man angeben, ob vor dem Start der Aufnahme ein Piepton (vergleichbar zum Anrufbeantworter) gespielt wird. Dieser ist im System hinterlegt und kann nicht verändert werden. Tipp: Möchten Sie einen alternativen Sound vewernden, dann setzen sie beep='false' und bauen Sie einen eigenen Piepton direkt in Ihr Audioprompt ein.
Mit dem Parameter maxtime wird die maximale Aufnahmezeit -ab dem beep- bestimmt.
Sagt der Anrufer nichts mehr, wird die Aufnahme nach der in finalsilence eingestellten Zeit endgültig beendet.
Über dtmfterm wird die Gelegenheit gegeben, die Aufnahme mit einer Taste abzuschliessen.
Eine Aufnahme endet, wenn
- ein Fehler aufgetreten ist (auch auflegen)
- eine passende DTMF- oder Spracheingabe erkannt wurde
- der mit maxtime eingestellte Wert erreicht wurde
- nach der mit finalsilence angegeben Stille, nachdem der Anrufer nichts mehr gesagt hat.
Abschliessend noch ein Beipiel für eine Aufnahme incl. Bestätigungsdialog:
<form id ="Aufnahme">
<record name="message" beep="true" maxtime="30s" finalsilence="4000ms" dtmfterm="true" type="audio/x-wav">
<prompt timeout="3s">
Ich bin derzeit nicht zuhause, hinterlassen Sie einfach eine Nachricht nach dem Ton.
</prompt>
</record>
<field name="confirm">
<grammar type="application/srgs+xml" src="/grammars/boolean.grxml"/>
<prompt>
Ihre Nachricht lautet: <value expr="message"/>
Zum Speichern sagen Sie ja, ansonsten sagen Sie nein.
</prompt>
<filled>
<submit next="..." enctype="multipart/form-data" method="post" namelist="message"/>
</filled>
</record>
</form>
Shadow Variablen eines <record> Elementes:
Variable |
Beschreibung |
name$.duration |
Die Länge der Aufnahme in Millisekunden.
|
name$.size |
Die Grösse der Aufnahme in Bytes.
|
name$.termchar |
War dtmfterm gesetzt, und die Eingabe wurde mit einer Taste abgeschlossen, so enthält diese Variable die gedrückte Taste. (z.B. #).
|
name$.maxtime |
Ja/Nein Ausdruck, ob bei der Aufnahme die mit maxtime eingestellte Zeit erreicht wurde.
|
7 ASR-spezifische Themen
7.1 Confidence level
Jedes <field> mit name Attribut existieren sogenannte
Shadow Variablen <name>$:
Variable |
Beschreibung |
name$.utterance |
Das tatsächliche Ergebnis des Spracherkenners.
|
name$.inputmode |
Der Modus der Eingabe: dtmf or voice.
|
name$.interpretation |
Die in der Grammatik festgelegte Interpretation für die Eingabe.
|
name$.confidence |
Der Confidence level zeigt an, wie sicher sich der Spracherkenner bei seiner Ergebnisfindung war.
Bereichswerte liegen zwischen 0.0 und 1.0
|
Mittels name$.confidence kann man sich für jedes vom Spracherkenner zurückgelieferte Ergebnis darstellen lassen,
wie "sicher" sich der Spracherkenner war,
d.h. wie hoch die Wahrscheinlichkeit ist, dass das Erkennungsergebnis mit dem tatsächlich gesagten übereinstimmt.
Der zurückgelieferte Wert für confidence liegt immer zwischen 0.0 und 1.0
VoiceXML Plattformen müssen ein globales Property confidencelevel unterstützen.
Der Startwert ist plattformspezifisch (meist um 0.5) und kann jederzeit verändert werden.
Als allgemeine Empfehlung sollte man Werte <0.4 als "nicht erkannt" interpretieren, Werte >0.7 kann man getrost als "erkannt" werten,
alle Werte zwischen 0.4 und 0.7 sollten bestätigt (nachgefragt) werden. Diese Werte sind jedoch stark abhängig von der
verwendeten Anwendung und liegen somit im Ermessensspielraum des Entwicklers.
7.2 N-Best Spracherkennung
VoiceXML unterstützt mittlerweile auch die sogenannte N-Best Erkennung. Bei Verwendung dieser Erkennungsvariante wird nicht
nur ein einzelnes Ergebnis zurückgeliefert, sondern eine ganze Liste möglicher Ergebnisse inklusive deren Wahrscheinlichkeiten.
Die Ergebnisse werden in absteigender Reihenfolge zurückgegeben, d.h. das wahrscheinlichtse Ergebnis zuerst, und danach die
alternativen Ergebnisse mit fallender Wahrscheinlichkeit.
Die Anzahl der maximalen Interpretationen wird über den Plattformparameter maxnbest angegeben, der Standardwert ist 1.
Zur Übermittlung der Ergebnisse wird ein Array verwendet. Diese Array heisst application.lastresult$ und besitzt
konsequenterweise die gleichen Bestandteile wie die Shadowvariablen des <field> Elementes.
- application.lastresult$[i].utterance
- application.lastresult$[i].inputmode
- application.lastresult$[i].interpretation
- application.lastresult$[i].confidence
Im Falle von maxnbest > 1 liefert der Spracherkenner ein Array beginnend mit dem Startindex [0] zurück, absteigend sortiert
nach dem Confidence level.
Die Vorteile der N-Best Erkennung liegen auf der Hand:
Bei mehreren Interpretationen kann die Applikationslogik "unsinnige" Ergebnisse ausblenden, oder aber gewollte Mehrfacheinträge
identifizieren (zum Beispiel das mehrmalige Auftauchen des Namens Müller in einem Adressbuch).
Ein mögliches Ergebnis einer Adressbuchgrammatik könnte bei Eingabe des Namens "Müller" vielleicht so aussehen:
- application.lastresult$[0].interpretation = "Müller, Franz"
- application.lastresult$[1].interpretation = "Mueller, Sabine"
- application.lastresult$[2].interpretation = "Müller, Werner"
Da die Anzahl der Interpretationen vorab nicht zwingend feststeht, wird mit application.lastresult$.length die Anzahl
der tatsächlich generierten Ergebnisse zurückgeliefert. Diese Zahl kann durchaus kleiner sein als der mit maxnbest definierte Wert.
Die Anwendung kann jetzt entsprechen reagieren:
<prompt>
Ich habe <value expr="application.lastresult$.length"/> Ergebnisse gefunden. Suchen Sie...
</prompt>
|