Jsiphon - Ein JSON-Parser für LLM-Streaming mit Delta-Tracking und Erkennung von Mehrdeutigkeiten
(github.com/webtoon-today)Hallo, ich möchte euch Jsiphon vorstellen. Es löst ein häufiges Problem beim Einsatz strukturierter Antworten mit LLM-Streaming.
Wenn man strukturierte Antworten (JSON-Modus) eines LLM zusammen mit Streaming verwendet, stößt man oft auf das Problem, Teilantworten parsen zu müssen. In solchen Fällen kann man nicht einfach JSON.parse() verwenden und greift stattdessen häufig zu Verfahren, die unvollständiges JSON wiederholt rekonstruieren.
Wenn man jedoch die append-only-Eigenschaft von LLM-Antworten ausnutzt — also dass Inhalte nur ergänzt werden und nicht ohne Umkehr der Reihenfolge verändert werden —, lässt sich dieses Problem sauberer lösen, und Jsiphon bietet dafür die folgenden drei Funktionen:
-
Append-only-Parsing — Wenn eine teilweise Antwort wie
{"msg": "Heleingeht, wird sofort eine vollständige Antwort wie{msg: "Hel"}zurückgegeben. Beim Schema vor dem Feldmsgwird dabei bereits davon ausgegangen, dass es vollständig ist. -
Delta-Tracking — Jede ausgegebene Antwort enthält zusätzlich zum vollständigen Snapshot auch separat den zuletzt hinzugefügten Inhalt. Wenn man zum Beispiel mehrere Chatbot-Sprechblasen rendert, muss man nicht jedes Mal alles neu zeichnen, sondern kann nur den Zuwachs der letzten Sprechblase aktualisieren. Wenn das LLM im obigen Beispiel anschließend
lo, World!ausgibt, findet man direkt{msg: "lo, World!"}unterdeltader Antwort. Dadurch entfällt die Notwendigkeit, bei jedem Snapshot JSON zu rekonstruieren und zu parsen sowie ein diff zu berechnen. -
Erkennung von Mehrdeutigkeiten — Es wird ein Mehrdeutigkeitsbaum (
ambiguity tree) zurückgegeben, der exakt dieselbe Baumstruktur wie die Antwort besitzt. Damit lässt sich auf verschiedenen Tiefen erkennen, ob die im Snapshot enthaltenen Daten bereits feststehen oder ob noch weiter aus der Antwort gelesen wird. Wenn zum Beispiel die folgenden Daten gestreamt werden{"header":{"title": "abcdefghijk","date": "..."},"body": "..."}
kann man mit
isAmbiguous(ambiguous.header.title)dentitleab dem Zeitpunkt verwenden, an dem er vollständig ist (Antwort Nr. 3), ohne auf die Fertigstellung nachfolgender Felder warten zu müssen. Es wird also nicht nur Gesamtvollständigkeit, sondern partielle Vollständigkeit auf allen Ebenen bereitgestellt. Deshalb liefertisAmbiguous(ambiguous.header)erst dannisAmbiguous = false, wenn alle Nachfahren vonheadervollständig sind.
Es gibt bereits viele Bibliotheken zur Rekonstruktion bzw. zum Parsen von partiellem JSON (partial-json, gjp-4-gpt usw.), und sie lösen das grundlegende Parsing-Problem gut. Jsiphon nutzt jedoch die Eigenschaft, dass LLMs append-only streamen, und liefert dadurch nicht nur Snapshots, sondern auch feldweise Inkremente (delta) und die Möglichkeit, bei jeder Iteration bereits vollständige Felder zu erkennen.
Wenn ihr bereits ein ähnliches Problem gelöst habt, könnt ihr vermutlich gut nachvollziehen, worauf das abzielt. Ich kombiniere Jsiphon derzeit mit Multi-Type-SSE, damit ein Chatbot Text streamen und gleichzeitig mehrere Flags (is_adult, need_admin usw.) in Echtzeit bestimmen kann.
Zusätzlich gibt es aus praktischer Sicht weitere Komfortfunktionen: zero-dependency, keine Exceptions selbst bei fehlerhaften Antworten sowie das Entfernen bedeutungsloser Texte vor und nach dem JSON.
GitHub: https://github.com/webtoon-today/jsiphon
npm install jsiphon
Falls es hilfreich für euch ist, probiert es gern aus und gebt mir Feedback.
Der ambiguity tree scheint mir ein durchaus anspruchsvolles Design zu sein, und ich bin neugierig, ob es dafür noch bessere Ansätze gibt. Feedback zum API-Design wäre sehr willkommen.
Noch keine Kommentare.