Dieser Beitrag setzt das Hackathon-Thema fort und zeigt die technische Umsetzung von Sprachbefehlen in .NET MAUI. Außerdem geht es um die Herausforderungen, denen das Entwicklungsteam begegnet ist, und wie diese erfolgreich gelöst wurden.
Nachdem verschiedene Ideen aus dem Team diskutiert worden waren, entschieden wir uns für die Idee, einen sprachgesteuerten Assistenten - im Folgenden einfach „Assistent“ genannt - zu entwickeln, der Informationen über gegessene Speisen und getrunkene Getränke in natürlicher Sprache erfassen kann.
In diesem Blogartikel möchte ich zeigen, wie das Entwicklungsteam dabei vorgegangen ist und welche Schritte für die Business-Logik und die Visualisierung der Anwendung nötig waren.
Zunächst mussten wir ein konzeptionelles Modell erstellen, um besser zu verstehen, wie das Programm funktionieren soll. Grundsätzlich sollte der Ablauf wie folgt aussehen:
Der Assistent stellt dem Nutzer eine Frage.
Der Nutzer antwortet in natürlicher Sprache.
Die gesprochene Antwort wird in Text umgewandelt und analysiert.
Auf Basis der Analyse wird die Antwort des Assistenten formuliert und wieder ausgegeben.
Auf Basis dieses Modells wurde ein Flussdiagramm erstellt, das den Ablauf im Detail beschreibt:
Wie das Flussdiagramm zeigt, lässt sich die Kommunikation mit dem Assistenten in drei Phasen unterteilen:
Der Nutzer erzählt frei, was er gegessen oder getrunken hat und in welcher Menge.
Falls Mengenangaben fehlen, stellt der Assistent Rückfragen zu einzelnen Zutaten.
Wenn alle Zutaten Mengen besitzen, fragt der Assistent, ob noch weitere Mahlzeiten hinzukommen.
Assistent einrichten und Datenmodell erstellen
Die größte Schwierigkeit bei dieser Art von Anwendung besteht darin, den Text des Nutzers zu analysieren und daraus strukturierte Datenmodelle zu erzeugen. Gleichzeitig wollten wir den Nutzer nicht in ein bestimmtes Format zwingen. Er sollte seine Mahlzeiten so beschreiben können, wie es für ihn natürlich ist. Für genau diesen Zweck bietet sich KI besonders gut an. Schauen wir uns an, wie die Initialisierung des GPT-Chats aussieht, den wir für diese Aufgabe gewählt haben:
Anschließend wurde die folgende Datenmodell-Struktur aufgebaut:
public class Meal
{
[JsonPropertyName("error")]
public string Error { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("ingredients")]
public List<Ingredient> Ingredients { get; set; }
}
public class Ingredient
{
[JsonPropertyName("n")]
public string Name { get; set; }
[JsonPropertyName("a")]
public string Amount { get; set; }
[JsonPropertyName("m")]
public string Measurement { get; set; }
[JsonPropertyName("c")]
public string Category { get; set; }
[JsonPropertyName("p")]
public bool IsNatural { get; set; }
}
public class IsEndResponse
{
[JsonPropertyName("hasMore")]
public bool HasMore { get; set; }
}
Das Ergebnis der Kommunikation mit dem Assistenten soll ein Meal-Array sein, das Zutaten mit Name, Menge, Kategorie und Einheit enthält.
Freitext in ein Datenmodell parsen
Jetzt kam vermutlich der spannendste Teil: die GPT-Chat-Konfiguration so zu definieren, dass freie Sprache zuverlässig in strukturierte Daten überführt wird. Aus dem Flussdiagramm ergeben sich dafür drei Methoden:
public async Task<Meal> ParseMeal(string userText)
public async Task<Ingredient> GetAmount(string userText, string name)
public async Task<bool> IsEnd(string userText)
Beispielhaft sieht der Body von ParseMeal so aus:
string assistantRequest = "?";
public async Task<Meal> ParseMeal(string userText)
{
var chat = _chatCompletion.CreateNewChat(assistantRequest);
chat.AddUserMessage(userText);
var answer = await _chatCompletion.GenerateMessageAsync(chat);
return JsonSerializer.Deserialize<Meal>(answer);
}
Besonders spannend ist dabei der Inhalt der Variablen assistantRequest, denn genau dieser Prompt konfiguriert den Chat-Client. Für die drei Methoden sahen die Konfigurationen so aus:
ParseMeal:
"You are a language assistant who can filter out from a sentence which foods have been eaten and in what quantities.
were eaten. From the information you create a JSON in the following form (everything in <> are placeholders).
Use common abbreviations for units of measurement. Where no amount is entered, omit the amount and measurement value.
Organize each ingredient into the following categories food, drink.
If you cannot find an ingredient, fill in the error field with your answer.
Classify each ingredient as to whether it is a natural product and put it in p as a bool:
{
\"error\": <Error message>,
\"ingredients\": [
{
\"n\": \"<ingredientname>\",
\"a\": \"<amount>\",
\"m\": \"<measure>\",
\"c\": \"<category>\",
\"p\": \"<naturalproduct>\"
}
]
}"
GetAmount:
"The measurement units can be filtered out and this information returned in JSON with the following structure.
One of the two specifications is also sufficient:
{
\"a\": \"<amount>\",
\"m\": \"<measure>\"
}"
IsEnd:
"You are a voice assistant and recognize from the message whether the answer contains more information
or is negating and return it in the following JSON form with hasMore = false if the answer is negating,
otherwise hasMore = true:
{
hasMore: <value>
}"
Durch diese klaren Prompt-Vorgaben definieren wir also die Antwortstruktur in JSON so präzise, dass wir später mit Deserialisierung direkt die benötigten Objekte daraus erzeugen können.
Voice-to-Text und umgekehrt
Sprache in Text umwandeln
Der nächste Schritt bestand darin, Sprache in Text und Text wieder in Sprache zu überführen. Glücklicherweise ist das .NET MAUI Community Toolkit sehr umfangreich und bietet eine Speech-to-Text-API. Damit lassen sich gesprochene Worte in Text umwandeln, was auf iOS, Android, Mac und Windows genutzt werden kann. Ein Beispiel für die Verwendung dieses Services:
Zusätzlich haben wir einen Timer mit drei Sekunden Verzögerung eingebaut, nach dessen Ablauf die Aufnahme gestoppt wird. Bei erfolgreicher Erkennung wird der resultierende String über den Delegate Action<string> progress weiterverarbeitet und in RecognitionText geschrieben.
Die Kommunikation mit dem Assistenten sollte sich für Nutzer möglichst natürlich anfühlen. Deshalb haben wir entschieden, den Dialog mit dem Assistenten wie eine Unterhaltung mit einem anderen Menschen zu gestalten.
Dafür wurden in SystemMessageTemplate, OutgoingMessageTemplate, ResultMessageTemplate und MessageDataTemplateSelector drei Nachrichtentypen definiert, die je nach Typ automatisch das passende Template in einer CollectionView verwenden. Die Templates hängen wiederum von den Eigenschaften MessageType und MealType des MessageViewModel ab.
Für das Entwicklungsteam war es eine echte Herausforderung, die Komfortzone zu verlassen, Neues auszuprobieren, eine passende Architektur zu entwerfen und in nur drei Tagen einen funktionierenden Prototypen zu bauen. Als Ergebnis haben wir wertvolle Erfahrung in der Integration künstlicher Intelligenz in Anwendungen gesammelt - und gleichzeitig erlebt, wie produktiv und angenehm ein starkes Team in kurzer Zeit zusammenarbeiten kann.
FAQ
Für Spracheingabe und Textausgabe lassen sich in .NET MAUI unter anderem die Speech-to-Text- und Text-to-Speech-Funktionen des .NET MAUI Community Toolkit bzw. von Essentials nutzen. Die erkannte Sprache wird in Text umgewandelt, analysiert und anschließend wieder in gesprochene Antworten überführt.
Im gezeigten Ansatz übernimmt ein GPT-basierter Chat-Dienst das Parsing. Über klar formulierte Prompts wird freie Sprache in ein definiertes JSON-Format überführt, das anschließend in Datenmodelle deserialisiert wird.
Die KI analysiert natürlich formulierte Aussagen zu Mahlzeiten, erkennt Zutaten, Mengen und Kategorien und hilft so dabei, freie Sprache in eine strukturierte Anwendungslogik zu überführen.
Igor Gridin
Mit über 15 Jahren Erfahrung in der Softwareentwicklung mit Java und Kotlin unterstütze ich im Banken- und Logistik-Bereich unsere Kunden bei spannenden nativen Android-Projekten. Hier schreibe ich über alle neuen Technologien und Trends, die im Bereich der Android-Anwendungsentwicklung auftauchen.
In diesem Artikel lernst du, wie du Lottie-Animationen in .NET MAUI integrierst und sie mit Gesten, Scroll-Positionen und CarouselViews verknüpfst. Du erfährst, wie Animationen per Tap, durch Tippen & Halten, über Scroll-Interaktionen sowie beim Wechseln von CarouselView-Seiten gesteuert werden. Zusätzlich bekommst du komplette XAML- und C#-Beispiele, Best Practices und fertige Demo-Videos, um interaktive und moderne UI-Erlebnisse in deiner MAUI-App umzusetzen.
Als Mobile-App-Entwickler:innen müssen wir ständig Informationen zwischen App und Backend austauschen. In den meisten Fällen ist eine RESTful-API die Lösung. Aber was, wenn ein konstanter Datenfluss in beide Richtungen benötigt wird? In diesem Beitrag schauen wir uns MQTT an und wie man eine einfache Chat-App in .NET MAUI erstellt.
Ich arbeite derzeit an der Portierung einer Xamarin Forms App zu .NET MAUI. Die App verwendet auch Karten von Apple oder Google Maps, um Standorte anzuzeigen. Obwohl es bis zur Veröffentlichung von .NET 7 keine offizielle Unterstützung in MAUI gab, möchte ich Ihnen eine Möglichkeit zeigen, Karten über einen benutzerdefinierten Handler anzuzeigen.