PROJEKTEINBLICK ·

Mein Assistent sollte mehr können als antworten

Mein Assistent konnte inzwischen zuhören und sprechen. Jetzt wollte ich mit meiner Stimme auch etwas auslösen. Aus den ersten einfachen Befehlen wurde eine Intent-Pipeline, die mit jeder Idee weiterwuchs. Dabei musste ich mir immer wieder überlegen, wie viel Freiheit ich meinem Assistenten eigentlich geben wollte.

KONZEPT · UMSETZUNGOliver Röschner
LESEZEIT5 Minuten
BEREICHTECHNOLOGIE
Kupferne Signalwege führen auf einer schwarzen Routingtafel kontrolliert zu einzelnen Werkzeugen
KI GENERIERTES ARTIKELBILD | OPERUS

Die ersten einfachen Befehle

Zunächst waren es einfache Dinge: den Taschenrechner öffnen, den Browser starten oder eine bestimmte Webseite aufrufen. Ich sagte, was ich wollte, und mein Rechner tat es. Nach all der Arbeit an der Sprachverarbeitung war das der nächste Moment, in dem sich das Projekt ein Stück mehr nach meinem eigenen Assistenten anfühlte.

Natürlich blieb es nicht bei diesen ersten Befehlen. Sobald etwas funktionierte, fiel mir die nächste Fähigkeit ein. Mit den Ideen wuchs auch die Intent-Pipeline: der Teil meiner Anwendung, der erkennen sollte, was ich vorhatte und welche Aktion dazu passte.

Intent-Pipeline · vereinfachter Ablauf text
„Öffne den Taschenrechner“
          ↓
Absicht erkennen: Anwendung öffnen
          ↓
Ziel bestimmen: Taschenrechner
          ↓
Passende Aktion auswählen und ausführen
          ↓
Der Taschenrechner öffnet sich

Ein paar Befehle waren schnell eingebaut. Eine Struktur zu finden, in der auch die nächsten Ideen noch vernünftig Platz hatten, beschäftigte mich deutlich länger.

Warum ich bei C# geblieben bin

Ich schrieb das Projekt in C#. Damit fühlte ich mich wohl, und ich kam schnell voran. Außerdem wollte ich Typsicherheit und klare Schnittstellen zwischen den einzelnen Teilen der Anwendung.

Mir war durchaus bewusst, dass es für vieles bereits Lösungen in Python gab. Trotzdem wollte ich diesen Weg gehen. Nach einigen Jahren als Softwareentwickler hatte ich ziemlich konkrete Vorstellungen davon, wie ich meine eigene Anwendung aufbauen wollte.

Sie sollte modular sein, mit klar getrennten Schichten und Zuständigkeiten. Die Spracherkennung, das Sprachmodell und die Sprachausgabe sollten sich austauschen lassen. Wenn ich ein anderes LLM ausprobieren wollte, wollte ich dafür nicht die halbe Anwendung umbauen müssen.

Gerade am Anfang war mir diese Freiheit wichtig. Ich wollte mit verschiedenen Bausteinen spielen können, bevor ich mich auf die Architektur festlegte. Was in einem ersten Versuch gut aussah, musste im täglichen Zusammenspiel noch lange nicht die richtige Entscheidung sein.

Meine eigenen Regeln machten Arbeit

Eine Zeilenzahl allein macht natürlich noch keinen sauberen Code. Für mich war sie aber ein Anlass, genauer hinzusehen: Gehören diese Aufgaben wirklich zusammen? Oder hat sich hier inzwischen etwas angesammelt, das eine eigene Zuständigkeit braucht?

Mit jeder Erweiterung stieß ich wieder an solche Fragen. Ich refactorierte entsprechend oft. Manchmal wollte ich eigentlich nur eine neue Fähigkeit einbauen und stellte dabei fest, dass ich zuerst die vorhandene Struktur überarbeiten musste.

Das kostete Zeit. Gleichzeitig konnte ich durch die Trennung einzelne Teile besser testen und austauschen. Ein anderes Sprachmodell, eine andere TTS-Lösung oder eine andere Spracherkennung wurden dadurch zu Versuchen, die ich innerhalb meiner Anwendung machen konnte.

Ich musste mich also noch nicht bei jedem Experiment endgültig festlegen. Diese Möglichkeit war mir die zusätzliche Arbeit wert.

Das Sprachmodell kam am Ende

Eine weitere Entscheidung war mir besonders wichtig: Ich wollte nicht jede Eingabe automatisch an das LLM schicken.

Wenn ich einen klaren, bekannten Befehl aussprach, sollte die Anwendung ihn erkennen und direkt darauf reagieren. Für das Öffnen des Browsers wollte ich nicht erst auf eine generierte Antwort warten.

  • Zuerst bekannte Befehle mit Regeln erkennen.
  • Bei einem Treffer dem festgelegten Ausführungspfad folgen.
  • Reicht die direkte Erkennung nicht aus, das Sprachmodell als mögliche nächste Stufe nutzen.

Das fühlte sich bei eindeutigen Befehlen sehr gut an. Die Reaktion kam schnell, und ich konnte nachvollziehen, warum eine bestimmte Aktion ausgewählt worden war.

Es hatte allerdings einen Preis: Die Fähigkeiten musste ich selbst implementieren. Das Modell konnte mir diese Arbeit nicht abnehmen. Die eigentliche Aktion führte mein C#-Code aus, innerhalb der Möglichkeiten, die ich vorgesehen hatte.

Mein Assistent konnte also nicht plötzlich alles, nur weil ein LLM angeschlossen war. Jeder neue Handlungsspielraum musste erst entstehen. Dafür wusste ich bei den definierten Befehlen ziemlich genau, welchen Weg sie durch die Anwendung nahmen.

Dann kamen die Dateien

Browser öffnen und Webseiten aufrufen wurde irgendwann langweilig. Ich wollte ausprobieren, was sich noch sinnvoll mit der Stimme bedienen ließ, und begann, Dateisystemoperationen einzubauen.

Das war spannend. Gleichzeitig merkte ich schnell, dass ich hier ein Sicherheitsnetz brauchte. Sobald eine Anwendung mit meinen Dateien arbeiten kann, haben Missverständnisse eine andere Bedeutung als bei einer unpassenden Antwort im Gespräch.

Ich wollte die Fähigkeiten weiter ausbauen. Aber ich musste dabei auch überlegen, wo ihre Grenzen lagen und wie ich die Kontrolle behielt.

Mit jeder neuen Aktion wurde außerdem eine andere Frage dringlicher: Wie füge ich Fähigkeiten hinzu, ohne sie überall in der Anwendung zu verteilen? Und wie bekomme ich sie wieder heraus, wenn ich sie nicht mehr haben möchte?

Fähigkeiten bekamen einen eigenen Platz

Daraus entstand mein Skillsystem. Eine Fähigkeit sollte einen klaren Platz haben: welche Intents sie verarbeiten konnte und wie sie die zugehörige Aktion ausführte.

So konnte ich Fähigkeiten als eigene Bausteine hinzufügen oder wieder entfernen. Die Pipeline erkannte das Anliegen, und ein passender Skill kümmerte sich um die Ausführung.

Ein zentrales schwarzes System verbindet sich über getrennte Kupferleitungen mit wenigen Werkzeugmodulen
KI-generierte Illustration. Ein gemeinsamer Kern, getrennte Werkzeuge: Fähigkeiten wachsen als eigene Bausteine.
Skillsystem · vereinfachtes Beispiel text
Erkannter Auftrag
  Anwendung öffnen → Taschenrechner

Passender Skill: Anwendungen öffnen
  Zuständig für: das Öffnen von Programmen
  Benötigt:      den Namen des Programms
  Führt aus:     das gewünschte Programm starten

Andere Fähigkeiten → eigene Skills
  Webseite aufrufen → Browser-Skill
  Datei suchen      → Datei-Skill

Für mein wachsendes Projekt war das ein wichtiger Schritt. Neue Ideen hatten jetzt einen Ort, an dem ich sie umsetzen konnte. Ich musste bei Erweiterungen nicht jedes Mal alles neu sortieren.

Mit der Ordnung im Code war allerdings noch nicht jede Frage beantwortet. Mein Assistent sagte manchmal Dinge, bei denen ich mich fragte, was davon gerade wirklich passiert war. Hatte er eine Aktion ausgeführt? Oder klang seine Antwort nur so?

Ich wollte mich auf seine Rückmeldungen verlassen können. Gerade dann, wenn er mehr durfte als einen Browser zu öffnen.

Teil 4: Bevor er handelt, muss er mich fragenIm nächsten Teil erzähle ich, warum das Skillsystem eine gute Entscheidung war – und welche Leitplanken ich brauchte, damit ich nicht nur hörte, was mein Assistent behauptete, sondern auch wusste, was er tatsächlich getan hatte. JORIS