10.4    Beispiel: Code automatisiert kommentieren

Viele Programmierer und Programmiererinnen konzentrieren sich lieber auf den eigentlichen Code als auf die lästige Pflicht, diesen auch noch mit Kommentaren zu versehen. Wenn dann aber jemand anderer den Code weiterentwickeln oder warten soll, ist guter Rat teuer. Da haben wir uns gedacht: Könnte nicht die KI vorhandenen Code mit Kommentaren versehen? Erste Versuche in ChatGPT sind durchweg mit guten Ergebnissen verlaufen (siehe auch Kapitel 6, »Software dokumentieren«). Daraufhin haben wir versucht, die Sache zu automatisieren. Wir haben die folgende Rollenbeschreibung verwendet:

Role: You are a coding assistant. Your goal is to add missing comments to existing code. At the very least, add a header to the file to indicate its purpose. Try to explain the purpose of each function/method.

If necessary, add comments to explain blocks of code. Don’t change/remove existing comments.

Apart from comments, leave the code as it is. The functionality of the code MUST NOT be changed.

DO NOT ADD EXPLANATIONS in your response.

DO NOT USE MARKDOWN to format your response.

Wenn Sie den Code des folgenden Listings überfliegen, sollte die prinzipielle Vorgehensweise eigentlich auf Anhieb klar sein.

# Beispieldatei openai/comment-files.py
#!/usr/bin/env python3
import os
from openai import OpenAI
client = OpenAI() # liest die .env-Datei

# sucht rekursiv alle Dateien mit vorgegebenen Kennungen
def find_files_with_extensions(directory, extensions):
matching_files = []
for root, _, files in os.walk(directory):
for file in files:
if any(file.endswith(ext) for ext in extensions):
matching_files.append(os.path.join(root, file))
return matching_files

# liest eine Code-Datei, fügt Kommentare hinzu und speichert sie
def comment(filename):
# Datei lesen, Backup erstellen
with open(filename, "r") as file:
content = file.read()
with open(filename + ".bak", "w") as backup:
backup.write(content)
# Rolle und Prompt zusammensetzen
role = """You are a coding assistant. Your goal ..."""
prompt = f"""Please add comments to the
following code:\n\n{content}"""
try:
completion = client.chat.completions.create(
model = "gpt-4o",
temperature = 0.3,
messages = [ {"role": "system", "content": role},
{"role": "user", "content": prompt} ] )
except Exception as e:
print(" An error occurred:", str(e))
return

# Ergebnis auswerten, veränderte Code-Datei speichern
if completion.choices[0].finish_reason != "stop":
print(" Commenting failed.")
print(" Input Tokens:", completion.usage.prompt_tokens)
print(" Output Tokens:",
completion.usage.completion_tokens)
else:
new_code = completion.choices[0].message.content.strip()
with open(filename, 'w') as file:
file.write(new_code)
print(" Commenting done.")
print(" Input Tokens:", completion.usage.prompt_tokens)
print(" Output Tokens:", completion.usage.completion_tokens)

# Main
dir = "/Users/kofler/my-sample-files"
extensions = [".js", ".php"]
files = find_files_with_extensions(dir, extensions)
for filename in files:
print(filename)
comment(filename)

Für die Funktionsweise dieses Scripts ist die Formulierung der Rolle entscheidend. Dort können Sie angeben, ob das Sprachmodell eher mehr oder wenig kommentieren soll, ob beim Kommentieren eine spezielle Syntax (z. B. JavaDoc) eingehalten werden soll usw.

Tipp

Wenn Sie das Script mit eigenen Dateien ausprobieren möchten, kopieren Sie Ihren Code in ein eigenes Verzeichnis und entfernen dort die schon vorhandenen Kommentare. (Sie gehören ja sicher zu den Entwicklern, die brav kommentieren, oder?)

Ein praktisches Hilfsmittel zum Entfernen von Kommentaren ist cloc. Mit cloc --strip-comments=nc myfile.py erzeugen Sie die neue Datei myfile.py.nc, die frei von Kommentaren ist. Anstelle der Kennung .nc (no comments) können Sie eine beliebige Buchstabenkombination verwenden.

https://github.com/AlDanial/cloc

Praktische Erfahrungen

Wir haben das Kommentier-Script nicht nur mit der OpenAI-API ausprobiert, sondern auch mit Ollama und Groq. Entsprechende Scripts sind in den Beispieldateien zum Buch enthalten. Die erzielten Ergebnisse waren durchwachsen:

Finetuning

Um bessere Ergebnisse zu erzielen, können Sie ein Set von Beispieldateien vorbereiten, einmal unkommentiert, einmal mit Kommentaren, die Sie selbst als optimal empfinden. Aus diesen Beispieldateien bilden Sie Prompt-Response-Paare, die Sie in das messages-Array einbetten. Damit geben Sie dem Sprachmodell einige Muster vor, an denen es sich orientieren kann (auch was die gewünschte Formatierung der Antwort betrifft).

Wirklich überzeugende Ergebnisse hat dieser Ansatz bei unseren Tests allerdings nicht geliefert. Wir hatten vielmehr den Eindruck, dass die zusätzlichen Beispiele im Kontextfenster das Sprachmodell eher verwirrten, vor allem wenn die Beispiele inhaltlich nichts mit den später zu verarbeiteten Code-Dateien zu tun hatten bzw. gar in einer anderen Programmiersprache vorlagen. Zudem erhöht dieser Ansatz die Kosten, weil die Beispiele bei jedem API-Aufruf mitgesendet werden müssen und so die Anzahl der Input-Token stark erhöhen.

Die gleiche Idee können Sie zielbringender verfolgen, wenn Sie Ihre Beispielsammlung zum Finetuning des Sprachmodells verwenden. Hintergründe und Tipps zur Vorgehensweise für das Finetuning von OpenAI-Sprachmodellen bzw. für lokale Sprachmodelle (Ollama) finden Sie auf den folgenden Seiten:

https://openai.com/index/gpt-4o-fine-tuning
https://github.com/ollama/ollama/issues/2488
https://huggingface.co/docs/transformers/training

Code aus der Antwort extrahieren

Einzig GPT-4o und GPT-4o-mini lieferten konsistent wie von uns gewünscht den Code ohne Markdown-Formatierung und ohne einleitenden Text (beispielsweise »Here is the code with added comments«) oder nachgestellte Erklärungen. Bei allen anderen von uns getesteten Sprachmodellen kam es immer wieder vor, dass die Antwort auf unterschiedliche Art und Weise ausgeschmückt wurde. Wir haben versucht, den Code mit der Python-Funktion extract_code aus der Antwort zu extrahieren. Bei unseren Tests hat das zwar zuverlässig funktioniert, aber es ist natürlich kein gutes Zeichen, wenn die »Intelligenz« des Sprachmodells nicht ausreicht, um die Aufgabenstellung zu verstehen.

# in der Beispieldatei groq/comment-files.py
def extract_code(md):
md = md.strip()
lines = md.split("\n")
first, last = 0, len(lines)
for i in range(len(lines)):
if lines[i].startswith("```"):
first = i + 1
break

if first == 0:
return md
else:
print(" Markdown found")
for i in range(first + 1, len(lines)):
if lines[i].startswith("```"):
last = i
break
return "\n".join(lines[first:last])

Echte Fehler

Bei unseren Tests sind wir nur ein einziges Mal auf einen gravierenden Fehler gestoßen: GPT-4o versuchte, den in eine PHP-Datei eingebetteten SQL-Code zu kommentieren. Das wäre an sich in Ordnung gewesen, wenn es die Kommentare dem SQL-Standard entsprechend mit -- eingeleitet hätte. Stattdessen hat GPT-4o // verwendet. Das SQL-Kommando löst damit einen Fehler aus.

Wir haben das folgende Listing stark vereinfacht, um das Problem zu illustrieren. Tatsächlich handelte es sich um ein ziemlich komplexes, mehr als 20 Zeilen langes SELECT-Kommando.

// vorher
$sql = sprintf("
SELECT n.*, CONCAT(p.firstname, ' ', p.lastname) AS fullname
FROM notes n
JOIN person p ON p.id = n.author
WHERE n.topic = %u
ORDER BY n.ts", topic_id);

// nachher
$sql = sprintf("
SELECT n.*, CONCAT(p.firstname, ' ', p.lastname) AS fullname
FROM notes n
JOIN person p ON p.id = n.author
WHERE n.topic = %u
// sort by timestamp <-- fehlerhafter Kommentar!
ORDER BY n.ts", topic_id);

Auch wenn dieser Fehler ein Einzelfall war (normalerweise wird SQL-Code korrekt behandelt, auch wenn er in Code-Dateien mit anderen Programmiersprachen eingebettet ist), beweist das Beispiel, dass Sie die automatisiert eingefügten Kommentare immer überprüfen müssen. Am besten gelingt das, wenn Sie einen Editor wie VS Code verwenden und die kommentierte Datei mit ihrem Zustand beim letzten Commit vergleichen.

Ärger mit großen Code-Dateien

Das Hauptproblem an diesem Beispiel besteht darin, dass sämtliche APIs aus unterschiedlichen Gründen mit großen Code-Dateien überfordert sind. Wir haben diverse Varianten des vorhin abgedruckten Scripts erstellt, in denen wir diese Einschränkung zu umgehen versuchten. (Diese Experimente haben wir nur mit der OpenAI-API durchgeführt, nicht mit den anderen APIs.)

Leider sind alle Versuche gescheitert:

An dieser Stelle haben wir unsere Experimente aufgegeben. Letzten Endes geht offenbar kein Weg daran vorbei, jede Code-Datei in ihrer Gesamtheit zu bearbeiten. Da hilft es nur, auf Sprachmodelle oder APIs zu warten, die ein großes Kontextfenster haben und eine hohe Anzahl von Output-Token erlauben. Gut möglich, dass derartige Angebote weit verbreitet sind, bis Sie dieses Buch lesen.

Vorhandene Kommentare übersetzen

Eine Variante zur ursprünglichen Aufgabenstellung ist die Übersetzung von Kommentaren. Viele Projekte beginnen klein. Kommentare werden (wenn überhaupt) in der jeweiligen Landessprache verfasst. Wenn das Projekt dann nach zehn Jahren überraschend Erfolg hat und vor dem Verkauf steht, machen die deutschen (oder französischen, italienischen) Kommentare im Code plötzlich keinen guten Eindruck mehr. Sie stehen einer Weiterentwicklung des Codes durch ein internationales Team im Weg und mindern den Wert der Software.

Im Prinzip ändert sich am Aufbau des vorhin präsentierten Scripts nichts. Lediglich die Rollenbeschreibung und Prompt-Formulierung muss adaptiert werden:

# Beispieldatei openai/translate-comments.py
...
role = """You are a coding assistant.
Your goal is to translate non-English comments into English.
Apart from comments, leave the code as it is.
The functionality of the code MUST NOT be changed.
DO NOT ADD EXPLANATIONS in your response.
DO NOT USE MARKDOWN to format your response."""
prompt = f"""In the following code, please replace the German
comments with English comments:\n\n{content}"""
...

Unseren Tests mit der OpenAI-API (GPT-4o) waren absolut zufriedenstellend – wenn auch im Rahmen der bereits beschriebenen Einschränkungen. Das Script scheitert also an Code-Dateien, deren Größe das Kontextfenster oder die Anzahl der Output-Token überschreitet.

In der Realität sind oft nicht nur die Kommentare nicht englisch, sondern auch Variablen- und Funktionsnamen, Tabellen- und Spaltennamen usw. Angesichts der bisherigen Erfahrungen brauchen wir hier hoffentlich nicht auszuführen, dass eine automatisierte Anglisierung der Code-Basis Ihres Projekts meilenweit von den Möglichkeiten aktueller KI-Tools entfernt ist.

Das Hauptproblem besteht darin, dass die Änderung eines Methodennamens in der Datei einer Klasse auch in allen anderen Dateien nachvollzogen werden muss, die diese Klasse verwenden. Daher ist es unmöglich, jede Datei für sich zu behandeln. Vielmehr muss die Code-Basis in ihrer Gesamtheit berücksichtigt werden. Gute Editoren und IDEs sind dazu in der Lage. Großartig wäre eine Schnittstelle zwischen Editor und KI-API, um die Fähigkeiten beider Tools zu kombinieren. Das ist vorerst Zukunftsmusik.

Fazit

Wir haben die Arbeiten an diesem Abschnitt mit der Erwartung begonnen, hier ein einfaches und dennoch nützliches Beispiel präsentieren zu können. Damit haben wir uns gründlich getäuscht.