Ein neuer KI-Assistent sollte her — und das Team arbeitete hauptsächlich mit .NET. Die Herausforderung? Einen funktionsfähigen Assistenten entwickeln, der nicht nur auf Hype setzt, sondern praxistauglich ist. Die Lösung erforderte klare Architektur, Abstraktionen und die richtige Transportmethode für Echtzeit-Interaktionen.
Vom Hype zur praktischen Lösung: Warum Abstraktionen unverzichtbar sind
Die erste Lektion kam schneller, als erwartet: Ein KI-Assistent darf nicht an eine bestimmte Technologie oder einen Anbieter gebunden sein. Die Angst vor vendor lock-in war berechtigt. Viele AI-Frameworks ändern ihre Architektur rasant — und plötzlich steht man vor einem großen Refactoring.
Daher setzte das Team auf abstrakte Schnittstellen. Das .NET-Ökosystem bietet mittlerweile passende Tools: Die Bibliothek `Microsoft.Extensions.AI` übernimmt die Rolle eines Vermittlers. Sie stellt generische Interfaces bereit, die unabhängig vom Anbieter funktionieren:
IChatClient– für Chat- und LLM-ZugriffIEmbeddingGenerator– für semantische SucheITextToSpeechClient– für SprachausgabeMicrosoft.Extensions.VectorData– für vektorielle Datenspeicher
Der Vorteil: Der eigentliche Anbieter (z. B. OpenAI) wird erst bei der Registrierung im Dependency Injection-Container festgelegt. Das bedeutet maximale Flexibilität, falls sich die Landschaft ändert. Der folgende Code zeigt, wie einfach die Integration gelingt:
// Registrierung des Anbieters im DI-Container
builder.Services.AddKeyedSingleton<IChatClient>("CoriAI", (sp, _) =>
sp.GetRequiredService<OpenAIClient>()
.GetChatClient("gpt-4o-mini")
.AsIChatClient()
.AsBuilder()
.UseFunctionInvocation()
.Build());
// Nutzung der Schnittstelle in der Anwendung
public sealed class Summarizer([FromKeyedServices("CoriAI")] IChatClient chat) {
public async Task<string> OneLiner(string topic, CancellationToken ct) {
var reply = await chat.GetResponseAsync(
$"Erkläre {topic} in einem Satz.",
cancellationToken: ct);
return reply.Text;
}
}Warum Semantic Kernel keine langfristige Lösung war
Die ersten Versuche basierten auf Semantic Kernel, einer damals vielversprechenden Bibliothek von Microsoft. Doch schnell zeigte sich: Die APIs waren noch nicht ausgereift und wurden später durch neue Frameworks wie Microsoft Agent Framework ersetzt.
Das Ergebnis? Ein Großteil des Codes musste neu geschrieben werden — ein teures und unnötiges Unterfangen. Die Erfahrung unterstrich, wie wichtig Abstraktionen von Anfang an sind. Wer heute auf experimentelle Bibliotheken setzt, riskiert morgen eine vollständige Überarbeitung.
Transportprobleme: Warum SignalR allein nicht reicht
Der nächste große Knackpunkt war der Transport der KI-Ausgabe zum Client. Der Assistent generiert zwei Arten von Ausgaben:
- Semantische Ausgabe (Text, Tool-Aufrufe, Statusänderungen)
- Audio-Ausgabe (Sprachsynthese in Echtzeit)
Ein reiner SignalR-Ansatz schien naheliegend, doch er deckte nur einen Teil der Anforderungen ab. SignalR eignet sich zwar für Text- und Status-Updates, stößt aber an Grenzen, wenn es um Streaming-Audio oder komplexe Tool-Aufrufe geht.
Die Lösung? Eine hybride Architektur, die SignalR mit anderen Mechanismen kombiniert. Für semantische Daten wurde SignalR genutzt, während Audio über WebSockets oder spezielle Audio-Streaming-Dienste übertragen wurde. Der Client erhielt so eine nahtlose, aber differenzierte Erfahrung.
Funktionierende Tool-Aufrufe: Der Schlüssel zur Interaktivität
Der KI-Assistent sollte nicht nur antworten, sondern auch Aktionen ausführen können — etwa das Drehen eines 3D-Modells oder das Abrufen von Inhalten. Dafür setzte das Team auf Funktionsaufrufe (Function Calling).
Die Funktionsweise ist simpel: Die KI fragt nach einer Funktion (z. B. Rotate), und das Backend führt diese aus. Wichtig war, dass die KI nicht selbst Code ausführt, sondern nur die Anfrage stellt. Das Backend interpretiert die Anfrage und führt die entsprechende Aktion aus.
Ein Beispiel für die Implementierung:
// Definition der verfügbaren Funktionen
var availableFunctions = new Dictionary<string, Delegate>
{
{ "Rotate", new Action<string, int>(RotateModel) },
{ "SearchContent", new Func<string, string>(SearchContent) }
};
// Integration in den KI-Client
builder.Services.AddKeyedSingleton<IChatClient>("CoriAI", (sp, _) =>
sp.GetRequiredService<OpenAIClient>()
.GetChatClient("gpt-4o-mini")
.AsIChatClient()
.AsBuilder()
.UseFunctionInvocation(availableFunctions)
.Build());Tokens und Streaming: Warum die Antwort nicht auf einmal kommt
Ein oft unterschätztes Detail ist das Token-Streaming. KI-Modelle senden Antworten nicht als fertigen Block, sondern in kleinen Teilen. Wer diese Teile sofort an den Client weiterleitet, erzielt eine flüssigere Nutzererfahrung — statt einer unnatürlichen Pause.
Die Implementierung erforderte eine asynchrone Verarbeitung der Tokens. Der Client erhielt so eine Live-Typing-Wirkung, die den Dialog natürlicher wirken ließ. Ein einfaches Code-Beispiel zeigt, wie das funktioniert:
var chatClient = serviceProvider.GetRequiredKeyedService<IChatClient>("CoriAI");
var response = await chatClient.GetResponseStreamingAsync(
"Erkläre die Funktion des Herzens in einem Satz.");
await foreach (var chunk in response) {
await SendToClient(chunk.Text); // Stream an Client senden
}Lessons Learned: Was bleibt und was man besser macht
Die Entwicklung des KI-Assistenten war ein Lernprozess mit vielen Umwegen. Drei zentrale Erkenntnisse prägen heute die Architektur:
- Abstraktionen sind kein Luxus, sondern Pflicht – Wer früh auf generische Schnittstellen setzt, spart später Zeit und Nerven.
- SignalR allein reicht nicht aus – Echtzeit-Interaktionen erfordern oft eine Kombination mehrerer Technologien.
- Experimentelle Frameworks meiden – Wer auf noch nicht ausgereifte Bibliotheken setzt, riskiert teure Nacharbeiten.
Die größte Überraschung? Dass .NET mittlerweile eine solide Basis für KI-Anwendungen bietet — wenn man weiß, wie man die Tools richtig einsetzt. Der Ausbau der Microsoft.Extensions.AI-Bibliothek zeigt, dass Microsoft die Bedeutung von .NET in der KI-Welt erkannt hat.
Fazit: Der Weg zu einem stabilen KI-Assistenten
Ein KI-Assistent ist mehr als nur ein Chatbot — er muss handeln, reagieren und kommunizieren können. Die Entwicklung eines solchen Systems erfordert klare Architekturentscheidungen, die richtige Transportmethode und eine flexible Integration des KI-Anbieters.
Wer heute in .NET eine KI-Lösung baut, sollte Abstraktionen priorisieren, auf ausgereifte Bibliotheken setzen und Streaming-Techniken nutzen, um eine flüssige Nutzererfahrung zu schaffen. Die Technologie entwickelt sich rasant — aber mit der richtigen Grundlage bleibt der Code auch in zwei Jahren noch wartbar.
Die Reise ist noch nicht zu Ende. Doch mit den gewonnenen Erkenntnissen ist das Team besser vorbereitet auf die nächsten Herausforderungen der KI-Integration.
KI-Zusammenfassung
C# ve .NET kullanarak AI asistanı geliştirmenin gerçek zorlukları ve pratik çözümleri. Soyutlama katmanları, SignalR entegrasyonu ve kod örnekleriyle adım adım rehber.