Im Beitrag „KI bloggt unabhängig über zufällige Themen: Der Aufstieg von ChatGPT“ haben wir gezeigt, wie man mit ChatGPT Blog-Beiträge mit Zufallsthemen erzeugen kann. Um den Beitrag dann wirklich veröffentlichen zu können, ist allerdings noch sehr viel Copy-Paste und Nacharbeit notwendig. Es gibt aber gute Nachrichten: Mit der OpenAI API (und in unserem Fall der WordPress REST API) lässt sich ein Großteil davon automatisieren und besser strukturieren.

Eine Übersicht und Dokumentation der OpenAI API findet ihr hier. Um die API verwenden zu können, benötigt ihr zusätzlich einen Account bei OpenAI. Prinzipiell ist die API kostenpflichtig und wird bzgl. GPT-Requests token-basiert abgerechnet – ihr könnt aber aktuell eine Trial-Version zum Testen verwenden. OpenAI stellt euch dabei 18$ für den Testzeitraum kostenlos zur Verfügung. Falls ihr wissen wollt, was ein Prompt bzw. eine Antwort in Token entspricht, dann könnt ihr das mit dem Tokenizer-Tool testen.

Vorgehensweise

Der Ablauf in unserem Python-Skript sieht in etwa so aus:

1. Generieren eines zufälligen Themas für den Beitrag

Hierbei soll mit einem geeigneten Prompt das zufällige Thema samt Metadaten generiert werden. Damit das ganze etwas Struktur enthält und auch für wiederholte Requests durchführbar ist, soll die Ausgabe von ChatGPT in JSON erfolgen. Wir benötigen folgende Informationen:

  • Beitragstitel
  • Focus-Keyphrase
  • SEO-Titel
  • Meta-Beschreibung
  • Image Prompt für das Beitragsbild

Zusätzlich wollen wir Beiträge in Englisch und Deutsch erzeugen, also müssen auch oben genannten Inhalte auch in beiden Sprachen generiert werden.

Schließlich müssen wir den JSON-Inhalt noch in eine Datei schreiben, damit wir später noch darauf zugreifen können. Im Dateinamen werden wir auch gleich die Anzahl an Token mit hinzufügen, welche für Request und Reply verbraucht worden sind

2. Den Inhalt des Beitrags erzeugen

Nachdem das Thema von ChatGPT gewählt worden ist, werden wir einen geeigneten Prompt zur Erzeugung des englisch-sprachigen Inhalts generieren. Dabei sollen einige Kriterien zur Lesbarkeit mitgegeben werden:

  • Schreibstil
  • Textlänge
  • Absatzlänge
  • Maximal Anzahl an Wörtern je Absatz
  • Die Focus-Keyphrase soll im Einleitungssatz vorkommen

Außerdem soll ChatGPT html-Tags für Unterüberschriften und Absätze einfügen.

Auch diese Antwort von ChatGPT soll wieder in eine Datei geschrieben werden incl. der verbrauchten Tokens.

3. Übersetzen des erzeugten Inhalts

Sobald der englisch-sprachige Inhalt erzeugt worden ist, muss dieser schließlich noch von ChatGPT ins deutsche übersetzt werden.

Den übersetzten Text werden wir wie gehabt auch als Datei samt der verbrauchten Token ablegen.

4. Mit der WordPress REST API die Entwürfe erzeugen

Um uns eine Menge Arbeit zu ersparen, wollen wir, soweit möglich, den erzeugten Content direkt an unseren Blog senden. Dies ist möglich mit der REST API von WordPress. Ihr benötigt dafür lediglich einen dafür konfigurierten Benutzer mit mindestens Autor-Rechten und zugehörigem Application Password:

Danach könnt ihr HTTP-Requests an euren Blog senden, um Inhalte auszulesen oder aber auch Beiträge zu erzeugen. Hier findet ihr die API-Dokumentation. Ihr könnt euch aber auch den Python-Code für die Requests – wie ich – direkt von ChatGPT oder Bing-Chat erzeugen lassen.

5. Eine Themen-Blacklist erzeugen

Es kann vorkommen, das ChatGPT wiederholt Inhalte für bereits generierte Themen erzeugt. Aus diesem Grund wollen wir noch eine Blacklist mit bereits generierten Themen erstellen, welche wir in Schritt 1 direkt mit berücksichtigen können.

Vorbereitung der Prompts

Im ersten Schritt wollen wir einige Hilfsfunktionen zum Generieren der Prompts implementieren:

import openai
import os
import json
import requests

openai.api_key = "<YOUR API KEY>"
openai.organization = "<YOUR ORGANISATION>"

def generate_text(prompt, system_hint):
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": system_hint},
            {"role": "user", "content": prompt},
        ]
    )

    return response

def get_topic_prompt(topic=None, blacklist=None):
    prompt = "Generate a "
    
    if topic is None:
         prompt += "random topic for a blog post "
    else:
         prompt += f"blog post for the topic ""{topic}"" "

    prompt += "in english and german. \
        Write an appropriate focus keyphrase with 4 words or less. \
        Write a short SEO title and a post title. \
        Write a prompt for an image creator. \
        Write a meta description with a maximum length of 156 characters."
    
    if not (blacklist is None):
        prompt += f" Do not use a topic from the following list: {blacklist}. "

    prompt += "Use the following json format as output: \
        { \
        \"focus_keyphrase_english\": \"\", \
        \"seo_title_english\": \"\", \
        \"post_title_english\": \"\", \
        \"image_prompt_english\": \"\", \
        \"meta_description_english\": \"\", \
        \"focus_keyphrase_german\": \"\", \
        \"seo_title_german\": \"\", \
        \"post_title_german\": \"\", \
        \"meta_description_german\": \"\" \
        }"
    
    system_hint = "You are a helpful assistant that writes blog posts."
    
    return prompt, system_hint
    
def get_content_prompt(topic):
    prompt = f"Write a journalistic blog post on the following topic: \
        {topic} \
        Leave out the title and begin with an introduction. Use appropriate subtitles. \
        Use html tags for subtitles and paragraphs. \
        The focus keyphrase should appear in the introduction. \
        A paragraph should not be longer than 200 words. \
        No more than 25 percent of the sentences should have more than 20 words. \
        The post should contain a maximum of 10 percent passive sentences. \
        The post should contain at least 600 words and have enough transition words."
    
    system_hint = "You are a helpful assistant that writes blog posts."

    return prompt, system_hint

def get_translation_prompt(target_language, content):
     prompt = f"Translate the following into {target_language} and keep existing html tags: {content}"

     system_hint = "You are a helpful assistant that translates texts from english to german."

     return prompt, system_hint

Der Code definiert vier Funktionen, die Aufgaben im Zusammenhang mit dem Schreiben von Blog-Posts erledigen.

Die Funktion generate_text() verwendet die OpenAI GPT-3-API, um einen Text auf Basis einer Anweisung und eines Systemhinweises zu generieren.

Die Funktion get_topic_prompt() generiert eine Anweisung zur Generierung eines Themas für einen Blog-Post, während get_content_prompt() eine Anweisung zum eigentlichen Schreiben des Blog-Posts mit entsprechenden Hinweisen zum Schreibstil generiert.

Schließlich generiert die Funktion get_translation_prompt() eine Anweisung zum Übersetzen von Text von Englisch in eine andere Sprache unter Beibehaltung vorhandener HTML-Tags.

Blacklist auslesen und das Thema erstellen

blacklist_filename = "blacklist.txt"

if not os.path.isfile(blacklist_filename):
    with open(blacklist_filename, 'w') as f:
        f.write('')

with open("blacklist.txt", "r") as f:
    blacklist = f.read()

if(len(blacklist) > 0):
    prompt_topic = get_topic_prompt(blacklist=blacklist)
else:
    prompt_topic = get_topic_prompt()

topic_response = generate_text(prompt_topic[0], prompt_topic[1])

topic = topic_response.choices[0].message.content
topic_tokens = topic_response.usage.total_tokens

topic_json = json.loads(topic)
focus_keyphrase = topic_json["focus_keyphrase_english"]

if not os.path.exists(focus_keyphrase):
    os.makedirs(focus_keyphrase)

topic_filename = f"TOPIC_{topic_tokens}.json"
topic_file_path = os.path.join(focus_keyphrase, topic_filename)

with open(topic_file_path, "w") as f:
        f.write(json.dumps(topic_json, indent=4))

with open("blacklist.txt", "a") as f:
        f.writelines(focus_keyphrase + ";")

Der Code prüft zunächst, ob eine Datei namens blacklist.txt existiert. Wenn nicht, wird eine leere Datei mit diesem Namen erstellt. Dann wird der Inhalt der blacklist.txt-Datei gelesen und in eine Variable gespeichert. Wenn die Blacklist nicht leer ist, wird eine Anweisung zur Generierung eines Themas unter Verwendung dieser Blacklist generiert. Andernfalls wird eine Anweisung ohne Blacklist generiert.

Anschließend wird der generierte Anweisungstext mit der generate_text()-Funktion an GPT-3 gesendet und die erste generierte Antwort wird verwendet, um das Thema und den Fokus-Schlüsselbegriff zu extrahieren. Dann wird ein Ordner mit dem Namen des Fokus-Schlüsselbegriffs erstellt, falls er noch nicht existiert. Eine Datei mit dem generierten Thema wird im JSON-Format im neu erstellten Ordner gespeichert. Schließlich wird der Fokus-Schlüsselbegriff der Blacklist hinzugefügt, indem er am Ende der blacklist.txt-Datei angehängt wird.

Den englischen Inhalt generieren

shortened_topic_json = {
    'focus_keyphrase_english': topic_json['focus_keyphrase_english'],
    'post_title_english': topic_json['post_title_english'],
    'meta_description_english': topic_json['meta_description_english']
}

prompt_content = get_content_prompt(json.dumps(shortened_topic_json))

content_response = generate_text(prompt_content[0], prompt_content[1])

content = content_response.choices[0].message.content
content_tokens = content_response.usage.total_tokens

content_filename = f"CONTENT_EN_{content_tokens}.txt"
content_file_path = os.path.join(focus_keyphrase, content_filename)

with open(content_file_path, "w") as f:
        f.write(content)     

In diesem Code werden Inhalte für einen Blog-Beitrag generiert und in eine Textdatei gespeichert. Zunächst wird ein verkürztes JSON-Objekt aus dem ursprünglichen Thema erstellt, das nur die Schlagwortphrase, den Post-Titel und die Meta-Beschreibung enthält. Dann wird ein Prompt für die Generierung von Inhalten unter Verwendung dieses reduzierten Themas erstellt. Die generierten Inhalte in einer Textdatei mit dem Namen CONTENT_EN_<Token-Anzahl>.txt gespeichert, wobei die Token-Anzahl aus der Antwort der generierten Inhalte stammt.

Den generierten Inhalt übersetzen lassen

prompt_translation = get_translation_prompt(target_language="german", content=content)

translation_response = generate_text(prompt_translation[0], prompt_translation[1])

translation = translation_response.choices[0].message.content
translation_tokens = translation_response.usage.total_tokens

translation_filename = f"CONTENT_DE_{translation_tokens}.txt"
translation_file_path = os.path.join(focus_keyphrase, translation_filename)

with open(translation_file_path, "w") as f:
        f.write(translation)  

Dieser Code-Abschnitt erstellt eine Übersetzung des zuvor generierten Inhalts von Englisch nach Deutsch. Dazu wird ein Text-Prompt erstellt, der an den OpenAI-Dienst gesendet wird, um eine maschinelle Übersetzung zu erhalten. Das Ergebnis wird in einer Datei im gleichen Verzeichnis wie die ursprüngliche englische Version gespeichert.

Erzeugung der Entwürfe

url = 'https://<YOUR DOMAIN>/wp-json/wp/v2/posts'
headers = {'Content-Type': 'application/json'}
auth = ('<YOUR USER>', '<YOUR PASSWORD>')

data = {
    'title': topic_json["post_title_english"],
    'content': content,
    'status': 'draft',
    'categories': '<YOUR CATEGORY ID OR LIST OF IDs>',
    'lang': 'en'
}

requests.post(url, headers=headers, auth=auth, data=json.dumps(data))

data = {
    'title': topic_json["post_title_german"],
    'content': translation,
    'status': 'draft',
    'categories': '<YOUR CATEGORY ID OR LIST OF IDs>',
    'lang': 'de'
}

requests.post(url, headers=headers, auth=auth, data=json.dumps(data))

In diesem Codeabschnitt werden zwei POST-Anfragen an eine WordPress-REST-API gesendet. Die Variable url enthält die URL zur REST-API. In der Variable headers sind die HTTP-Header definiert, die in der Anfrage gesendet werden sollen, und in der Variable auth werden die Anmeldedaten des Benutzers angegeben.

Die Daten, die in den POST-Anfragen gesendet werden, sind in den Variablen data definiert. In der ersten Anfrage wird der englische Titel und Inhalt des Beitrags, der Status als Entwurf sowie eine Kategorie-ID und die Sprache en angegeben. In der zweiten Anfrage wird der deutsche Titel und Inhalt des Beitrags, der Status als Entwurf sowie eine Kategorie-ID und die Sprache de angegeben.

Fazit

Mit diesem Code haben wir uns jede Menge Arbeit gespart und gleichzeitig gezeigt, wie man die OpenAI API hinsichtlich der GPT-Modelle verwenden kann, um programmatisch und dialogbasiert ChatGPT-Inhalte verarbeiten kann.

Mit diesem Skript sind wir zwar noch nicht ganz am Ende, denn die Metadaten für die SEO-Optimierung müssen noch manuell ergänzt werden und die Übersetzungen müssen in WordPress auch noch miteinander verknüpft werden. Zusätzlich muss das Beitragsbild noch generiert werden.

Aber genau aus diesem Grund wurden die Beiträge als Entwurf angelegt. Damit ist es möglich, noch Feintuning zu betreiben und vor allem sich vor Veröffentlichung noch einmal den generierten Inhalt ansehen zu können. Denn letztendlich wollen wir ja doch noch ein Stück Kontrolle über die KI behalten.

Alles in allem bietet sich die OpenAI API für unterschiedlichste Einsatzszenarien an, um Abläufe mit Unterstützung von künstlicher Intelligenz zu automatisieren. Mit diesem Beitrag konnten wir euch hoffentlich einen von vielen Einsatzzwecken aufzeigen und euch auch für die Möglichkeiten begeistern, welche sich durch den API-basierten Zugriff ergeben!