Eine KI-Chat-App ist eine Text-Chat-Schnittstelle, bei der ein Benutzer eine Nachricht eingibt; die App sendet diese an einen Dienst, der ein KI-Modell aufrufen kann, und zeigt dem Benutzer anschließend die Antwort des Modells an.
Bei vielen Produkten trifft die Antwort als Stream ein, sodass der Benutzer sieht, wie nach und nach Tokens erscheinen, anstatt auf die vollständige Antwort warten zu müssen.
Um das Szenario einfach zu halten, gehen wir von einem kleinen internen Team aus, das eine Web-App nutzt, die mit einem einzigen Backend-Dienst kommuniziert, welcher wiederum einen Modellanbieter aufruft.
Bei Verwendung eines einzelnen Servers gleicht der Ablauf einem Kreislauf. Der Client sendet die Nachricht des Benutzers an einen einzelnen API-Server; dieser lädt den Gesprächsverlauf, um über den nötigen Kontext zu verfügen, ruft dann einen Modellanbieter auf und übermittelt schließlich die Antwort des Assistenten an den Client zurück.
Die Speicherung des Gesprächsverlaufs ist selbst bei diesem Basismodell wichtig, da ein Neuladen der Seite den Chat nicht löschen sollte. Der API-Server speichert jede Benutzer- und Assistentennachricht dauerhaft und verwendet diese gespeicherten Nachrichten, um dasselbe Gespräch im nächsten Schritt fortzusetzen.
Hier ist der einzelne Chat-Austausch, den wir als Basismodell implementieren.
Das Diagramm umfasst vier Aufgaben. Der Client erfasst die Eingaben und stellt den Datenstrom dar, der API-Server steuert den Lebenszyklus der Anfrage, der Konversationsspeicher bewahrt die Nachrichten auf (damit der Gesprächsverlauf auch nach einem Seiten-Refresh erhalten bleibt), und die Modell-API generiert die Assistant-Token, die der Server an den Client weiterleitet.
Der funktionale Umfang beschränkt sich auf reinen Text-Chat. Es gibt keine Tools, Bilder oder Datei-Uploads; die einzigen Aufgaben pro Interaktionsschritt sind somit das Speichern von Nachrichten, das Zusammenstellen des Kontexts und der Aufruf des Modells.
Betrachten wir nun eine Kennzahl, die einen einzelnen Server an seine Grenzen bringt: Bei 2.000 gleichzeitigen Nutzern und zwei Interaktionsschritten pro Minute muss das Backend etwa Nutzerinteraktionen pro Sekunde bewältigen – was einer entsprechenden Anzahl paralleler Modellaufrufe entspricht.
Diese eine Kennzahl erzeugt zwei nicht-funktionale Anforderungen, auf die das weitere Design reagieren muss. Die Latenz wird kritisch, da Modellaufrufe den Großteil der Gesamtlaufzeit ausmachen, und die Kosten werden zu einem wichtigen Faktor, da das System für jede Anfrage und jedes generierte Token bezahlen muss.
Schon vor der Skalierung lässt sich das Basissystem sauber in wiederverwendbare Bausteine unterteilen, von denen jeder eine spezifische Aufgabe übernimmt.