Sprachfähige Apps entwickeln – Teil 1: Cortana Integration

 

Cortana ist Microsofts persönliche Assistentin, die den Nutzer bei verschiedenen Anforderungen und Aufgaben unterstützt. Sie wurde im Dezember 2014 in Deutschland angekündigt und befindet sich noch in der Alpha-Phase. Neben einer Vielzahl an Funktionen, bietet Cortana auch die Möglichkeit, eigene Apps einzubinden.
Diese Blogserie zeigt, wie man sprachfähige Anwendungen mit Cortana und der Speech SDK entwickeln kann.

Ich werde nun anhand von einer Sample App in diesem ersten Blogartikel zeigen, wie man Schritt für Schritt eine Anwendung mit Cortana erweitern kann. In diesem Fall überarbeite ich eine App namens GasFinder, die ich vor kurzem implementiert habe. Die App ermöglicht das Suchen und Finden von günstigen Tankstellen in der Nähe des Nutzers. Es handelt sich jedoch nur um eine Beispiel-App, die keine realen Daten enthält, sondern nur exemplarisch immer eine erfundene Tankstelle als Ergebnis zurückgibt. Das Projekt ist auch auf github verfügbar.

Für die Integration eigener Apps mit Cortana muss man hauptsächlich drei Schritten folgen:

1. Eine Voice Command Definition (VCD) XML-Datei erstellen, die die Befehle enthält.
2. Die VCD XML-Datei bei der App Inbetriebnahme registrieren.
3. Die Voice Commands entsprechend bearbeiten, falls die App mittels Cortana aktiviert wurde.

Als Erstes müssen wir also eine XML-Datei erstellen, die die Sprachbefehle enthält. Diese VoiceCommandDefinition-Datei kann aus einem oder mehreren CommandSets für unterschiedliche Sprachen bestehen. Für diese Demo habe ich nur ein Set für die deutsche Sprache erstellt. Dieses enthält die Tags <CommandPrefix>, <Example>, <Command> und mehrere <PhraseList>s.

Als <CommandPrefix> kannst du den Namen deiner Anwendung oder eine Kurzform davon eingeben. Der Nutzer wird später damit deine Anwendung starten können. Da meine App einen englischen Namen hat, habe ich den Prefix "Sprit Sucher" gegeben.

<Example> repräsentiert ein Beispiel auf der obersten Ebene, wie man mit deiner App interagieren kann. Dieses Beispiel erscheint auch, falls du Cortana "Was kann ich sagen?" fragst (siehe Bild unten).

waskannichsagen

<Command> beschreibt einen Befehl und enthält ein weiteres Beispiel: <Example>, gefolgt von dem was der Nutzer zu Cortana sagen kann: <ListenFor>, wie Cortana darauf antwortet soll: <Feedback> und welche Aktion sie ausführen soll: <Navigate>.

<ListenFor> kann mehrere Ausdrücke enthalten. Diese können darüber hinaus auch <PhraseTopic> und <PhraseList> enthalten.

Ich habe die XML-Datei "VCD" benannt und mit nur einem Befehl namens "NextStation" erweitert. Um unterschiedliche Varianten abzudecken, habe ich <PhraseList> verwendet. Zum Beispiel könnte der Nutzer statt "finde" auch "suche" oder "vermittle" sagen und verstanden werden.

 <?xml version="1.0" encoding="utf-8"?>

<VoiceCommands xmlns="https://schemas.microsoft.com/voicecommands/1.1">
 <CommandSet xml:lang="de-DE">
  <CommandPrefix>Sprit Sucher</CommandPrefix>
  <Example> wo ist die nächste Tankstelle </Example>

  <Command Name="NextStation">
   <Example> wo ist die nächste Tankstelle </Example>
    <ListenFor> wo {ist} die nächste Tankstelle</ListenFor>
    <ListenFor> wo {ist} die nächste Tankstelle mit [billigem] {Sprit} </ListenFor>
    <ListenFor> {finde} die nächste Tankstelle </ListenFor>
    <ListenFor> {finde} die nächste Tankstelle mit [billigem] {Sprit} </ListenFor>
    <ListenFor> {finde} die nächste Tankstelle mit günstigem {Sprit} </ListenFor>
    <ListenFor> {finde} die günstigste Tankstelle [in meiner Nähe] </ListenFor>
    <Feedback> Suche Tankstelle... </Feedback>
    <Navigate />
   </Command>
    
   <PhraseList Label="ist">
     <Item> befindet sich </Item>
     <Item> finde ich </Item>
     <Item> gibt es </Item>
   </PhraseList>

   <PhraseList Label="finde">
     <Item> suche </Item>
     <Item> finde </Item>
     <Item> vermittle </Item>
     <Item> entdecke </Item>
   </PhraseList>

   <PhraseList Label="Sprit">
     <Item> Benzin </Item>
     <Item> Sprit </Item>
     <Item> E10 </Item>
     <Item> Super </Item>
     <Item> Super Plus </Item>
     <Item> Diesel </Item>
   </PhraseList>

 </CommandSet>
</VoiceCommands>

Um die erstellte XML-Datei beim Starten der App zu registrieren, muss man folgende Methode verwenden: await VoiceCommandManager.InstallCommandSetsFromStorageFileAsync(file); wobei file die VCD Datei ist. Dafür habe ich eine Methode RegisterVoiceCommands() im App.xaml.cs erstelllt, die ich dann in der OnLaunched() -Methode aufrufe.

 private async void RegisterVoiceCommands()
{
     Uri uriVoiceCommands = new Uri("ms-appx:///vcd.xml", UriKind.Absolute);
     StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uriVoiceCommands);
     try
     {
    await VoiceCommandManager.InstallCommandSetsFromStorageFileAsync(file);
     }
  catch (Exception ex)
    {
    string errorMessage = String.Format(
         "An error was encountered installing the Voice Command Definition file: \r\n {0:x} \r\n {1}",
         ex.HResult,
         ex.Message);
         Debug.WriteLine(errorMessage);
    }
}

In OnActivated werden dann die Befehle bearbeitet. Man kann entweder abhängig vom Befehl auf unterschiedliche Seiten der App navigieren oder immer auf eine bestimmte Seite navigieren und da die Befehle bearbeiten. Die letzte Variante ist auch die Vorgehensweise für unsere Demo-App. Wir prüfen erstmal, ob die Anwendung mittels Cortana aktiviert wurde indem wir schauen, ob ActivationKind gleich VoiceCommand ist und springen dann zur MainPage.

 protected override void OnActivated(IActivatedEventArgs args)
{
     CommonInitialize();
 
    Frame rootFrame = Window.Current.Content as Frame;
    Type navType = typeof(MainPage);

    if (args.Kind == ActivationKind.VoiceCommand)
    {
      VoiceCommandActivatedEventArgs vcArgs = (VoiceCommandActivatedEventArgs)args;
      Debug.Assert(rootFrame.Navigate(navType, vcArgs.Result), 
                "Failed to create initial page");
    }
     // Ensure the current window is active
     Window.Current.Activate();
 }

Somit sind wir mit der Integration von Cortana fertig. Diese wird nun die App GasFinder starten, sobald ein geeigneter Befehl erkannt wird. In den nächsten Blogartikeln werden ich euch vorstellen, wie man zwischen Sprach- und Text-Befehle unterscheiden kann und wie man den Dialog innerhalb der App  mittels der Speech SDK weiterführen kann.