OpenAI (ChatGPT) API Tutorial – Teil #3 Funktionen (Function calling) 

OpenAI (ChatGPT) API Tutorial – Teil #3 Funktionen (Function calling) 

Einleitung

Im Teil #2 der Tutorial-Reihe habe ich dir einen Überblick über die unterschiedlichen Modelle gegeben und etwas unterhaltsamer gezeigt, wie man mit Dall-E Bilder anhand eines Prompts generieren lassen kann.

In diesem (letzten) Teil möchte ich dir ein relativ neues Feature vorstellen: Funktionsaufrufe oder function calling. Das klingt auf den ersten Blick nicht so spannend aber dahinter verbirgt sich eigentlich etwas sehr Nützliches. Eventuell kennst du schon die Plugins, die man bei der Verwendung von ChatGPT in der Bezahlversion nutzen kann. Mit den Funktionsaufrufen kannst du ähnliches auch über die API erreichen und sogar viel mehr, denn wir können unsere eigenen Funktionen einbinden.

Function calling

Ok was ist das jetzt genau? Grundsätzlich können wir bei der „Unterhaltung“ mit dem Sprachmodell eine Liste von Funktionen mitgeben. Zu jeder Funktion gibt es eine Beschreibung, wofür die Funktion gut ist. Das hilft dem Sprachmodell bei der Entscheidung, ob es notwendig oder hilfreich ist, diese Funktion zum Lösen der Aufgabe aufzurufen. Zusätzlich zu der textuellen Beschreibung müssen wir auch die Parameter der Funktion angeben. Auch hier gehört zu jedem Parameter eine textuelle Beschreibung dazu, damit das Sprachmodell weiß wozu ein Parameter dient.

Aber ruft jetzt OpenAI die Funktion auf? Nein. Es ist vielmehr so, dass das Sprachmodell uns als Antwort mitteilt, dass ein Funktionsaufruf notwendig ist, den wir dann mit unserem Programm ausführen. Die API gibt dabei an, mit welchen Parametern und Werten die Funktion aufgerufen werden soll.

Wenn die Antwort des Funktionsaufrufes vorliegt, übermitteln wir das Ergebnis an das Sprachmodell und es arbeitet also weiter mit diesen Daten und erzeugt entweder eine Antwort und wenn nötigt, fordert uns auf weitere Funktionen aufzurufen.

Ob ein Funktionsaufruf notwendig ist, entscheidet das Modell selbst. Auch wenn wir eine Reihe von Funktionen angeben, so kann das Modell entscheiden, dass ein Funktionsaufruf nicht notwendig ist. Allerdings kann man das auch beeinflussen. So kann man entweder eine bestimmte Funktion vorgeben oder man kann auch angeben, dass keine Funktion ausgeführt werden soll.

Demoanwendung

Schauen wir uns ein Beispiel an. In dem Beispiel definiere ich eine Funktion, die in der Lage ist die Feiertage in Deutschland zurückzugeben. Da das Sprachmodell auf dem Datenstand von 2021 (zum Zeitpunkt der Erstellung) ist, kann es u.U. nicht wissen, welche Feiertage aktuell sind. Gut rein hypothetisch kann es das wissen, denn Feiertage werden ja nicht ausgewürfelt und stehen lange im Voraus fest. 

Hierfür nutzen wir eine öffentliche REST-Schnittstelle, die tatsächlich von der Bundesrepublik betrieben wird. Hier übrigens eine Liste aller Schnittstellen: https://bund.dev/apis

Schauen wir uns den ersten Code-Ausschnitt an:

import openai
import json
import requests
openai.api_key = "Füge hier deinen API-Key ein"
feiertage_api_url = "https://feiertage-api.de/api/"
prompt = "Wir haben den 3.7.2023, wann ist der nächste gesetzliche Feiertag in Hessen?"
messages = [{"role": "user", "content": prompt}]
def feiertage(jahr):
print(f"Hole Feiertage für Jahr {jahr}")
print(feiertage_api_url + "?jahr=" + jahr)
return requests.get(feiertage_api_url + "?jahr=" + jahr).json()

Als erstes importieren wir eine Reihe von Bibliotheken. Die Bibliotheken json und requests benötigen wir für die Kommunikation mit der Feiertage-API.

Anschließend übergeben wir, wie wir das schon kennen, den API-Key an openai. 

Der Prompt, der uns hier interessiert , fragt nach dem nächsten Feiertag in Hessen. Theoretisch weiß das Modell nichts über Feiertage nach 2021 und das sollte dazu führen, dass ein Funktionsaufruf notwendig wird. Da das Modell auch nicht weiß, welches Datum wir aktuell haben, sind wir so nett und geben das mit an.

Zuletzt definieren wir unsere Funktion feiertage. Diese akzeptiert einen Pflichtparameter für das Jahr, in dem der Feiertag liegen soll. Das Einzige was die Funktion macht, ist den Feiertags-Endpunkt aufzurufen und das Resultat als JSON zurück zu geben.

Jetzt wo wir die Funktion fertig haben, ist es Zeit die Übergabe an das Modell vorzubereiten:

functions = [
{
"name": "feiertage",
"description": "Ermittelt die gesetzlichen Feiertage in Deutschland",
"parameters": {
"type": "object",
"properties": {
"jahr": {"type": "string", "description": "Das Jahr in dem die Feiertage liegen sollen, Beispiel '2023'"},
},
"required": ["jahr"],
},
}
]

Wir legen eine Variable functions an, die eine Liste von Funktionsdefinitionen enthält.

Das Feld „name“ enthält wenig überraschend den Namen der Funktion. In dem Feld „description“ beschreiben wir, was die Funktion leistet. Wie schon weiter oben beschrieben, dient es dazu dem Modell mitzuteilen was die Funktion leistet.

Anschließend beschreiben wir die Parameter der Funktion. Der entscheidende Teil dabei ist „properties„. Wir benennen dort die Parameter und geben an welchen Datentyp sie haben. Ganz wichtig natürlich die Beschreibung, damit das Modell weiß wozu der Parameter dient.

Die Pflichtangaben landen in der Liste „required„, hier in unserem Fall ist der Parameter „jahr“ ein Pflichtfeld.

Jetzt ist es so, dass das Modell sich einen Funktionsaufruf wünschen kann. Dabei gibt es den Namen der Funktion plus die Werte der Parameter an. Das bekommen wir mehr oder weniger in textueller Form als JSON. Damit wir die Funktion anhand dessen aufrufen können, schreiben wir uns eine Hilfsfunktion, dessen Aufgabe es ist, mithilfe der Antwort die passende Funktion inkl. Parameter aufzurufen und das Ergebnis zurück liefert. 

def execute_function_call(message):
if message["function_call"]["name"] == "feiertage":
jahr = json.loads(message["function_call"]["arguments"])["jahr"]
results = feiertage(jahr)
else:
results = f"Fehler! Es existiert keine Funktion mit dem Namen: {message['function_call']['name']}"
return results

Unserer Hilfsfunktion nimmt eine OpenAI-API-Antwort als Parameter an. Sie wirft dann einen Blick auf das Feld „function_call“ und schaut, welche Funktion gemeint ist.

Wir kennen aktuell nur eine Funktion, nämlich „feiertage“. Kommt etwas anderes zurück, so geben wir lediglich eine Fehlerantwort zurück.

Andernfalls extrahieren wir die Parameter aus der Antwort und rufen die Funktion auf und geben das Ergebnis zurück.

Jetzt sind wir soweit, dass wir die OpenAI-API aufrufen können:

response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=messages,
functions = functions
)
print(response)
ai_msg = response["choices"][0]["message"]
messages.append(ai_msg)
if ai_msg.get("function_call"):
results = execute_function_call(ai_msg)
print(results)
messages.append({"role": "user", "content": ""})
response = openai.ChatCompletion.create(model="gpt-3.5-turbo",messages=messages,functions = functions)
print(response)

Wir rufen, so wie wir das bereits kennen, die ChatCompletion API auf. Neben der Liste der Prompts und die Angabe des Modells, übergeben wir nun auch die Liste der Funktionen über den Parameter functions.

Da wir uns im Chat-Modus befinden, fügen wir die Antwort der AI in die Liste der Nachrichten ein (siehe den ersten Teil der Tutorial-Reihe, falls dir unklar ist, warum wir das so machen).

Jetzt kann es sein, dass das Modell anstelle eines menschlich lesbaren Textes nur einen Funktionsaufruf anfordert. Das prüfen wir in dem wir in der Antwort nachschauen, ob das Feld „function_call“ mit einem Wert befüllt ist. Ist das der Fall, so rufen wir unsere Hilfsfunktion execute_function_call auf, die ihrerseits unsere Feiertags-Funktion aufruft.

Das Resultat des Funktionsaufrufes fügen wir in die Liste der Prompts ein und rufen erneut die ChatCompletion API auf. Jetzt bekommt das Sprachmodell die notwendigen Daten und kann uns eine finale Antwort liefern:

{
"id": "chatcmpl-7YqFEqFINsLnzX2GJa3uYeGo6UNNO",
"object": "chat.completion",
"created": 1688538336,
"model": "gpt-3.5-turbo-0613",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"function_call": {
"name": "feiertage",
"arguments": "{\n  \"jahr\": \"2023\"\n}"
}
},
"finish_reason": "function_call"
}
],
"usage": {
"prompt_tokens": 99,
"completion_tokens": 18,
"total_tokens": 117
}
}
Hole Feiertage für Jahr 2023
https://feiertage-api.de/api/?jahr=2023
{'BW': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Heilige Drei Könige': {'datum': '2023-01-06', 'hinweis': ''}, 'Gründonnerstag': {'datum': '2023-04-06', 'hinweis': 'Gemäß § 4 Abs. 3 des Feiertagsgesetzes von Baden-Württemberg[10] haben Schüler am Gründonnerstag und am Reformationstag schulfrei. In der Regel legt das Kultusministerium die Ferientermine so fest, dass diese beiden Tage in die Osterferien bzw. in die Herbstferien fallen.'}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': 'Gemäß § 4 Abs. 3 des Feiertagsgesetzes von Baden-Württemberg[10] haben Schüler am Gründonnerstag und am Reformationstag schulfrei. In der Regel legt das Kultusministerium die Ferientermine so fest, dass diese beiden Tage in die Osterferien bzw. in die Herbstferien fallen.'}, 'Allerheiligen': {'datum': '2023-11-01', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'BY': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Heilige Drei Könige': {'datum': '2023-01-06', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': ''}, 'Augsburger Friedensfest': {'datum': '2023-08-08', 'hinweis': 'Das Augsburger Friedensfest ist nur im Stadtgebiet Augsburg (nicht jedoch im angrenzenden Umland) gesetzlicher Feiertag (Art. 1 Abs. 2 Bayerisches Feiertagsgesetz[7]).'}, 'Mariä Himmelfahrt': {'datum': '2023-08-15', 'hinweis': 'Mariä Himmelfahrt ist in Bayern in von den derzeit 1704[8] (Zensus 2011, bis 2013: 1700) Gemeinden mit überwiegend katholischer Bevölkerung gesetzlicher Feiertag, in den restlichen 352 (Zensus 2011, bis 2013: 356) Gemeinden nicht. Gemäß Art. 1 Abs. 3 des Bayerischen Feiertagsgesetzes[7] ist es Aufgabe des Bayerischen Landesamtes für Statistik und Datenverarbeitung, festzustellen, in welchen Gemeinden Mariä Himmelfahrt gesetzlicher Feiertag ist. Die aktuelle Festlegung beruht auf dem Ergebnis der letzten in der Bundesrepublik Deutschland durchgeführten Volkszählung vom 25. Mai 1987. Gemäß Art 4. Abs. 3 des Bayerischen Feiertagsgesetzes entfällt im gesamten Bundesland zu Mariä Himmelfahrt an Schulen aller Gattungen der Unterricht. Diese Festlegung gilt ausdrücklich auch in den Teilen Bayerns, in denen dieser Tag kein gesetzlicher Feiertag ist. Eine Übersichtskarte aller Gemeinden, in denen Mariä Himmelfahrt ein Feiertag ist, kann beim Bayerischen Landesamt für Statistik und Datenverarbeitung heruntergeladen werden (Link siehe unter "Weitere Weblinks").'}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Allerheiligen': {'datum': '2023-11-01', 'hinweis': ''}, 'Buß- und Bettag': {'datum': '2023-11-22', 'hinweis': 'Gemäß Art. 4 Nr. 3 des Bayerischen Feiertagsgesetzes[7] entfällt im gesamten Bundesland am Buß- und Bettag an allen Schulen der Unterricht.'}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'BE': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Frauentag': {'datum': '2023-03-08', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'BB': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostersonntag': {'datum': '2023-04-09', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstsonntag': {'datum': '2023-05-28', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'HB': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'HH': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'HE': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'MV': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Frauentag': {'datum': '2023-03-08', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'NI': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'NW': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Allerheiligen': {'datum': '2023-11-01', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'RP': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Allerheiligen': {'datum': '2023-11-01', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'SL': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': ''}, 'Mariä Himmelfahrt': {'datum': '2023-08-15', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Allerheiligen': {'datum': '2023-11-01', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'SN': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': 'Fronleichnam ist kein gesetzlicher Feiertag außer in folgenden katholisch geprägten Gemeinden des sorbischen Siedlungsgebietes im Landkreis Bautzen:\n\t\t\t\tBautzen (nur in den Ortsteilen Bolbritz und Salzenforst), Crostwitz, Göda (nur im Ortsteil Prischwitz), Großdubrau (nur im Ortsteil Sdier), Hoyerswerda (nur im Ortsteil Dörgenhausen), Königswartha (nicht im Ortsteil Wartha), Nebelschütz, Neschwitz (nur in den Ortsteilen Neschwitz und Saritsch), Panschwitz-Kuckau, Puschwitz, Räckelwitz, Radibor, Ralbitz-Rosenthal und Wittichenau. Entscheidend ist dabei der Arbeitsort, nicht der Wohnort eines Arbeitnehmers.\n\t\t\t\tDie gesetzliche Grundlage für diese durch die Fronleichnamsverordnung festgelegte Regelung ergibt sich aus § 1 Abs. 1 des Sächsischen Feiertagsgesetzes.[5]'}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, 'Buß- und Bettag': {'datum': '2023-11-22', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'ST': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Heilige Drei Könige': {'datum': '2023-01-06', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'SH': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'TH': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Fronleichnam': {'datum': '2023-06-08', 'hinweis': 'Fronleichnam ist kein gesetzlicher Feiertag außer im gesamten Landkreis Eichsfeld (79 Gemeinden am 31. Dezember 2013, Auflistung siehe dort) sowie in folgenden Gemeinden des Unstrut-Hainich-Kreises und des Wartburgkreises:\n\t\t\t\tAnrode (nur in den Ortsteilen Bickenriede und Zella), Brunnhartshausen (nur in den Ortsteilen Föhlritz und Steinberg), Buttlar, Dünwald (nur in den Ortsteilen Beberstedt und Hüpstedt), Geisa, Rodeberg (nur im Ortsteil Struth), Schleid, Südeichsfeld und Zella/Rhön.\n\t\t\t\tDie gesetzliche Grundlage für diese Regelung ergibt sich aus § 2 Abs. 2 und § 10 Abs. 1 des Thüringer Feiertagsgesetzes.[6]'}, 'Weltkindertag': {'datum': '2023-09-20', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, 'Reformationstag': {'datum': '2023-10-31', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}, 'NATIONAL': {'Neujahrstag': {'datum': '2023-01-01', 'hinweis': ''}, 'Karfreitag': {'datum': '2023-04-07', 'hinweis': ''}, 'Ostermontag': {'datum': '2023-04-10', 'hinweis': ''}, 'Tag der Arbeit': {'datum': '2023-05-01', 'hinweis': ''}, 'Christi Himmelfahrt': {'datum': '2023-05-18', 'hinweis': ''}, 'Pfingstmontag': {'datum': '2023-05-29', 'hinweis': ''}, 'Tag der Deutschen Einheit': {'datum': '2023-10-03', 'hinweis': ''}, '1. Weihnachtstag': {'datum': '2023-12-25', 'hinweis': ''}, '2. Weihnachtstag': {'datum': '2023-12-26', 'hinweis': ''}}}
{
"id": "chatcmpl-7YqFGOa3vJ6Ls9YiPj0JRolY6O0Zy",
"object": "chat.completion",
"created": 1688538338,
"model": "gpt-3.5-turbo-0613",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "{\n  \"jahr\": \"2023\"\n}\nDer n\u00e4chste gesetzliche Feiertag in Hessen nach dem 3.7.2023 ist der Tag der deutschen Einheit am 3. Oktober 2023."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 124,
"completion_tokens": 50,
"total_tokens": 174
}
}

Das hier ist natürlich die unverarbeitete Antwort, um besser zeigen zu können, was passiert.

Zuerst sehen wir nämlich, dass das Modell einen Funktionsaufruf haben möchte und gibt uns für den Parameter „jahr“ den Wert „2023“ an.

Wir sehen dann, dass unserer Funktion die Feiertage von der API anfragt und die Antwort enthält alle Feiertage für dieses Jahr.

Nachdem das Model diese Daten verdaut hat, sehen wir die finale Antwort: „{„jahr“: „2023“} Der nächste gesetzliche Feiertag in Hessen nach dem 3.7.2023 ist der Tag der deutschen Einheit am 3. Oktober 2023.„.

Wir haben ja keine Angaben gemacht, wie die Antwort ausgegeben werden soll, daher die etwas fummelige Ausgabe mit der Angabe des Parameters vor dem eigentlichen Text.

Hinweise

Der Beispielcode ist äußerst rudimentär und müsste deutlich überarbeitet werden. Wenn wir auf den ersten Prompt keine Funktionsaufruf-Empfehlung bekommen hätten, dann würde sich das Programm ohne jede weitere Ausgabe einfach beenden. Das müsste man viel eleganter gestalten und eine Routine schrieben, die sich darum kümmert zu schauen, ob wir eine Text- oder Funktions-Antwort vorliegen haben und entsprechend handelt.

Außerdem ist es nicht so schön wie der OpenAI-API Aufruf einfach in den if-Block kopiert wurde. Das gehört natürlich ein eigene Funktion ausgelagert etc.

Zu guter Letzt noch ein Hinweis zu einem optionalen Parameter, den wir mitgeben können: „function_call“ (so ähnlich wie in der Antwort). Damit können wir steuern, welche Funktion angesprochen werden soll oder ob gar keine Funktionsaufrufe gewünscht sind. Beispiele:

# Bestimmte Funktion soll aufgerufen werden
function_call: {"name": "<Name der Funktion>"}
# Kein Funktionsaufruf erwünscht:
function_call: "none"
# Standardwert ist "auto"
function_call: "auto"

Ende

Das war jetzt der letzte Teil der Serie. Mit den Funktionsaufrufen haben wir ein mächtiges Werkzeug kennengelernt. Es ermöglicht uns eine Art Plugins bereit zu stellen und Daten, die dem Modell sonst unzugänglich wären, bereit zu stellen.

Den Code findest du in meinem GitHub-Repository unter: https://github.com/ki-zeitalter/openai-api-tutorial/blob/main/openai_functions_tutorial.ipynb.

Mit den Funktionen können wir auch so etwas wie Agenten implementieren. D.h. die Funktionsaufrufe können zu weiteren automatisch erzeugten Prompts führen, z.B. um eine komplexe Aufgabe in viele Teilschritte zu teilen.

Was wir in dieser Tutorialreihe gesehen haben ermöglicht uns sehr coole Sachen umzusetzen. Allerdings ist es nicht der eleganteste Weg, wie man das erreichen kann. Es gibt Frameworks, die das Ganze kapseln und die Komplexität verbergen. Insbesondere möchte ich hier LangChain empfehlen. Ein Tutorial zu LangChain ist bereits in Arbeit! wink

Ich hoffe dir haben die Tutorials gefallen und ich würde mich über jedes Feedback sehr freuen!

Die anderen Teile der Serie

Teil 1: OpenAI (ChatGPT) API Tutorial – Teil #1 

Teil 2: OpenAI (ChatGPT) API Tutorial – Teil #2 Modelle und Dall-E

OpenAI (ChatGPT) API Tutorial – Teil #3 Funktionen (Function calling) 

OpenAI (ChatGPT) API Tutorial – Teil #2 Modelle und Dall-E

Einleitung

Im Teil #1 der Tutorial-Reihe haben wir uns mit der Chat Completions API beschäftigt und einen Chatbot erstellt. Hier kommst du zum Teil #1: https://ki-zeitalter.de/openai-chatgpt-api-tutorial-teil-1/

In diesem Teil gebe ich dir erstmal eine Übersicht über die unterschiedlichen Modelle, die man auswählen kann. Die Modellauswahl kann Einfluss auf die Qualität der Antworten haben, aber auch auf die Antwortgeschwindigkeit und nicht zuletzt auf die Kosten.

Die Modelle

OpenAI bietet eine vielfältige Reihe von Modellen mit unterschiedlichen Fähigkeiten und Preisen an. 

GPT-4

GPT-4 ist eine Sammlung von Modellen, die auf GPT-3.5 aufbauen und natürliche Sprache oder Code verstehen und generieren können. GPT-4 befindet sich derzeit in einer eingeschränkten Beta-Version und ist nur für diejenigen zugänglich, die dafür Zugriffsrechte erhalten haben. Es handelt sich um ein großes multimodales Modell, das schwierige Aufgaben mit größerer Genauigkeit als jedes bisherigen Modelle lösen kann, dank seines breiteren Allgemeinwissens und fortgeschrittenen Schlussfolgerungsfähigkeiten.

GPT-3.5

Die Modelle GPT-3.5 können natürliche Sprache oder Code verstehen und generieren. Das leistungsfähigste und kosteneffektivstes Modell in der GPT-3.5-Reihe ist gpt-3.5-turbo, das für Chats optimiert wurde, aber auch gut für traditionelle Vervollständigungsaufgaben geeignet ist.

DALL·E

DALL·E ist ein AI-System, das realistische Bilder und Kunst aus einer natürlichen Sprachbeschreibung erstellen kann. Es unterstützen die Fähigkeit, ein neues Bild mit einer bestimmten Größe zu erstellen, ein vorhandenes Bild zu bearbeiten oder Variationen eines vom Benutzer bereitgestellten Bildes zu erstellen.

Whisper

Whisper ist ein Modell, das Audio in Text umwandeln kann. Es ist ein Allzweck-Spracherkennungsmodell, das auf einem großen Datensatz verschiedener Audiodaten trainiert wurde und auch Aufgaben wie mehrsprachige Spracherkennung, Sprachübersetzung und Sprachidentifikation erfüllen kann.

Embeddings

Embedding-Modelle können Text in eine numerische Form umwandeln. Sie sind nützlich für Suchaufgaben, Clustering, Empfehlungen, Anomalieerkennung und Klassifizierungsaufgaben.

Moderation

Das Moderationsmodell ist darauf ausgelegt, zu erkennen, ob ein Text sensibel oder unsicher sein könnte. Es bietet Klassifikationsfähigkeiten, die nach Inhalten in folgenden Kategorien suchen: Hass, Bedrohung, Selbstschädigung, sexueller Inhalt und Gewalt.

GPT-3

Die Modelle GPT-3 können natürliche Sprache verstehen und generieren. Diese Modelle wurden durch die leistungsfähigeren Modelle der GPT-3.5 Generation abgelöst.

 

Bildgenerierung mit Dall-E

Kommen wir jetzt zu dem spannenderen Teil des Tutorials. Ich zeige dir, wie wir mit der OpenAI API Bilder anhand einer Textbeschreibung erstellen können.

Dabei werden uns drei verschiedene Möglichkeiten angeboten: ein komplett neues Bild erzeugen, ein vorhandenes Bild wird erweitert und Variationen eines existierenden Bildes.

Zum Zeitpunkt der Erstellung dieses Tutorials befindet sich die API noch im Beta-Stadium, d.h. das eine oder andere könnte nicht richtig funktionieren oder Funktionalität wird geändert.

Über die API können wir uns Bilder in den Größen 256×256, 512×512 oder 1024×1024 Pixel erstellen lassen. Pro Anfrage können wir uns 1-10 Bilder erzeugen lassen.

Schauen wir uns das erste Beispiel an. In diesem Beispiel erzeugen wir ein neues Bild, das anhand unser „Prompts“ ein Bild erzeugt. Wie man alle Voraussetzungen für die Nutzung der API erfüllt, wie API-Key und die Installation der notwendigen Bibliotheken, erfährst du im ersten Teil der Tutorial-Reihe.

Erstellung eines Bildes anhand eines Prompts

import openai
openai.api_key = "Füge hier deinen API-Key ein!"
prompt = "Eine schöne Seenlandschaft"
response = openai.Image.create(
prompt=prompt,
n=1,
size="256x256"
)
image_url = response['data'][0]['url']
print(image_url)

Wenn alles gut gegangen ist, erzeugt der obige Code ein Bild mit den Maßen 256 x 256 Pixel. Das schöne ist, es versteht auch auf Deutsch geschriebene Prompts.

Als Ergebnis erhalten wir hier eine URL, die zum Anzeigen/Download des Bildes führt. Die URL wird hier im Beispiel lediglich ausgegeben, also einfach kopieren und im Browser einfügen. In meinem Fall habe ich dieses doch recht nette Ergebnis bekommen:

Da wir n=1 angegeben haben, hat uns Dall-E nur ein Bild erzeugt und damit haben wir auch nur eine URL als Ergebnis bekommen. Bei n > 1 hätten wir entsprechend viele URLs erhalten.

Mit dem Parameter response_format können wir auch angeben, dass wir das Bild als Base64 kodierte Daten bekommen wollen: response_format=“b64_json“. Dann entfällt das Laden des Ergebnisses von der URL

Übrigens kannst du über https://labs.openai.com/ deine Prompts ausprobieren und einige Beispiele anschauen.

Damit wir das Bild in unserer Anwendung verwenden könne, wäre es sicherlich nett, wenn wir das Bild mit Python auch herunterladen. Das geht zum Glück sehr einfach. Dazu erweitern wir das Beispiel folgendermaßen:

import requests
import os
def bild_herunterladen(url, dateiname):
# Die Bilddatei herunterladen
bild = requests.get(url)
# Die Bilddatei auf dem Laufwerk speichern
with open(dateiname, "wb") as datei:
datei.write(bild.content)
bild_herunterladen(image_url, 'bild-1.png')

Da die URL keinen Dateinamen enthält, geben wir den Dateinamen hier einfach selbst an.

Bildbereiche neu erzeugen lassen

Das war, finde ich, doch recht einfach bis jetzt. Schauen wir uns noch die anderen Varianten an. D.h. wir laden ein Bild hoch und lassen es editieren.

Damit Dall-E auch weiss, welche Bereiche des Bildes geändert werden sollen, muss man die entsprechenden Stellen transparent machen. Das geht meist recht einfach in einer Bildverarbeitung wie Photoshop oder Gimp usw. Oder wir nutzen dafür eine AI, wie https://removal.ai, mit dessen Hilfe wir den Hintergrund entfernen können. Genau das habe ich gemacht. Zuerst habe ich ein Bild von einer jungen Frau in einer Stadt generieren lassen:

Anschließend habe ich https://removal.ai genutzt, um den Hintergrund zu entfernen:

 

Und jetzt sind wir bereit Dall-E „zu bitten“ etwas neues in dem transparenten Bereich zu erzeugen.

 

import PIL.Image
import io
rgba_image = PIL.Image.open("woman_transparent.png")
rgba_image = rgba_image.convert('RGBA')
img_byte_arr = io.BytesIO()
rgba_image.save(img_byte_arr, format='PNG')
img_byte_arr = img_byte_arr.getvalue()
response = openai.Image.create_edit(
image=img_byte_arr,
prompt="Eine junge Frau in einer Kunstgalerie",
n=1,
size="512x512"
)
image_url = response['data'][0]['url']

Das Bild, das ich als Eingabe verwendet muss im RGBA-Format vorliegen, also ein Bildformat mit eine Alpha-Kanal für die Transparenz. Wir nutzen hier die PIL-Bibliothek um das Bild zunächst zu laden und dann in RGBA zu konvertieren.

Die API braucht aber die rohen Bytes der Bilddaten, daher nutzen wir io um aus dem PIL Bild die Bytes zu bekommen.

Zuletzt rufen wir die API-Funktion create_edit auf und geben einen Prompt mit. In meinem Beispiel möchte ich, dass die Frau in einer Kunstgalerie steht. Das Ergebnis, das ich erhalten habe sieht wie folgt aus:

Übrigens empfiehlt es sich im Prompt das gesamte Bild zu beschreiben, d.h. nicht nur den Hintergrund, sondern auch was sonst zu sehen ist. Das hilft der KI den Kontext zu verstehen.

Bildvariationen

Die letzte Variante lässt Dall-E das Bild modifizieren. Wir laden dazu ein Bild hoch aber diesmal haben wir keine Möglichkeit einen Prompt mitzugeben. Schauen wir uns den Code dazu an:

import PIL.Image
import io
rgba_image = PIL.Image.open("young-woman-edited.png")
img_byte_arr = io.BytesIO()
rgba_image.save(img_byte_arr, format='PNG')
img_byte_arr = img_byte_arr.getvalue()
response = openai.Image.create_variation(
image=img_byte_arr,
n=1,
size="512x512"
)
image_url = response['data'][0]['url']

Der Code ist dem vorherigen Beispiel sehr ähnlich. Nur in diesem Fall brauchen wir keine transparenten Bereiche und das Format muss nicht unbedingt im RGBA vorliegen.

Nachdem wir das Ausgangsbild geladen haben, rufen wir die create_variation Funktion auf. Wie zuvor erhalten wir hier eine Bild-URL und in meinem Fall sah das Ergebnis so aus:

Ok ok…. das Ergebnis ist, öhm sagen wir mal Dall-E hat sich große Mühe gegeben! wink

Aber wer weiß, wenn man ein paar Versuche startet, dann kommt womöglich etwas brauchbares heraus. Außerdem ist damit zu rechnen, dass die Modelle weiter trainiert werden und mit der Zeit besser werden. Dall-E befindet sich außerdem noch in der Beta-Phase.

Ende Teil #2

Das war Teil #2 der Tutorial-Reihe über die OpenAI API (ChatGPT). Ich hoffe es hat dir etwas gebracht. Ich hoffe man sieht, dass man mit nur wenigen Zeilen Code, etwas tolles kreieren kann! 

Ausblick

Im letzten Teil der Reihe zeige ich dir ein sehr cooles und relativ neues Feature: „functions“. Mit „functions“ können wir über die API unsere oder andere APIs praktisch einbinden. Das eröffnet ganz neue Möglichkeiten und wir können Funktionalitäten wie die ChatGPT-Plugins uvm. umsetzen!

OpenAI (ChatGPT) API Tutorial – Teil #3 Funktionen (Function calling) 

OpenAI (ChatGPT) API Tutorial – Teil #1

Einleitung

Du möchtest eine Anwendung erstellen, die ChatGPT benutzt? Dann ist dieses Tutorial für dich richtig. Hier lernst du die unterschiedlichen APIs von OpenAI kennen und zu nutzen.

Wir schauen uns an, wie man einzelne Prompts an die API schicken kann und Antworten alà ChatGPT zurück bekommt. Ich gebe dir eine Übersicht über die unterschiedlichen Sprachmodelle, die man verwendent kann. In einem erweitertem Beispiel zeige ich dir, wie man eine eigene Chatbot-Seite erstellt.

ChatGPT ist im Grunde genommen einfach nur eine Webanwendung, die im Hintergrund die OpenAI API nutzt. Es nimmt bei der Nutzung einige Voreinstellungen vor, die wir mit der API selbst definieren können. So werden wir uns den Spaß erlauben einen eigenen Chat zu schreiben, bei dem der KI-Assistent mürrisch antwortet. Wir können außerdem die Kreativität der Antworten beeinflussen.

Außerdem lernst du die weiteren APIs kennen, mit denen du Bilder mit Dall-E erzeugen kannst und wie man Sprache zu Text transkripieren kann.

Damit du alles aus der API rausholen kannst, empfähle ich dir mein Tutorial rund um die Prompts: Prompt Leitfaden: bessere Ergebnisse aus ChatGPT und Co holen

Einrichtung

Bevor du loslegen kannst, brauchst du für die Nutzung von der OpenAI API einen API-Key. Beachte, dass die Nutzung kostenpflichtig ist. Meiner Erfahrung nach entstehen nur sehr geringe Kosten, wenn man nur ein wenig ausprobiert. Bis jetzt bewegt sich das alles im Cent-Bereich. Beachte bitte das Kapitel, wo ich dir erkläre, wie man vermeiden kann zu hohe Kosten zu verursachen!

API-Key

Zunächst musst du dich auf https://platform.openai.com/ registrieren. Hier wirst du aufgefordert erstmal Zahlungsdaten zu hinterlegen.  Wichtig: schau dir alles an, vor allem die Preisinformationen.

Danach kannst du dir unter https://platform.openai.com/account/api-keys  einen API-Key anlegen. Ich empfehle für jede Anwendung etc. einen eigenen Key zu machen. Die Keys kannst du nämlich als ungültig markieren, z.B. nachdem du deine Tests abgeschlossen hast.

Wichtig: wenn dir der Key angezeigt wird, dann musst du ihn sofort kopieren und irgendwo notieren, denn er wird dir nicht noch einmal angezeigt. Im schlimmsten Fall musst du einen neuen anlegen.

Wichtige Einstellungen

Du kannst im Menü Billing/Usage limits festlegen, wie viel du maximal ausgeben möchtest. Stelle dort bevor du etwas probierst für dich akzeptable Werte ein.

 

Hier kannst du zwei Werte einstellen: „Hard limit“ und „Soft limit“.

Über „Hard limit“ stellst du, wie der Name schon sagt, ein hartes Limit ein. D.h. es wird maximal bis zu diesem Betrag abgebucht.

Das „Soft limit“ ist die Schwelle, die dazu dient, dich zu warnen. Sobald diese Grenze überschritten wird, wirst du per E-Mail informiert.

Stelle unbedingt für dich vernünftige Werte ein, bevor du mit deinen Versuchen startest!

Python

Die Beispiele, die ich in diesem Tutorial zeige, sind in Python geschrieben. Wenn du lieber eine andere Programmiersprache verwenden möchtest, so geht das natürlich auch. Du findest auf https://platform.openai.com/docs/libraries eine Übersicht über weitere Bibliotheken für andere Programmiersprachen. Während Python und Node.js von OpenAI direkt angeboten werden, gibt es für jede erdenkliche andere Sprachen eine Liste von Community-Bibliotheken.

Wenn du bei Python bleibst, dann hast du mehrere Möglichkeiten. Zum einen kannst du auf deinem Rechner Python installieren. Auf https://www.python.org/downloads/ findest du für dein Betriebssystem die passende Version. Folge dort einfach den Instruktionen für die Installation.

Wenn du lieber nichts installieren willst, dann gibt es im Internet einige Online-Möglichkeiten. Diese bieten dir meist kostenlos die Möglichkeit eine Python-Umgebung zu nutzen. Meist mit einem Online-Editor usw. Nutze eine Suchmaschine deiner Wahl und suche z.B. „python online programmieren“. Da wird einiges angeboten.

Ganz gut ist z.B. https://replit.com/. Anfangs ist die Seite etwas unübersichtlich und man muss erstmal schauen, wo es weiter geht. Hier suchst du den Button „Create a Repl“ und wählst Python als Sprache aus. Dann erhälst du eine Arbeitsumgebung mit Editor und die Möglichkeit den Code auszuführen.

Was ich allerdings am liebsten nutze, sind die Workspaces auf GitHub. Du musst die einen GitHub-Account anlegen (ist kostenlos) und dann ein Repository. In dem Repository kannst du dann einen Workspace erstellen. Darin hast du Visual Studio Code als Editor und eine Python-Laufzeitumgebung. 

 

 

 

Die Chat Completions API

Es gibt zwei unterschiedliche APIs mit denen wir durch Prompt-Eingaben Text generieren lassen können, die Completions API und die Chat Completions API. Die APIs unterscheiden sich leicht in der Nutzung. Dieses Tutorial wird nur die Chat Completions API nutzen, da diese alles abdeckt was man benötigt und von der Nutzung her intuitiver ist.

Wir schauen uns ein sehr einfaches Beispiel an, um ein Gefühl zu bekommen, wie die API funktionert.

Bevor wir loslegen können, müssen wir allerdings erst die OpenAI API für Python installieren. Hierfür verwenden wir pip.

$ pip install openai

Nachdem die Bibliothek installiert wurde, können wir mit dem Code anfangen und erstellen eine Python-Datei simple-example.py (fertigen Code findest du in meinem GitHub-Repository unter https://github.com/ki-zeitalter/openai-api-tutorial)

simple-example.py

import openai
openai.api_key = "Hier deinen API-Key einfügen"
prompt = "Welche APIs gibt es von OpenAI?"
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": prompt}
]
)
print(response.choices[0].message["content"])

Bevor du den Code ausführen kannst, musst du erst deinen zuvor erstellten API Key einfügen.

Führe das Python-Skript mit python simple-example.py aus und wenn alles gut gegangen ist, dann solltest du eine Ausgabe sehen, die in etwa so aussieht:

@ki-zeitalter $ python simple-example.py 
Es gibt mehrere APIs von OpenAI, einschließlich:
1. GPT-2 API: Mit dieser API können Entwickler auf den Textgenerationsdienst von OpenAI zugreifen, der auf der neuesten Version des GPT-2-Modells basiert.
2. DALL-E API: Diese API ermöglicht es Entwicklern, auf den DALL-E-Dienst von OpenAI zuzugreifen, der Bilder aus einer natürlichen Sprachbeschreibung generieren kann.
[...]

Schauen wir uns an, was hier passiert. Auf die Details der API gehe ich ein, wenn wir den Chat programmieren.

Zuerst haben wir mit dem import Befehl die OpenAI API importiert und anschliessend übergeben wir unseren API-Key an die Bibliothek.

Es erfolgt dann der Aufruf an die ChatCompletion API. Hier übergeben wir zwei Parameter model und messages.

Der model Parameter gibt an, welches zugrunde liegendes Sprachmodell verwendet werden soll. Wie du vielleicht von ChatGPT kennst, dort kannst du z.B. zwischen GPT-3.5 und GPT-4 wählen. Hier hast du einige weitere Möglichkeiten. Weiter unten gehe ich auf die unterschiedlichen Modelle ein.

Mit dem messages Parameter übergeben wir eine Liste von Nachrichten. In diesem Fall enthält die Liste nur einen Eintrag, nämlich unseren Prompt und eine Rolle.

Was es mit der Rolle auf sich hat, sehen wir in dem Chat-Beispiel später deutlicher. In diesem Fall übergeben wir die Rolle „user„. Damit sagen wir der API, dass diese Nachricht/Prompt von dem Benutzer eingegeben wurde.

Die Antwort

Zu guter letzt geben wir die Antwort aus. Hier greifen wir aber nur auf einen Teil der Antwort zu und geben es aus und zwar nur den Teil, der die eigentliche Textantwort von dem Sprachmodell. Die Antwort selbst enthält mehr Informationen, die wir uns ansehen können:

Die gesamte Antwort

{
"id": "chatcmpl-7UVUZC7EF2VG1nvehNZsjlR938uOS",
"object": "chat.completion",
"created": 1687505251,
"model": "gpt-3.5-turbo-0301",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "OpenAI bietet verschiedene APIs an, darunter:\n\n1. GPT-3 API: Eine Natural Language Processing (NLP)-API, die es erm\u00f6glicht, menschen\u00e4hnliche Texte zu generieren.\n2. DALL-E API: Eine Vision-API, die es erm\u00f6glicht, neue Bilder aus Textbeschreibungen zu generieren.\n3. Codex API: Eine API, die es erm\u00f6glicht, Codes in verschiedenen Programmiersprachen zu generieren und zu bearbeiten.\n4. API f\u00fcr maschinelles Lernen: Eine API, die es erm\u00f6glicht, Modelle f\u00fcr maschinelles Lernen zu trainieren und zu implementieren.\n5. API f\u00fcr Neuroevolution: Eine API, die es erm\u00f6glicht, neuronale Netze durch evolution\u00e4re Algorithmen zu trainieren.\n\nDiese APIs k\u00f6nnen von Entwicklern genutzt werden, um ihre Anwendungen oder Projekte mit hochwertiger KI- und NLP-Technologie zu verbessern."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 17,
"completion_tokens": 204,
"total_tokens": 221
}
}

In der Antwort sehen wir eine Reihe von Informationen. Die wichtigsten Informationen, die uns vermutlich interessieren, befinden sich in den Blöcken „choices“ und „usage„.

Innerhalb von „choices“ finden wir den Antworttext, so wie wir das weiter oben bereits gesehen haben. Zusätzlich gibt es das Feld „finish_reason„. Hier haben wir den Wert „stop“ erhalten. Das bedeutet, dass die Antwort vollständig erzeugt wurde. Für den Fall, dass ganze Verarbeitung die maximale Anzahl an Tokens überschreitet, würde man hier den Wert „length“ bekommen.

Wir sehen außerdem, dass „choices“ eine Liste ist. Das hat den Hintergrund, dass man theoretisch eine gewisse Anzahl an Antworten anfordern kann. Hier werden wir keinen Gebrauch davon machen.

Im Block „usage“ sehen wir wieviele Tokens verwendet wurden. Was Tokens sind, erkläre ich gleich. Hier wird unterschieden zwischen der Token-Anzahl für den Prompt und für die Antwort. In „total_tokens“ sieht man die Summer daraus. Diese Information kann hilfreich sein, um zu sehen, was das ganze kostet und ob wir uns der Obergrenze der Tokens annähern. Denn wir können diesen Chatverlauf ja weiter führen. Wie das geht, sehen wir im Bereich Chat.

Tokens

Wenn Sprachmodelle trainiert werden, dann werden sie mit zahlreichen Texten gefüttert. Daraus werden die Zusammenhänge gelernt.

Die einzelnen Wörter werden dabei „tokenisiert“. Das kann bedeuten, dass ein Wort einem Token entspricht, manche Wörter jedoch werden in mehrere Token aufgeteilt.

Bei der Antwortgenerierung läuft es ähnlich. Das Sprachmodell berechnet sehr vereinfacht gesagt das jeweils nächste Wort oder genauer das nächste Token.

Daher sehen wir in der Antwort, wieviele Tokens uns das gekostet hat. Die Preise für die Nutzung der API richten sich nach der Anzahl der Tokens. Dabei kosten die Tokens je nach verwendeten Modell unterschiedlich. Eine Preisübersicht findest du hier: https://openai.com/pricing. Dazu sei gesagt, dass sich die Preise pro Token unterhalb von einem Cent bewegen.

Neben dem Preis, spielt es auch eine Rolle, wie viele Tokens wir insgesamt für eine Anfrage maximal nutzen können. Da gibt es je nach Modell unterschiedliche Obergrenzen. Ist die Summe der Länge aus Anfrage und Antwort zu groß, dann wird die Antwort abgeschnitten. Hier kann es u.U. helfen die Anfrage in mehrere Teile aufzuteilen, wenn möglich. Hier findest du eine Übersicht über die Modelle und die jeweiligen Grenzen: https://platform.openai.com/docs/models.

Ein eigener (mürrischer) Chat

In diesem Abschnitt wollen wir eine einfach Webanwendung bauen, die so ähnlich funktioniert wie ChatGPT. Damit der Rahmen nicht gesprengt wird, halten wir die Anwendung möglichst einfach. Den fertigen Chat findest du in meinem GitHub-Repostory: https://github.com/ki-zeitalter/openai-api-tutorial. Checke gerne das Projekt aus, clone es und spiele damit rum oder erweitere es. Wenn du etwas cooles daraus baust, dann würde ich mich über eine Nachricht freuen! wink

Warum ist der Chat mürrisch? Einfach nur so. Damit will ich nur zeigen, was möglich ist. Du kannst es natürlich anpassen.

Aufbau

Die kleine Anwendung besteht aus einem einfachen Backend, das die Eingaben der Weboberfläche entgegen nimmt und an die OpenAI API sendet. Wir nutzen hier Flask als Webserver. Denke daran, dass wenn du vorhast etwas ernsthaftes zu bauen, dann ist Flask nicht die geeignete Wahl. Hier ist es allerdings aufgrund seiner Einfachheit, genau das Richtige.

Außerdem nutzen wir eine Handvoll weiterer Bibliotheken, die uns bei den Aufgaben helfen. Welche das sind, steht in der requirements.txt Datei.

Vorbereitung

Die notwendigen Bibliotheken müssen wir erstmal installieren. Das kannst du entweder händisch machen, indem du jede Bibliothek in requirements.txt mit pip install installierst oder du machst es direkt so:

pip install -r requirements.txt

Falls du hier „mitprogrammierst“ und nicht das Projekt aus GitHub ausgecheckt hast, dann muss du zuvor die requirements.txt Datei anlegen:

requirements.txt

openai==0.27.8
Flask==2.3.2
pyhton-dotenv==1.0.0

Frontend

Jetzt können wir mit dem Code anfangen. Zunächst legen wir im Unterverzeichnis „templates“ eine Datei mit dem Namen index.html an.

Zunächst legen wir den Header-Bereich der HTML-Datei an:

templates/index.html

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.7.0.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
<title>OpenAI API Tutorial - Eigener ChatGPT</title>
</head>
[...]
</html>

Im Header importieren wir zum einen Bootstrap und JQuery. Bootstrap, eine CSS-Bibliothek, hilft uns die Webseite ein wenig hübscher aussehen zu lassen. Auf die Details dazu, gehe ich hier nicht ein. Mehr Infos findest du auf https://getbootstrap.com/

JQuery ist eine JavaScript-Bibliothek, die uns später behilflich wird auf einzelne Elemente der Seite zuzugreifen um z.B. den Senden-Button zu deaktivieren während eine Übertragung läuft.

Jetzt widmen wir uns dem body zu. Zunächst fügen wir eine Überschrift ein und einen Bereich, wo wir die bisherigen Chat-Nachrichten ausgeben:

<body>
<div class="container">
<div class="row">
<div class="col">
<h1>OpenAI API Tutorial - Eigener ChatGPT</h1>
</div>
</div>
{% for entry in chat_entries %}
<div class="row">
<div class="col-md-1">
{{ entry['role'] }}
</div>
<div class="col">
<span style="white-space: pre-line">{{ entry['content'] }}</span>
</div>
</div>
{% endfor %}
[...]
</div>
</body>

Die for-Schleife iteriert über die chat_entries. Dabei handelt es sich um eine Variable, die im Backend-Code gehalten wird und durch Flask hier eingefügt wird. Jeder Eintrag enthält dabei die Felder für die Rolle („role„), also wer hat diesen Eintrag verfasst und den eigentlichen Text („content„).

Bei der Ausgabe des Inhaltes nutzen wir eine CSS-Style-Angabe „pre-line„. Die API liefert nämlich die Antworten als Plain-Text, also mit ganz normalen Zeilenumbrüchen. Diese werden ja in HTML ignoriert. Durch die Style-Angabe werden sie jedoch berücksichtigt.

Jetzt haben wir den Ausgabebereich definiert, fehlt noch eine Eingabemöglichkeit. Unterhalb der for-Schleife fügen wir also folgendes ein:

<form action="/send_prompt/" method="post" id="promptForm">
<div class="row">
<div class="col-md-1">user</div>
<div class="col-md-10">
<input class="form-control" id="prompt-input" type="text" placeholder="Gib hier deinen Prompt ein"
name="prompt" />
</div>
<div class="col-md-1">
<button id="sendButton" class="btn btn-primary" type="submit" name="Senden">Senden</button>
</div>
</div>
</form>

Wir haben hier also ein HTML-Formular, das die eingegebenen Daten an die Adresse „/send_prompt/“ sendet.

Wir haben ein Eingabefeld für den Prompt des Benutzers und einen Button zum Absenden.

Zu guter letzt wollen wir, wenn der Button gedrückt wird, dass der Button deaktiviert wird, damit während der Übertragung nicht noch eine Anfrage gesendet wird. Außerdem wäre eine Ladeanzeige ganz nett.

Das erreichen wir indem wir mit etwas JavaScript-Code das Absenden des Formulars abfangen. Außerdem muss BootStrap initialisiert werden, was wir hier auch erledigen. Folgenden Code fügen wir direkt nach dem Formular, vor dem </body>-Tag ein:

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
crossorigin="anonymous"></script>
<script>
$(document).ready(function () {
$("#sendButton").click(function () {
$(this).prop("disabled", true);
$(this).html(
`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`
);
$("#promptForm").submit();
});
});
</script>

Backend

Jetzt widmen wir uns dem Backend-Code, wo die eigentliche Arbeit passiert. Im Hauptverzeichnis des Projektes legen wir eine Datei mit dem Namen chat.py an.

Wir beginnen damit die notwendigen Bibliotheken zu importieren:

import openai
import os
from dotenv import load_dotenv, find_dotenv
from flask import Flask, render_template, request

Wir importieren, so wie wir das schon kennen, die openai Bibliothek. Ausserdem laden wir ein paar andere Bibliotheken, die benötigt werden. Die Bibliotheken „os“ und „dotenv“ ermöglichen es uns den API-Key über eine Umgebungsvariable zugänglich zu machen.

Die „flask“ Bibliothek bietet uns die Webserver-Funktionalität.

Danach müssen wir einiges an Initialisierung vornehmen:

load_dotenv(find_dotenv())
api_key = os.getenv('API_KEY')
openai.api_key  = api_key
app = Flask(__name__)

Zuerst suchen und laden wir mit load_dotenv(find_dotenv()) eine Datei mit dem Namen .env. Wir haben die Möglichkeit den API-Key per Umgebungsvariable, die wir mit Betriebssystem-Mitteln oder mittels .env zu seten.

Den ermittelten API-Key übergeben wir anschliessend an die openai Bibliothek.

Zuletzt erstellen wir eine Instanz von Flask.

Wir müssen jetzt angeben, was Flask eigentlich an den Webbrowser liefern soll, wenn wir die Seite aufrufen:

chat_entries = [{'role':'system', 'content':'Du bist ein genervter Chatbot. Du antwortest auf die Fragen, aber auf eine genervte Art und Weise'}]
@app.route('/')
def index():
return render_template('index.html', chat_entries = chat_entries)

Erstmal definieren wir die chat_entries Variable und initialisieren diese direkt mit einem Wert. Dieser Wert gibt an, dass die Rolle „system“ ein genervter Chatbot sein soll. Mehr dazu gleich.

Anschliessend definieren wir, dass wenn unser Webserver (ohne Pfad bzw. „/“) aufgerufen wird, dass dann die index.html angezeigt werden soll. Die zuvor angelegte Variable chat_entries machen wir dabei im Template bekannt.

Jetzt müssen wir den Empfangs-Endpunkt für die Formular-Eingabe definieren:

@app.route("/send_prompt/", methods=['POST'])
def send_prompt():
chat_entries.append(
{'role':'user', 'content': request.form["prompt"]}
)
print("Sende Prompt...")
response = send_prompt_to_openai()
print(f"response={response}")
chat_entries.append(response.choices[0].message)
return render_template('index.html', chat_entries=chat_entries);

Die Route definieren wir als „/send_prompt/“, wie wir es in dem Formular als Ziel definiert haben.

Jetzt fügen wir den Eingegebenen Text zu den chat_entries hinzu und geben dabei die Rolle „user“ an, da dieser Text ja von dem Benutzer stammt.

Anschliessend möchten wir die Prompts an OpenAI senden. Diese Funktion definieren wir gleich.

Die Antwort, die wir erhalten entspricht dem, was ich weiter oben beschrieben habe. Aus der Antwort extrahieren wir den „message“ Teil und fügen es den chat_entries hinzu. Der Aufbau der „message“ ist ja {‚role‘:’…‘, ‚content‘: ‚…‘} wie der initiale Wert, den wir weiter oben hinzugefügt haben.

Das bedeutet, dass wenn der Benutzer jetzt einmal einen Prompt abgeschickt hat, dass wir drei Nachrichten in der Liste haben: die Nachricht von der Rolle „system„, die Nachricht von dem Benutzer „user“ und die Antwort von OpenAI in der Rolle „assistant„. Diese drei Nachrichten zeigen wir jetzt auf der Weboberfläche an. Gibt der Benutzer nun noch einen Prompt ein und schickt ihn ab, dann haben wir nach der Antwort von OpenAI zusätzlich diese zwei Nachrichten in der Liste. So geht es praktisch immer weiter.

Der Hintergrund davon ist, dass OpenAI sich nicht merkt, was du bisher eingegeben hast. Du musst also jedes Mal die ganze Konversation übertragen. Auch die vorherigen Antworten. Diese Reihe von Nachrichten bilden also den Kontext. Das bewirkt also, dass man sich in dem nächsten Prompt auf die Nachrichten davor beziehen kann, z.B. um OpenAI zu bitten die vorherige Antwort ausführlicher zu machen usw.

Die initiale erste Nachricht, mit der Rolle „system“, gibt dem System an, wie sich der Assistent verhalten soll. Wenn du ChatGPT nutzt, dann wird dir das praktisch versteckt. Der Standard-Wert ist nämlich „You are a helpful assistant.“. Passe das einfach deinen Wünschen an.

An letzter Stelle dieser Funktion veranlassen wir Flask die Seite index.html zu zeigen und übergeben dabei wieder die Variable chat_entries.

Was noch fehlt, ist die Funktion zum Senden:

def send_prompt_to_openai(model="gpt-3.5-turbo", temperature=0):
response = openai.ChatCompletion.create(
model=model,
messages=chat_entries,
temperature=temperature
)
return response

Die Funktion zum Senden akzeptiert zwei Parameter „model“ und „temperature„, die jeweils aber mit Standardwerten vorbelegt werden.

Der Parameter model ist relativ selbsterklärend, denn damit können wir das Sprachmodell angeben.

Der Parameter temperature ist jedoch neu für uns. Mit temperature können wir steuern wie OpenAI antwortet. Bei temperature=0 antworten OpenAI relativ genau, d.h. die Antwort mit der größten Wahrscheinlichkeit wird zurück gegeben. Möchte man OpenAI etwas mehr Freiheit geben, dann kann man den Wert höher wählen, z.B temperature=0.5. Jetzt werden nicht unbedingt die wahrscheinlichsten Antworten geliefert und wenn man den gleichen Prompt wiederholt, dann gibt es mehr Variationen der Antwort. Man kann also sagen, man steuert hiermit die Kreativität. Tipp: möchte man möglichst genaue und „richtige“ Antworten bekommen, sollte man mit dem Wert 0 arbeiten. Möchte man jedoch etwas kreatives bekommen, dann wählt man einen höheren Wert.

Jetzt sind wir mit dem Chat fast fertig und müssen nur noch den Startpunkt für das Python-Skript festlegen:

if __name__ == "__main__":
app.run(host='0.0.0.0')

Beim Ausführen des Python-Skriptes wird dieser Code nun ausgeführt und Flask wird veranlasst einen Webserver zu starten.

Lege nun im gleichen Verzeichnis eine .env Datei an und trage dort deinen API-Key ein:

API_KEY=dein API-Key an dieser Stelle

Jetzt kannst du den Chat mit folgendem Befehl starten:

 

python chat.py

Die Anwendung startet und Flask wird einen Webserver auf http://localhost:5000 starten. Öffne diese URL im Browser und viel Spaß beim Chatten mit dem genervten Assistenten!

Ende Teil #1

Das war Teil #1 der Tutorial-Reihe über die OpenAI API (ChatGPT). Ich hoffe es hat dir gefallen!

Im nächsten Teil schauen uns die weiteren APIs an, mit denen man z.B. Bilder mit einem Prompt generieren kann uvm.

Ausserdem gibt es noch ein paar Hintegrund-Infos, die nützlich sein könnten, z.B. welche Modelle es gibt und was die Unterschiede sind.

Hier geht es zum zweiten Teil: https://ki-zeitalter.de/openai-chatgpt-api-tutorial-teil-2-modelle-und-dall-e/

 

Prompt Tutorial Teil #2: Wie schreibe ich Prompts für die Nutzung in der eigenen Anwendung

Prompt Tutorial Teil #2: Wie schreibe ich Prompts für die Nutzung in der eigenen Anwendung

Einleitung

Willkommen zum zweiten Teil meiner Tutorial-Reihe zum Thema: Wie schreibe ich vernünftige Eingaben für Chat-KIs um die optimalen Ergebnisse zu bekommen?

In diesem Teil wird es etwas technischer. Wir wollen uns anschauen, wie man die Antworten so gestaltet, dass man es in einer Anwendung oder einer App nutzen kann.

Wenn du noch keine oder wenig Erfahrung mit dem Formulieren von Prompts, dann schau dir auf jeden Fall Teil #1 der Reihe an, wo ich die Grundlagen erkläre und wertvolle Tipps gebe, wie man vernünftige Antworten bekommt. Hier geht es zum ersten Teil: Prompt Leitfaden: bessere Ergebnisse aus ChatGPT und Co holen

Exkurs: Die ChatGPT (OpenAI) API nutzen

Hinweis

Für dieses Tutorial brauchst du das nicht, das soll dir nur eine kurze Hilfe sein, falls du das schon mal programmatisch nutzen möchtest. Später wird es ein ausführliches Tutorial zum Thema OpenAI-API geben. Überspringe ruhig das Kapitel, wenn es für dich nicht relevant ist oder wenn es dir bereits alles bekannt ist.

 

API-Key

Noch ein Hinweis: um die API zu nutzen, brauchst du einen API-Key. Leider ist die Nutzung kostenpflichtig. Aber ich kann dich beruhigen, wenn man bloß ein bisschen probieren möchte, dann bewegen sich die Kosten im Cent-Bereich.

Zunächst musst du dich auf https://platform.openai.com/ registrieren. In deinem Profil findest du den Bereich für die API-Keys: https://platform.openai.com/account/api-keys. Hier wirst du aufgefordert erstmal Zahlungsdaten zu hinterlegen.  Wichtig: schau dir alles an, vor allem die Preisinformationen. Du kannst im Menü Billing/Usage limits festlegen, wie viel du maximal ausgeben möchtest. Stelle dort bevor du etwas probierst für dich akzeptable Werte ein.

Danach kannst du dir unter https://platform.openai.com/account/api-keys  einen API-Key anlegen. Ich empfehle für jede Anwendung etc. einen eigenen Key zu machen. Die Keys kannst du nämlich als ungültig markieren, z.B. nachdem du deine Tests abgeschlossen hast.

Wichtig: wenn dir der Key angezeigt wird, dann musst du ihn sofort kopieren und irgendwo notieren, denn er wird dir nicht noch einmal angezeigt. Gut, im schlimmsten Fall musst du halt einen neuen anlegen.

 

Voraussetzungen (Python)

Ich gehe hier nicht darauf ein, wie man Python installiert oder wie Python überhaupt funktioniert. Das mache ich in einem separaten OpenAI-API Tutorial.

Ich empfehle dir die kostenlose Version von Google Colaboratory (kurz Colab) zu nutzen. Dort kannst du Python Notebooks erstellen und das nette dabei ist, meist sind die notwendigen Bibliotheken vorinstalliert. Hier kommst du zu Google Colab: https://colab.research.google.com/

Du brauchst allerdings einen Google-Account.

 

 

 

 

Als erstes müssen wir die OpenAI-Bibliothek installieren:

$ pip install openai

Nachdem die Bibliothek installiert wurde können wir uns eine Python-Datei erstellen (wenn du ein Python-Notebook verwendest,  dann schreibst du einfach direkt den Code in die Zelle).

Wir fangen an und fügen folgendes hinzu:

import openai
openai.api_key = "Füge hier deinen API-Key ein!"
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo", 
messages=[
{"role": "user", "content": "Hallo ChatGPT, wie geht es dir?"}
]
)
print(response.choices[0].message["content"])

Führe das Python-Skript aus und wenn alles gut gegangen ist, dann solltest du eine Ausgabe sehen, die in etwa so aussieht:

Hallo! Als KI habe ich kein Bewusstsein und keine Emotionen, aber ich bin bereit, Ihnen zu helfen und Ihre Fragen zu beantworten. Wie kann ich Ihnen heute behilflich sein?

Mit dem Parameter „model“ kannst du übrigens angeben, welches Modell zum Ausführen verwendet wird. Hier habe ich das Modell „gpt-3.5-turbo“ gewählt, denn zum aktuellen Zeitpunkt ist das neuere 4.0 Modell für mich nicht verfügbar.

In der Liste „messages“ gibst du deinen Prompt an. Details dazu kommen in einem späteren Tutorial. Nur als kurze Anmerkung, man kann auch mehrere Prompts angeben. Die bauen dann aufeinander auf, d.h. das Prompt nutzt quasi die vorherigen Prompts als Kontext. Genauso, wie wenn du in ChatGPT mehrere Anfragen innerhalb eines Chats machst.

Grundlagen

Erstmal ist es wichtig, dass wenn wir nicht einfach die Antwort eins zu eins ausgeben möchten, dass wir die KI bitten nur die nackten Daten auszugeben. Ansonsten neigen die KIs dazu eine Erklärung auszugeben. Das ist zwar oft hilfreich wenn man den Chat nutzt, stört aber bei der maschinellen Verarbeitung.

Die erstmal einfachste Anweisung ist es der KI eine vorgegebene Menge an Antwortmöglichkeiten zu geben. Zum Beispiel möchten wir nur „JA“/“NEIN“ oder „true“/“false“ als Antwort haben. Man könnte auch Prozentangaben anfordern uvm. Alles abhängig davon was die Aufgabe ist.

Nehmen wir als Beispiel an, wir schreiben eine Anwendung, die die Kommentare einer Webseite darauf untersuchen möchte, ob es sich um eine Beschwerde handelt. Wir möchten also als Ergebnis eine Liste mit Kommentar-ID und dem Ergebnis „Beschwerde“ oder „Keine Beschwerde“ haben. Wir würden also über die API eine Liste von Kommentaren in einem für die KI verständlichen Format geben und natürlich mit dem passenden Prompt. Hier zeige ich das als Eingabe in ChatGPT:

Ein Prompt, der ChatGPT auffordert die Kommentare zu bewerten

Fast perfekt, denn ich hätte natürlich darum bitten sollen, dass nur die Bewertung ausgegeben wird. Aber mit diesem Ergebnis kann man diese einfache Anwendung realisieren.

Trennzeichen

Wenn wir das obige Beispiel uns anschauen, dann könnte sich daraus ein Problem ergeben. Wir haben die Kommentare der Nutzer einfach so in den Prompt eingefügt. Jetzt könnte natürlich sein, dass ein Kommentar scheinbare Anweisungen an die KI enthält. 

Das kann man durch die Verwendung von Trennzeichen vermeiden. Man sagt der KI also, dass die zu analysierenden Daten innerhalb (von uns beliebig auswählbaren) Trennzeichen stehen. Am Besten nutzt man dafür nicht unbedingt die gängigen Zeichen wie Anführungszeichen vor und nach dem Text. Schließlich könnten die Kommentare das ja auch enthalten. Besser wären wahrscheinlich so etwas:

Die zu analysierenden Daten sind innerhalb der Trennzeichen """-
"""
id=1,Kommentar="Schön hier!"
"""

Ausgabeformate

Kommen wir nun zu dem eigentlichen Tutorial (ab hier kannst du auch ganz normal mit dem Chat arbeiten, es ist nicht notwendig die API zu nutzen). Damit wir mit der Antwort besser arbeiten können, macht es durchaus Sinn ChatGPT (oder beliebige andere KI) ein bestimmtes Ausgabeformat vorzugeben.

Besonders hilfreich sind Formate wie JSON, CSV usw. Aktuell zum Erstellungszeitpunkt des Tutorials kann man nur Textformate erzeugen. Das liegt daran, dass kein Dateidownload angeboten wird. Ich gehe jedoch jede Wette ein, dass „bald“ etwas in die Richtung angeboten wird.

Lass uns ein Beispiel ansehen:

Ok das ist nicht optimal, denn wir bekommen jede Menge Erklärungen und sonstige Texte drumherum. Das würde uns stören, wenn wir nur den JSON-Inhalt nutzen möchten. 

Das Problem ist, dass ich nicht spezifisch genug war (siehe das erste Tutorial zum Thema Prompts schreiben).

Lass uns das besser formulieren:

Damit lässt sich auf jeden Fall viel besser arbeiten. Natürlich hätte ich noch genauer beschreiben müssen, was ich für Informationen haben möchte und in welcher Sprache.

Wir haben in dem vorherigem Beispiel keine Angaben darüber gemacht, welchen Inhalt und welche Struktur die JSON-Datei haben soll. Das kann man aber natürlich machen.

Zum einen kann ich einfach sagen, welche Felder ich haben möchte oder sogar noch besser, ich gebe ein Beispiel für das Format an:

Gar nicht so schlecht. Ok hier hat ChatGPT eine leichte Abweichung eingebaut, indem es das „Thema“ einfach „subject“ genannt hat. Mit noch ausführlicheren Prompt bekommt man das aber in den Griff. Fun-Fact: man findet teilweise Prompts die seitenlang sind um alles aus dem Modell herauszukitzeln… das ist halt manchmal notwendig!

Neben JSON können wir natürlich viele weitere Ausgabeformate anfordern und so könnten wir uns sogar eine HTML Seite erzeugen lassen.

Leider kann man noch keine Bilder oder Grafiken erzeugen lassen. Allerdings kann man wenigstens so etwas ähnliches hinkriegen, wenn man als Ausgabeformat SVG wählt. Das ist schließlich ein Textformat und somit kann man so etwas wie Diagramme erzeugen lassen.

Daten verarbeiten oder analysieren

Wir können nicht nur die Antwort in einem maschinenlesbaren Format ausgeben lassen, wir können auch strukturierte Daten verarbeiten lassen.

So können wir tabellarische Daten, z.B. als CSV-Datei oder andere Formate eingeben. Das geht natürlich etwas bequemer programmatisch über die API, aber wir schauen uns hier mal ein Beispiel an. Und zwar habe ich meinen Gas-Zählerstand in unregelmäßigen Abständen notiert. Mit Datum und aktuellem Zählerstand. Jetzt würde ich gerne sehen, wie sich mein Gasverbrauch entwickelt ansehen. Klar, findige Leute werfen das schnell in Excel rein und zaubern eins, zwei Formeln dahin und schon ist das Problem gelöst. Ich lasse mir lieber die Lösung von der KI erarbeiten.

Das Bild zeigt ein Beispiel für einen Prompt für die Analyse von Zählerständen

Hier habe ich den typischen Fehler gemacht und nicht angefordert, dass ich nur die Daten möchte. Für ein noch optimaleres Ergebnis hätte ich auch noch anfordern können, dass die Kalenderwochen, für die ich keine Daten habe, interpoliert werden. Aber ich denke es ist einigermaßen klar wie das funktioniert. Hier die entsprechende Antwort:

Das Bild zeigt die Analyse der Zählerstände durch ChatGPT

Jetzt brauche ich das nur als Datei speichern und kann mir in Excel ein Diagramm z.B. anzeigen lassen. Leider kann zu diesem Zeitpunkt ChatGPT keine Binärdaten erzeugen, ansonsten hätte ich das auch direkt anfordern können.

 

Fazit

Wir haben hier hilfreiche Prompts gesehen, wenn es darum geht die Antworten maschinell zu verarbeiten. Mit wenigen Worten können wir also etwas erreichen wofür wir mit herkömmlichen Mitteln aufwändige Programme schreiben müssten. Ich finde zumindest, dass das ganz neue Wege bietet!

In einem späteren Tutorial zeige ich dann, wie KIs bei der Programmierung helfen können. Sei es als Eingabehilfen, Erklärer oder um komplette Programme schreiben zu lassen.

Ausblick

Es existiert das sogenannte „Code interpreter Plugin“. Das ist zum jetzigen Zeitpunkt noch im Alpha-Stadium und leider erhalten nur wenige darauf Zugriff. Mit diesem Plugin kannst du ganze Exceldateien oder sonstige Dateiformate mit Daten hochladen und analysieren lassen.
Das beeindruckende dabei ist, dass man noch nicht einmal beschreiben muss um welche Daten es sich handelt. Das analysiert ChatGPT von ganz alleine und erzeugt ohne unser Zutun eine Analyse samt Diagrammen. Dabei greift es auf die Daten von Wolfram Alpha zu.

Inweiweit man das Plugin jedoch über die API nutzen kann, ist mir noch nicht bekannt.
Sobald ich Zugriff auf das Plugin bekomme, erstelle ich einen Blog-Post darüber.

Prompt Tutorial Teil #2: Wie schreibe ich Prompts für die Nutzung in der eigenen Anwendung

Prompt Leitfaden: bessere Ergebnisse aus ChatGPT und Co holen

Einleitung

Mit Sicherheit hast du wie viele andere schon mal ChatGPT oder Bard (aktuell ein Deutschland noch gar nicht verfügbar) genutzt. Vermutlich ging es dir dabei wie mir: du hast das Ding wie eine Suchmaschine benutzt! Ist ja auch klar, wir sind nichts anderes gewohnt.

Das Problem ist, wenn wir nur kurze Fragen oder Aufgabenstellungen eingeben, nutzen wir nur ein Bruchteil von dem Potential der LLMs (Larg Language Models – die Grundlage von ChatGPT usw.). Befolgt man ein paar „Regeln“, kann man wesentlich bessere Ergebnisse erzielen. Bei mir war das der Augenöffner. Nachdem ich gelernt habe, wie man mit Chat-KIs „sprechen“ muss, bekam ich erst den Eindruck davon was alles möglich ist…. Nun lange Rede, kurzer Sinn… starten wir mal!

Das Grundprinzip

Vorneweg, das wichtigste Prinzip ist es, der KI deine Erwartungen mitzuteilen. Es ist ja kein Mensch, der intuitiv mitdenkt und erraten kann was du benötigst.

Mache klare Anweisungen und spare nicht bei den Erklärungen. Je besser du beschreibst, was du willst und was du erwartest, desto besser wird das Ergebnis. Achte sogar auf korrekte Rechtschreibung. Zwar kommen die Chat-KIs mit Fehlern recht häufig gut zu recht, aber es schadet nicht.

Vergleichen wir mal folgende Beispiele. Bei dem ersten Beispiel werde ich eine typische Eingabe machen, so ähnlich wie eine Google-Suche.

 

 

Das Bild zeigt eine typische "Anfänger" Kommunikation mit ChatGPT

Ok zugegeben, die Antwort ist nicht schlecht. Aber wir hätten genauso den entsprechenden Wikipedia-Artikel aufrufen können.

Ich möchte aber, dass ChatGPT oder welche KI auch immer, mir das so erklärt, wie ich das gerne hätte. Starten wir noch einen Versuch…

Das Bild zeigt eine ausführlichere Eingabe, die bereits jetzt zu besseren Ergebnissen führt

Das zeigt glaube ich recht anschaulich, was möglich ist. 

Grundsätzliches Vorgehen

Wie anfangs erwähnt, ist es wichtig deine Erwartungen auszudrücken. Aber wie sollte man vorgehen und welche Möglichkeiten gibt es denn?

Grundsätzlich ist es gut, erstmal zu erklären, was man vor hat. Das steckt schon mal den groben Rahmen. Dann sollte man den Kontext benennen. Z.B. für wen die Information gedacht ist. Zu guter Letzt sollte man die zu erwartende Antwort beschreiben, also wie lang, welche Struktur etc.

Informationen, die hilfreich sind:

  • Worum geht es eigentlich?
  • Wer soll diese Information letztendlich bekommen (Zielgruppe)
  • Welchen Stil soll die Antwort haben? Professionell, humorvoll, lässig,…
  • Wie ausführlich soll die Antwort sein? 
  • Welches Format erwartest du? 

 

Analysen, Zusammenfassungen etc.

Neben dem Informationsabruf, wie z.B. „erkläre mir Dies und Jenes“, kann man auch vorhandene Texte verarbeiten lassen. Was ich zum Beispiel gerne mache, ist mir von langen Artikeln eine kurze Zusammenfassung erstellen lassen. Inzwischen beherrscht ChatGPT das Webbrowsing, das diese Aufgabe leichter macht.

Auch in diesem Fall ist es hilfreich anzugeben, wie du gerne diese Informationen aufbereitet bekommen möchtest. In dem du bestimmte Schlüsselwörter verwendest, bekommst du auch ganz unterschiedliche Ergebnisse.

Ich kann z.B. anstatt eine Zusammenfassung anzufordern schreiben: „Bitte analysiere den folgenden Artikel“. In diesem Fall werden nicht einfach die Vorhandenen Informationen zusammengefasst, sondern sie werden analysiert. D.h. die KI erzeugt also weitere Informationen. Beispiele für weitere Schlüsselwörter sind z.B. „Kritisiere“, „Erkläre“, „Vergleiche“ usw.

Ein Beispiel:

Das Bild zeigt wie man bestimmte Schlüsselwörter nutzen kann, wie z.B. "Kritik"

Ich mag übrigens die Art der Kritik, wie die KI einen negativen Artikel über KIs bewertet! cool

Apropos negativ. Wir können nicht nur Inhalte auf Fakten etc. analysieren lassen, wir können auch das Stimmungsbild ermitteln. Das könnte z.B. für ein Unternehmen hilfreich sein, das auf negative Kundennachrichten (automatisch) reagieren möchte.

Schauen wir uns dazu Beispiele an:

Dieses Bild zeigt ein Beispiel dafür, wie ChatGPT das Stimmungsbild von Aussagen bewertet

Das ist natürlich erstmal ein sehr einfaches Beispiel aber der Nutzen kann sehr groß sein. Sicher hast du bemerkt, dass ich die Ausgabe der Antwort so gestaltet habe, dass man das auch in einem Programm auswerten kann. In dem zweiten Teil der Tutorial-Reihe gehe ich auf die Besonderheiten ein, die für die Verwendung in einer Anwendung, wichtig sind. 

Rollen

Mein Tipp: nutze Rollen! Mit den Rollen hast du eine weitere Möglichkeit die Antworten zu gestalten. Das geht auch sehr einfach. Du sagst der Chat-KI deiner Wahl, dass sie die Rolle XY hat. Beispiel: „Du bist in der Rolle eines Reiseführers. Beschreibe mir bitte die interessantesten Sehenswürdigkeiten in Berlin“. Die Antwort wird dann dementsprechend diesen Stil haben.

Neben sicherlich nützlichen Rollen, wie z.B. „Lehrer“, „Wissenschaftler“, „Politiker“, kann man auch etwas humorvollere Rollen angeben. Probieren wir mal, ob ChatGPT als Pirat gute Figur macht.

 

Das Bild zeigt ein Beispiel, wie man Rollen nutzen kann

Ok ok, zurück zum Ernst! Mit Rollen hast du also ein starkes Instrument. Wenn du z.B. die Antworten eher kreativ haben möchtest, dann nutze z.B. Rollen wie „Künstler“ oder „Philosoph“. 

Komplexe Aufgaben lösen

Sprachmodelle wie ChatGPT sind in der Lage etwas komplexere Aufgaben zu lösen, wie z.B. Logik-Rätsel. Allerdings muss man einiges beachten, wenn man richtige Ergebnisse erzielen möchte. Schauen wir uns erst ein einfaches Rätsel an:

Beispiel für eine falsche Lösung eines Logik-Rätsels

Nun, diese Antwort ist nicht richtig. Die Kinder haben nur einen Euro übrig: 10€ – 2€ – 3€ – 3€ -1€ = 1€.

Was wir hier sehen ist ein Problem, das auftreten kann, wenn die KI „vorschnell“ zum Ergebnis kommt. Im Grunde genommen hat die KI schlichtweg die Zahlen subtrahiert, ohne wirklich Logik anzuwenden. Wie kann man das Problem in den Griff kriegen?

Man muss der KI auf die Sprünge helfen. Bei solchen Problemen hilft es eigentlich immer die KI zu bitten den Lösungsweg zu skizzieren. Dadurch erzwingt man das logische „nachdenken“ ein wenig. In einem weiteren Blog-Artikel zum Thema „Tree Of Thoughts“ gehe ich auf das Thema tiefer ein.

Lass uns die Aufgabe ein wenig anders formulieren und schauen wir, ob wir ein besseres Ergebnis bekommen.

Das Bild zeigt eine bessere Lösung für komplexere Aufgaben

Diese Antwort sieht wesentlich besser aus. Übrigens hat ChatGPT-3.5 es trotz Ermittlung des Lösungswegs das Ergebnis falsch berechnet. Damit sieht man auch, wie weit die KIs sich weiter entwickelt haben.

Halluzinationen

Als Halluzinationen bezeichnet man durch die KI ausgedachte Fakten. Das ist in der Tat ein großes Problem, das man immer „auf dem Schirm“ haben sollte. Die KIs neigen dazu, wenn Informationen fehlen, sich diese Informationen auszudenken. Dazu reicht eine Frage nach eine Ereignis, Produkt o.ä. das es nie gab. Beispiel:

Beispiel einer ausgedachten Information

Im Jahr 2017 gab es keine Landtagswahlen in Hessen. Hier hat die KI einfach das Ergebnis aus 2018 benutzt. In manchen Fällen denkt sich sogar die KI komplett fiktive Ergebnisse aus. Aus diesem Grund ist es immer ratsam das Ergebnis zu prüfen.
Um das Problem in den Griff zu bekommen, sollte man die KI bitten die Fakten vorher zu prüfen. In unserem Fall, sollten wir die KI prüfen lassen, ob 2017 überhaupt eine Landtagswahl stattfand.

Das Bild zeigt, wie man sogenannten Halluzinationen vermeiden kann

Gut die Antwort ist zwar stellenweise etwas unlogisch, da ich nach einem Ereignis frage, das vor 2021 stattfand. Aber immerhin hat die KI erkannt, dass etwas nicht richtig ist. Übrigens hat ChatGPT-3.5 weiterhin felsenfest behauptet, dass im Jahr 2017 eine Landtagswahl stattfand. frown

Teil zwei

Das war der erste Teil des Tutorials. Im nächsten Teil gehe ich auf etwas technischere Sachen ein. Wie man z.B. bei der Nutzung der API die Ergebnisse nutzbar gestalten kann. Wir werden auch verschiedene Ausgabe-Dateiformate erzeugen. Plus viele weitere Tipps und Tricks.

 

Hier geht es zum zweiten Teil der Tutorial-Reihe.