Das Elektronengehirn hinter ChatGPT von OpenAI plaudert erstaunlich gut mit menschlichen Partnern. Mike Schilli hat sich ein API-Token geholt und programmiert kleine praktische Anwendungen.
Jeden Monat kurz vor Redaktionsschluss des Linux-Magazins halte ich geheime Riten ab und führe Regentänze auf, um in letzter Minute ein interessantes Thema für Sie herbeizuzaubern. Mit Interesse habe ich deshalb in letzter Zeit den kometenhaften Aufstieg des KI-Chat-Roboters ChatGPT [1] verfolgt, der laut Alarmistenpresse so schlau ist, dass er bald Google als Suchmaschine den Rang ablaufen soll. Könnte dieses System mir vielleicht helfen, neue brandheiße Artikelthemen zu finden, die die Leser mit Begeisterung aufschnappen und gierig den darin verpackten Lernstoff aufsaugen?
Das GPT in ChatGPT steht für Generative Pre-trained Transformer. Das KI-System analysiert eingehenden Text, findet heraus, welche Art von Informationen gewünscht sind, fieselt passende Antworten aus einem massiven mit Informationen aus dem Internet trainierten Datenmodell heraus und verpackt diese wieder als Textantwort. Könnte das Elektronengehirn mir möglicherweise dabei helfen, ein interessantes Thema für diese Kolumne zu finden?
Ich machte die Probe aufs Exempel und richtete die Frage an den Chat-Automaten. Dazu tippte ich die Frage auf Englisch in das Suchfeld der Webseite des Chat-Roboters (Abbildung 1) ein, und zu meinem Erstaunen rückte die KI sogar mit brauchbaren Themen heraus. Einführungen in Rust und Go oder ein Deep Dive in die parallele Programmierung – das klingt doch interessant!
Eliza 2.0
Das Interface fühlt sich an wie eine moderne Version von Eliza [2], dem Standardprogramm aus der Steinzeit (1960) dieses Genres. Tatsächlich hat sich ja danach in Sachen KI und Textverarbeitung erst einmal ein halbes Jahrhundert lang trotz vollmundiger Ankündigungen so gut wie nichts getan. Mit Deep Learning und den zugehörigen neuronalen Netzwerken erfuhr der Bereich in den letzten zehn Jahren allerdings einen derartig gewaltigen Schub, dass man heute Mühe hat, einen plaudernden Computer von einem Menschen zu unterscheiden. Professionelle Romanschreiber [3] und Zeitungsfritzen geben unumwunden zu, für wortreiche Passagen und Plot-Entwicklung gern mal Kollege Computer ans Steuer zu lassen [4].
Tippt man im Browser-Interface von ChatGPT Fragen an den Bot ein, antwortet das System nach kurzer Bedenkzeit mit ruckelnder Schrift, ganz so, als spräche ein Sachbearbeiter mit einem anfragenden Kunden. Dabei beantwortet die KI nicht nur Fragen zum Allgemeinwissen, sondern schreibt auf Befehl auch gleich ganze Zeitungsartikel. Innerhalb einer halben Minute rieselte zum Beispiel auf das Kommando “Schreibe einen Artikel …” aus Abbildung 2 der darunter stehende Text zur Einführung in die parallele Programmierung mit Go aus dem Browser heraus. Das Ergebnis mutet erstaunlich professionell an, und weil die Frage auf Deutsch gestellt worden war, kam auch die Antwort in dieser Sprache zurück. Das funktioniert so auch mit anderen Sprachen. Man vermutet, dass ganze Heerscharen von Sport- und Finanzjournalisten bereits seit Jahren ähnliche Systeme nutzen, um die immer gleichen Artikelformate zu Drittligaspielen oder Börsenkursschwankungen im Akkord zu produzieren.
Terminal als Life-Coach
Wer sich nun nicht dauernd auf OpenAI.com mit E-Mail-Adresse, Passwort und nervigen I-am-not-a-Robot-Challenges einloggen will, um Anfragen zu starten, darf sich auf der Developer-Seite registrieren und einen API-Key [5] entgegennehmen (Abbildung 3). Gibt ein Programm diese Zeichenkette als Authentisierungs-Token bei API-Requests mit, antwortet OpenAI.com per HTTP-Response, die ein einfaches Kommandozeilenwerkzeug aufschnappen und ausgeben kann. Fertig ist das CLI-Interface zum Wissen der ganzen Welt.
Das Auth-Token zum Herumspielen gibt es nach einer Registrierung mit gültiger E-Mail-Adresse und Telefonnummer. Die Firma hinter OpenAI.com bietet zusätzlich ein Bezahlmodell [6] mit Kreditkartenzahlung an, aber die im kostenlosen Modus gewährten Rate-Limits sind so großzügig ausgelegt, dass bei normalem Gebrauch keine Kosten anfallen und deshalb gar keine Kartendaten hinterlegt werden müssen.
Mittels des Pakets »go-gpt3« von Github kommuniziert das Go-Programm in Listing 1 mit dem AI-Modell von GPT. Die Fragen – sogenannte Prompts – schickt das Programm per API-Request an den GPT-Server. Sofern der den Inhalt versteht und eine Antwort findet, verpackt er diese sogenannte Completion in eine API-Antwort und gibt sie zurück an den API-Client.
Listing 1
openai.go
package main
import (
"context"
"fmt"
gpt3 "github.com/PullRequestInc/go-gpt3"
"os"
)
type openAI struct {
Ctx context.Context
Cli gpt3.Client
}
func NewAI() *openAI {
return &openAI{}
}
func (ai *openAI) init() {
ai.Ctx = context.Background()
apiKey := os.Getenv("APIKEY")
if apiKey == "" {
panic("Set APIKEY=<I>API-Key<I>")
}
ai.Cli = gpt3.NewClient(apiKey)
}
func (ai openAI) printResp(prompt string) {
req := gpt3.CompletionRequest{
Prompt: []string{prompt},
MaxTokens: gpt3.IntPtr(1000),
Temperature: gpt3.Float32Ptr(0),
}
err := ai.Cli.CompletionStreamWithEngine(
ai.Ctx, gpt3.TextDavinci003Engine, req,
func(resp *gpt3.CompletionResponse) {
fmt.Print(resp.Choices[0].Text)
},
)
if err != nil {
panic(err)
}
fmt.Println("")
}
Listing 1 verpackt die Logik zur Kommunikation in einen Konstruktor »NewAI()« und eine Funktion »init()« ab Zeile 15, die das API-Token aus der Environment-Variablen »APIKEY« holt und damit in Zeile 21 einen neuen API-Client erzeugt. Damit auch andere Funktionen des Pakets den Client nutzen können, legt Zeile 21 ihn in einer Struktur vom Typ »openAI« ab, die ab Zeile 8 definiert wurde. Der Konstruktor gibt dem aufrufenden Programm einen Pointer auf die Struktur zurück, und Gos Receiver-Mechanismus packt sie den Funktionsaufrufen bei: Objektorientierung in Go.
Das in Zeile 16 erzeugte Context-Objekt dient zur Fernsteuerung des Clients, falls dieser auf einen Timeout läuft oder aus anderen Gründen einen laufenden Request abbrechen soll. Das wird zwar in der einfachen Anwendung in Listing 1 nicht benötigt, doch die »gpt3«-Library braucht ihn trotzdem, also packt Zeile 16 den Context ebenfalls in die OpenAI-Struktur, damit die Funktion »printResp()« ab Zeile 25 später darauf zugreifen kann.
Prompts und Completions
Dort findet der eigentliche Webzugriff statt. Zeile 24 erzeugt eine Struktur vom Typ »CompletionRequest« und setzt den Text der Anfrage in deren Attribut »Prompt«. Der Parameter »MaxTokens« legt fest, wie tief der Sprachprozessor in den Text einsteigt. Die Preisliste auf OpenAI.com definiert etwas nebulös, was ein Token ist [6]. Angeblich entsprechen 1000 Tokens etwa 750 Worten in einem Text; im kostenpflichtigen Modus zahlt der Kunde 2 US-Cent pro tausend Tokens.
Der Wert für »MaxTokens« bezieht sich sowohl auf die Frage als auch auf die Antwort. Wer zu viele Tokens verbraucht, stößt im kostenlosen Modus schnell an das Rate-Limit. Falls man den Wert allerdings zu niedrig ansetzt, kommt bei längeren Ausgaben (zum Beispiel automatisch geschriebenen Zeitungsartikeln) nur ein Teil der Antwort zurück. Der in Zeile 26 eingestellte Wert von 1000 Tokens funktioniert für typische Anwendungsfälle.
Der Wert für »Temperature« in Zeile 27 gibt an, wie hitzköpfig das Gehirn Antworten gibt. Ein höherer Wert als 0 lässt die Antworten variieren, auch auf Kosten der Genauigkeit – dazu später mehr. Die eigentliche Anfrage an den API-Server setzt erst die Funktion »CompletionStreamWithEngine« ab. Ihr letzter Parameter ist eine Callback-Funktion, die der Client immer dann anspringt, wenn Antworthappen vom Server eintrudeln. Zeile 32 gibt das Resultat lediglich auf der Standardausgabe aus.
Endlos-Bot
Um einen Chatbot zu implementieren, der endlos Fragen entgegennimmt und jeweils eine Antwort ausgibt, packt Listing 2 einen auf der Standardeingabe lauschenden Textscanner in eine Endlos-For-Schleife und ruft zu jeder eingetippten Frage in Zeile 16 die Funktion »printResp()« auf. Die kontaktiert den OpenAI-Server und gibt anschließend dessen Textantwort auf der Standardausgabe aus. Dann springt das Programm wieder an den Anfang der Endlosschleife und wartet auf die Eingabe der nächsten Frage.
Listing 2
chat.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
ai := NewAI()
ai.init()
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("Ask: ")
if !scanner.Scan() {
break
}
ai.printResp(scanner.Text())
}
}
Abbildung 4 zeigt die Ausgabe des interaktiven Chat-Programms, das die Frage des Benutzers auf dem Terminal entgegennimmt, zur Beantwortung an den OpenAI-Server sendet und dessen Antwort auf der Standardausgabe ausgibt. Der Bot wartet jeweils auf eine per Eingabetaste abgeschickte Frage, druckt die ankommende Antwort und springt zum nächsten Eingabe-Prompt. Ein Druck auf [Strg]+[D] oder [Strg]+[C] beendet das Programm.
Wichtig ist es noch, in die Umgebung des aufgerufenen Programms das API-Token in der Environment-Variablen »APIKEY« einzupflanzen, sonst bricht das Programm mit einer Fehlermeldung ab. In Abbildung 4 fragt der Benutzer das AI-Modell nach den Vor- und Nachteilen eines Wiener Schnitzels und erhält jeweils drei Pro- und Kontra-Punkte als Antwort. Es zeigt sich, dass das Elektronengehirn erstaunlich präzise Kenntnisse über alles verfügt, was sich irgendwo im Internet (bevorzugt auf Wikipedia) aufstöbern lässt. Es vermag diese Inhalte tatsächlich semantisch aufzubereiten und beantwortet selbst die abstrusesten Fragen dazu sinnvoll. Das Binary »chat« mit dem fertigen Chatbot erzeugt wie üblich der Dreisatz aus Listing 3.
Listing 3
Binary erzeugen
$ go mod init chat $ go mod tidy $ go build chat.go openai.go
Das Wissen der KI ist übrigens nicht immer ganz korrekt. In der Antwort auf die Frage nach meiner Person führt die englische Version zwei Perl-Bücher auf, die ich nie geschrieben habe (Abbildung 5). Korrekt wären die Titel “Perl Power” und “Go To Perl” gewesen. Zudem lässt sich nur schwer herausfinden, warum sich ein Fehler eingeschlichen hat, weil Quellverweise in den Antworten gänzlich fehlen.
Kreativ mit Temperatur
Die API kann auch die Vielfalt der Completions erhöhen, die die KI zum Besten gibt. Sollten die Antworten sehr präzise ausfallen oder soll es eher spielerisch auf dieselbe Frage mehrere, variierende Antworten geben?
Dieses Verhalten steuert der Parameter »Temperature« in Zeile 27 von Listing 1. Mit dem Wert 0 (»gpt3.Float32Ptr(0)«) gibt die KI roboterhaft immer dieselben präzisen Antworten. Schon bei einem Wert von 1 kommt Leben in die Bude: Die KI formuliert stetig um und produziert interessante neue Variationen. Beim Höchstwert 2 hat die KI allerdings zu tief ins Glas geblickt und gibt auch gelallten Unsinn mit zum Teil inkorrekter Grammatik zum Besten. In Abbildung 6 soll der Roboter einen neuen Slogan (“tagline”) für die Go-Kolumne erfinden und liefert mit einem »Temperature«-Setting von 1 fünf erstaunlich gute Vorschläge.
Übersetzen auf Kommando
Da die Kommunikation mit dem KI-Backend über Textbausteine statt über unterschiedliche API-Calls erfolgt, kann man nun problemlos weitere, spezialisierte Tools bauen, die dem Muster von Listing 2 folgen. Wie wäre es etwa mit einem Übersetzungsroboter, der Texte aus verschiedenen Sprachen ins Französische übersetzt? Listing 4 liest Texte ein und schickt sie mit dem Auftrag “Translate to French” an die API.
Listing 4
french.go
package main
import (
"bufio"
"os"
)
func main() {
ai := NewAI()
ai.init()
scanner := bufio.NewScanner(os.Stdin)
text := ""
for scanner.Scan() {
text += scanner.Text()
}
ai.printResp("Translate to French:\n" + text)
}
Da der Server Anfragen in vielen verschiedenen Sprachen analysieren kann, muss der API-Request gar nicht spezifizieren, in welcher Ausgangssprache der zu übersetzende Text vorliegt: Das findet die AI automatisch beim Lesen der Frage heraus. Abbildung 7 zeigt denn auch, dass das Elektronengehirn sowohl die deutsche als auch die englische Touristenfrage nach dem Weg zum Eiffelturm gut ins Französische übersetzt. Kompiliert wird das Binary »french« mittels »go build french.go openai.go«, was das neue Programm mit der Library aus Listing 1 verlinkt.
Für längere Texte muss Listing 1 den Wert für »MaxTokens« allerdings hochsetzen. Das führt irgendwann dazu, dass OpenAI für die Arbeit entlohnt werden möchte. Dann muss man dem Account ein Zahlungsmittel in Form einer Kreditkarte hinzufügen.
Harte Grenze
Ebenfalls erstaunlich ist, dass das Modell zwischen den verschiedenen Fragen einer Chat-Session den Kontext behält. Solange ein Programm denselben Client benutzt, behält das Gehirn vorangegangene Fragen im Gedächtnis, nachfolgende können darauf aufbauen.
Zusammenfassend lässt sich sagen, dass die KI bei dem Modell Davinci-003, das Listing 1 in Zeile 30 als Elektronengehirn eingestellt hat, erstaunlich gut über Fakten der realen Welt Bescheid weiß. Allerdings stoppt das Wissen abrupt im Jahr 2021, denn neuere Ereignisse wurden noch nicht eingefüttert. So muss der Server zum Beispiel bei allen Fragen über die Fußball-WM 2022 in Katar passen. Das nächste Modell wird das hoffentlich aktualisieren.
Verstörende Welten
Doch die künstliche Intelligenz kann nicht nur Texte verstehen, Probleme analysieren, Fragen beantworten und Ausgaben produzieren. Sie beherrscht auch Bildverarbeitung. Zum Beispiel kann sie mittels der API hochgeladene Bilder analysieren, einer Kategorie zuordnen oder erstaunlichen Transformationen unterwerfen.
Listing 5
var.sh
curl https://api.openai.com/v1/images/variations \ -H "Authorization: Bearer sk-XXXXXXXXXXXXX" \ -F image='@test.png' -F n=3 -F size="1024x1024"
Listing 5 nutzt zum Beispiel mittels eines Curl-Kommandos den API-Endpunkt »images/variations« und modelt das Originalfoto eines von mir selbst zubereiteten Wiener Schnitzels auf verstörende Weise um. Hier gilt es zu beachten, dass die KI nur quadratische Fotos annimmt, die im PNG-Format vorliegen und nicht größer als 4 MByte sind.
Mittels des Tools »convert« aus der ImageMagick-Kollektion hatte ich mein Schnitzelfoto mit den Optionen »-crop« und »-resize« in ein PNG-Bild mit den Dimension 1000 mal 1000 Pixel umformatiert. Mit einem gültigen API-Token aufgerufen, liefert das Curl-Kommando – es liest die Datei »test.png« ein und lädt sie hoch – in der JSON-Antwort drei verschiedene URLs. Jede davon führt zu einer aus dem Original generierten Fotovariation, die Curl problemlos vom Server auf die lokale Platte holt.
Abbildung 8 zeigt das hochgeladene Original, Abbildung 9 eine der drei Varianten, die OpenAI zum Herunterladen anbot. Dort liegt das Schnitzel auf anderem Geschirr und schwimmt in hellbrauner Soße. Neben dem Teller steht ein Bierglas voll Salat. Und statt gutem amerikanischem India Pale Ale sieht man links oben eine Flasche, die wie Sake-Wein aussieht.
Bei genauem Hinsehen ist auch das Schnitzel nicht mehr das Original. Vielmehr scheint der von der KI genutzte Algorithmus mein Foto mit einem aus seinem Archiv kombiniert zu haben, das wahrscheinlich aus einem japanischen Tonkatsu-Restaurant stammt. Wir leben in erstaunlichen Zeiten.
Der Autor
Michael Schilli arbeitet als Software Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen verschiedener Programmiersprachen. Unter mailto:mschilli@perlmeister.com beantwortet er gern Ihre Fragen.
Infos
- ChatGPT: https://openai.com/blog/chatgpt/
- Eliza: https://de.wikipedia.org/wiki/ELIZA
- “Collaborative Creative Writing with OpenAI’s ChatGPT”: https://andrewmayneblog.wordpress.com/2022/11/30/collaborative-creative-writing-with-openais-chatgpt/
- “How Kindle novelists are using ChatGPT”: https://www.theverge.com/23520625/chatgpt-openai-amazon-kindle-novel
- API-Key für ChatGPT abholen: https://beta.openai.com/account/api-keys
- OpenAI-Preisliste: https://openai.com/api/pricing/














