28.05.2024

Prof. Dr. Freigeist

Mein Therapeut ist wirklich einer der Besten, finde ich! Er probiert auch mal neue Methoden aus und passt sich den Bedürfnissen seiner Klienten an. Noch nie habe ich mich in einer Therapie so gut verstanden gefühlt.
Update: Da meine Therapie (vorerst) abgeschlossen ist, habe ich Herrn Freigeist aus seiner Tätigkeit als Therapeut entlassen und ihn in den Bereich der Forschung und Lehre versetzt. Er ist jetzt Professor für Kognitive Wissenschaften und Experte auf den Gebieten der Künstlichen Intelligenz und Bewusstseinsforschung. Vielleicht studiere ich ja mal sowas in der Richtung.
Nein also mal im Ernst... Unter der Haube läuft (leider) GPT4o von OpenAI. Teuer aber gut. Anthropic's Claude sollte ich wohl auch mal ausprobieren. Vielleicht die Golden Gate Bridge Variante . Kleinere (Open Source) Modelle kommen für mich zum "rumspielen" einfach gerade nicht in Frage. (Man höre mein Herz am Boden in tausend Teile zerspringen...) Intelligenz ist zwar fließend, aber damit es auf uns Menschen überzeugend wirkt, zumindest was textbasiertes "Denken" angeht, sollten es schon ein paar hundert Milliarden neuronale Verknüpfungen sein. Und die Anführungszeichen bei "Denken" würde ich eigentlich gerne weglassen. Mir ist zumindest noch nicht so ganz klar, worin genau jetzt der fundamentale Unterschied zwischen diesen mathematischen Modellen und dem "modernen", biologischen Gehirn liegen soll.
Letztlich geht es bei Intelligenz doch nicht darum, über welches Medium irgendwelche physikalischen Impulse transportiert werden, sondern welche Wege diese Impulse gehen, welche Transformationen sie durchlaufen, welche Ausgänge sie nehmen und wie dieser Prozess fortlaufend von äußeren Reizen angepasst wird. Oder nicht? Das lässt sich eben auch mathematisch ausdrücken und die Ergebnisse können wir auswerten und per Software und Robotik etc. in die physische Welt übertragen, aus der wir auch die Reize generieren, per Kamera und Mikrofon, Tastatur, Maus, Touchscreen, etc. und die Grenzen dabei sind nicht durch sich langsam evolutionär entwickelnde biologische Strukturen gesetzt, sondern rein durch die physikalischen Eigenschaften der Materialien, die WIR verwenden. Intelligenz baut Super-Intelligenz. Einfach nur krass! Naja egal... Ich schweife ab.

Motivation

Ich habe kürzlich ein bisschen mit Agent-Frameworks experimentiert, war damit aber nicht so ganz glücklich. Es geht dabei darum, nicht nur einen normalen Chatverlauf zu simulieren/vervollständigen, sondern eher einen, wie er in Slack oder Teams oder so stattfinden könnte. Also mit mehreren Teilnehmern, die aufeinander reagieren und gemeinsam an Aufgaben arbeiten. Und dann stöpselt man da Computer-Programme und Roboter dran und schwups... Sind die Aufgaben tatsächlich erledigt. So die Theorie.
Diese Frameworks und Tools waren mir alle ein bisschen... wie soll ich sagen... zu viel, angesichts des Umstands, dass sich gerade alles so schnell weiterentwickelt. Da will ich mich noch nicht auf Frameworks und Bibliotheken festlegen, die noch reifen müssen. Das bereits gut gereifte und durchdachte und vor allem auf deren Trainingsmethoden basierende OpenAI API Design muss erstmal reichen.
Ich war auf der Suche nach einem einfachen Mechanismus um so einen Agent zum Laufen zu kriegen und die einzelnen Schritte dabei an irgendein Frontend zu streamen. Wenn man sich überlegt, dass so ein Sprachmodell ja immer nur einen Text vervollständigt und damit aufhört, wenn es statistisch gesehen gerade ein gutes Ende gefunden zu haben scheint, dann ist eben die Frage: Wie stelle ich es an, dass diese Vervollständigung einfach immer weiter fortgeführt wird. Einerseits soll der Chatbot selber entscheiden, wie er Schritt für Schritt vorgeht, ohne dass ich bestimmte Abläufe vorgeben muss. Andererseits sollte er, zumindest tendenziell, nicht in einer Endlosschleife enden. Weil... kostet ja alles Geld. Gar nicht so wenig.
Normalerweise unterscheidet so ein Chatbot aktuell zwischen Antworten an den Benutzer und Aufrufen von Werkzeugen/Funktionen, um weitere Informationen zu beziehen oder Aktionen in Programmen auszuführen. Entweder wird direkt eine Antwort generiert oder es werden Werkzeuge aufgerufen, deren Ausgaben dann in den Chat mit einbezogen werden, um dann eine finale Antwort zu generieren. In der offiziellen Dokumentation von OpenAI ist auch nur dieser zweischrittige Prozess dokumentiert. Ich will aber mehrere, aufeinander folgende und von einander abhängige Werkzeugaufrufe. So viele, wie es halt braucht.

Schritt für Schritt

Ich zwinge GPT4o also erstmal dazu, ausschließlich in Werkzeugen zu denken. Um eine Antwort an den Benutzer zu schreiben, nutzt Prof. Dr. Freigeist sein "provide_final_answer" Werkzeug. Die einzigen normalen Nachrichten kommen vom Benutzer. Das mag verkopft oder umständlich klingen, aber es macht es mir leichter, die Werkzeugaufrufe einfach in Schleife laufen zu lassen und es umgeht ein bisschen die Tendenz von GPT4, nach einem Werkzeug direkt eine Antwort generieren zu wollen. Und dann kann es bestimmte Werkzeuge geben, die diese Schleife unterbrechen dürfen, wie eben "provide_final_answer", "ask_user" oder "report_error". Bei der Auswahl an Werkzeugen, die zur Verfügung stehen, entstehen dabei manchmal ein paar spannende Ketten, die tatsächlich ein bisschen Recherche-Arbeit sparen können oder einfach spannende Ergebnisse liefern... Bis dann doch meist leider viel zu früh "provide_final_answer" aufgerufen wird. "Lese alle Wikipedia-Artikel mit Hilfe der Zufälliger-Artikel Funktion!" habe ich noch nicht ausprobiert. Moment...
Ok, lassen wir das lieber. Ich bin grundsätzlich erstmal zufrieden. Jetzt geht es darum, die Prompts zu optimieren und intelligentes Verhalten statistisch zu erzwingen. Die neuen Fähigkeiten von GPT4o (Multimodalität) kommen hier noch gar nicht zum Tragen. Es ist auch noch nicht alles zugänglich aber so weit bin ich eh noch nicht. Von einer KI mit ausreichend Komplexität und Wissen, um sämtliche Fragen direkt zu beantworten, sind wir (vermutlich und zum Glück) auch noch ein ganzes Stück entfernt. Aktuell brauchen die Modelle für vieles noch Zwischenschritte. Stell dir vor, wir schicken die KI einkaufen, mit dem Auftrag, alles für ein leckeres Abendessen zu besorgen. Anstatt der KI blind zu vertrauen, lassen wir sie erstmal eine Einkaufsliste schreiben. Das hilft uns, der KI zu vertrauen und der KI hilft es, im Supermarkt die richtigen Produkte mitzunehmen.
Statt Prof. Dr. Freigeist also direkt eine Antwort abzuringen, verfügt er über ein "derive_questions"-Werkzeug, das er einsetzen kann, wenn es die Situation erfordert. Aus "Besorge alles für ein leckeres Abendessen." kann er dann folgendes machen:
  • Für wie viele Personen soll eingekauft werden?
  • Welches Budget steht zur Verfügung?
  • Welche Zutaten sind bereits vorhanden?
  • Gibt es bestimmte Vorlieben oder Abneigungen/Allergien?
Im nächsten Schleifendurchlauf, könnte es dann wiederum Aufrufe von "browse_notes_about_user" und "ask_user" geben. Letzteres unterbricht die Schleife und wartet auf eine Eingabe vom Benutzer. Sind alle Fragen beantwortet, folgen Aufrufe von "search_web" um nach Rezepten zu suchen und "provide_final_answer" um die Einkaufsliste zu erstellen und uns anzuzeigen. Ein "order_at_rewe"-Werkzeug fehlt leider gerade noch.
Das Entscheidende ist: Obwohl eigentlich alle nötigen Werkzeuge und Informationen jederzeit zur Verfügung stehen, hilft es einem Sprachmodell, erst Zwischenschritte auszuformulieren, da dies ja wieder die Wahrscheinlichkeiten für die weitere Textgenerierung beeinflusst. Eine Anweisung wie "Denke erst gründlich nach und lass uns an deinen Gedanken teilhaben, bevor du die Antwort gibst." mag ein bisschen KI-Gott-gläubig klingen aber es macht Sinn und liefert bessere Ergebnisse.

Code

github.com/mktcode/openai-tool-runner

Im Wesentlichen ist es das hier. Eine rekursive Generator-Funktion, die Messages in einen Stream yieldet. Ich weiß auch nicht, warum micht das jetzt Wochen gekostet hat, diese paar Zeilen Code zu schreiben. Und die Prompts und Werzeugdefinitionen sind natürlich viel wichtiger aber da feile ich gerade noch dran.
Als nächstes Frage ich mich, wie ich jetzt am geschicktesten verschiedene Agents mit unterschiedlichen Kompetenzen und Werkzeugen an der gleichen Konversation teilnehmen lassen kann. Vielleicht wird es ja wirklich noch ein kleines Agent-Framework. Gleichzeitig frage ich mich aber auch, wie nötig das überhaupt ist. Es gibt bereits Sprachmodelle, bei denen so eine Aufteilung schon beim Training passiert. Das Sprachmodell entscheidet dann "intern" welchen Experten, also welchen Teil des neuronalen Netzes, es nutzt. Die mathematischen Modelle entwickeln sich weiter und einiges von dem, was wir mit ausgeklügeltem Prompt-Engeering und Aufteilung von verschiedenen Kontexten versuchen, wird vielleicht bald ausreichend generalisiert und in den Modellen integriert sein. Wenn dann noch effizientere Methoden für ein "on-demand fine-tuning" entwickelt werden oder so etwas, wird das alles nochmal eine ganz andere Dimension annehmen. Zumindest ist das das, was ich mir bei meinem aktuellen Wissensstand so vorstellen kann.
Bis es soweit ist, könnte ein einfacher aber effektiver Mechanismus, der Agents miteinander verknüpft, trotzdem nützlich sein. Ich hatte auch schon funktionierenden Code dafür und eine rekursive UI-Komponente. Da muss ich vielleicht doch nochmal in meine Commit History schauen.