Von der Spezifikation zum Code: Methodische Erkenntnisse aus der LLM-gestützten Entwicklung eines Text-Stil-Editors#
Teil einer Serie über methodische Erkenntnisse aus LLM-gestützten Entwicklungsprojekten
Einleitung#
Über mehrere Monate hinweg wurden verschiedene Tools mit LLM-Unterstützung entwickelt, primär als Lernprojekte zur Exploration von Möglichkeiten und Grenzen des LLM-gestützten Codings. Der vorliegende Beitrag dokumentiert eines dieser Experimente: die Entwicklung eines Text-Stil-Editors. Der Fokus liegt dabei nicht auf dem Tool selbst, sondern auf den methodischen Erkenntnissen, die sich aus dem Entwicklungsprozess ergaben.
Das Experiment hatte einen konkreten Vorläufer: Ein profilbasiertes Stil-Transformations-Tool, das Stilmerkmale aus Referenztexten extrahierte und auf neue Texte anwendete. Die Erfahrungen mit diesem Vorgänger führten zu der Frage, ob ein alternativer Ansatz – direktes Einstellen von Stilparametern statt Stilextraktion – praktikablere Ergebnisse liefern könnte.
Das Tool: Konzept und Funktionsweise#
Grundidee#
Der Text-Stil-Editor verfolgt einen reglerbasierten Ansatz zur Texttransformation. Statt Stile aus Referenztexten zu extrahieren und zu kopieren, stellen Nutzer die gewünschten Stilmerkmale über Schieberegler direkt ein. Das Konzept basiert auf der Überlegung, dass viele Anwendungsfälle keine exakte Stilkopie erfordern, sondern eine gezielte Anpassung einzelner Stilparameter.
Zweistufiger Prozess#
Die Transformation erfolgt in zwei optionalen Schritten:
Schritt 1 – Neutralisierung: Der Eingabetext wird von stilistischen Eigenheiten befreit. Zehn Dimensionen stehen zur Auswahl: Tonalität, Register, Wortwahl, Redundanz, Subjektivität, Perspektive, Intensifizierer, Struktur, Metaphorik und kulturelle Referenzen. Die Dimensionen sind in “sichere” und “experimentelle” kategorisiert. Die Unterscheidung basiert auf der Vermutung, dass zu aggressive Neutralisierung die Bedeutung des Textes verfälschen könnte. Die experimentellen Dimensionen (Struktur, Metaphorik, kulturelle Referenzen) greifen stärker in den Textinhalt ein und werden daher mit Vorsicht behandelt.
Schritt 2 – Stilisierung: Über 34 Regler in sieben Kategorien werden die gewünschten Stilmerkmale eingestellt. Die Regler decken folgende Bereiche ab:
- Ton und Emotion (emotional, freundlich, optimistisch, heiter, liebevoll, dramatisch)
- Formalität und Höflichkeit (förmlich, höflich, direkt, persönlich, selbstsicher)
- Klarheit und Verständlichkeit (einfach, präzise, Klarheit, sachlich, verworren, Hedging, nominal, abstrakt)
- Kreativität und Stilmittel (poetisch, magisch, ironisch, sarkastisch, lustig, provokant, nostalgisch)
- Überzeugung und Rhetorik (überzeugend, motivierend)
- Format und Struktur (Länge, strukturiert, aktiv)
- Zielgruppe und Niveau (Sprachniveau, Zielgruppenalter, fachsprachlich)
Reglertypen#
Das System unterscheidet drei Reglertypen:
Polare Regler spannen ein Spektrum zwischen zwei Gegenpolen auf, etwa “förmlich” bis “informell” oder “emotional” bis “nüchtern”. Der Wertebereich reicht von -10 bis +10, wobei negative Werte den einen Pol verstärken, positive Werte den anderen.
Intensitätsregler steuern die Ausprägung eines einzelnen Merkmals von 0 (aus) bis 10 (maximal). Beispiele sind Ironie, Poetik oder Sachlichkeit.
Stufenregler bieten diskrete Optionen. Das Sprachniveau etwa kann auf A1, A2, B1, B2, C1 oder C2 gesetzt werden, die Zielgruppe auf verschiedene Altersgruppen.
Intensitätsstufen#
Ein zentrales Element sind die Intensitätsstufen, die bestimmen, wie stark ein Stilmerkmal umgesetzt wird. Die Abstufung reicht von “leicht” (Werte 1-2: “einen Hauch hinzufügen”) über “moderat” (3-4), “deutlich” (5-6), “stark” (7-8) bis “extrem” (9-10: “maximal übertrieben, auf die Spitze getrieben”). Diese Abstufung war nicht Teil der initialen Spezifikation, sondern entstand durch Tests, als sich zeigte, dass die Stilmerkmale ohne explizite Intensitätsangaben nicht deutlich genug umgesetzt wurden.
Presets und Persistenz#
Für den schnellen Einstieg stehen 23 Presets zur Verfügung, die vordefinierte Regler-Kombinationen für typische Anwendungsfälle bereitstellen – von “Akademische Arbeit” über “Business-E-Mail” bis “Märchen” oder “Zynischer Kommentar”. Zusätzlich können Einstellungen als Hash-Code exportiert und später wiederhergestellt werden. Diese Funktion wurde nachträglich ergänzt, als sich zeigte, dass das Tool gut funktioniert und erfolgreiche Einstellungen persistiert werden sollten.
Der Entwicklungsprozess#
Ausgangssituation und Motivation#
Das Experiment entstand aus der Erfahrung mit einem Vorgängerprojekt. Dieses hatte einen profilbasierten Ansatz verfolgt: Stilmerkmale wurden aus Referenztexten extrahiert (etwa aus Texten von Goethe, Steve Jobs oder Edgar Wallace) und als JSON-Profile gespeichert. Diese Profile enthielten detaillierte linguistische Metriken wie durchschnittliche Satzlänge (in Wörtern), Varianz der Satzlänge, Komplexität (Nebensätze pro Satz, Verschachtelungstiefe), Passiv-Anteil, Nominalstil-Score, Hedging-Level, Konnektoren-Häufigkeiten (kausal, adversativ, temporal), Fremdwörter-Anteil, Abstraktionsgrad und Formalitätsgrad.
Ein Beispielprofil für Goethe etwa enthielt Angaben wie “28,4 Wörter durchschnittliche Satzlänge”, “Komplexität: hoch”, “Passivanteil: 13%”, “Nominalstil: 68%”, “Hedging-Level: mittel” sowie detaillierte Konnektoren-Verteilungen (temporal 42%, kausal 28%, adversativ 18%, konditional 12%). Zusätzlich wurden repräsentative Beispielsätze und ein generierter Transformations-Prompt gespeichert.
Der Ansatz funktionierte technisch, hatte aber praktische Einschränkungen: Die extrahierten Profile waren komplex und für Nutzer schwer nachvollziehbar. Man konnte zwar sehen, dass ein Profil bestimmte Metriken vorsah, aber die direkte Kontrolle fehlte. Die Stilanalyse war eine Blackbox – das Ergebnis musste akzeptiert werden, wie es war. Die Erkenntnis war, dass Nutzer oft mehr direkte Kontrolle über einzelne Parameter wünschen. Das neue Tool sollte daher einen anderen Weg gehen: Synthese statt Analyse, direkte Parameterkontrolle statt Profilkopie.
Ein weiterer Aspekt war die Komplexität des Vorgängers: ~2.250 Zeilen Code in einer tieferen Modulhierarchie, mit separaten Komponenten für Stilanalyse, Feinabstimmung durch Vorher-Nachher-Vergleiche und Profilvisualisierung. Diese Komplexität war für den Anwendungsfall möglicherweise überdimensioniert.
Spezifikationsphase#
Die Entwicklung folgte einem zweiphasigen Ansatz, der sich als methodisch entscheidend herausstellte. Die erste Phase bestand aus einer ausführlichen Spezifikation im Dialog mit verschiedenen LLMs.
Die Spezifikation umfasste etwa 1.400 Zeilen und 50.000 Zeichen – nahezu so umfangreich wie der resultierende Code selbst. In dieser Phase wurden im Dialog mit dem LLM folgende Aspekte erarbeitet:
- Ziele und Abgrenzung des Projekts
- Funktionale Anforderungen (zweistufiger Prozess, Reglertypen, Presets)
- UI-Konzept (gruppierte Regler, Accordions, Token-Counter)
- Technische Architektur (Modulstruktur, Datenmodelle, API-Integration)
- Konfigurationskonzept (Umgebungsvariablen, JSON-Dateien)
Die Spezifikation war kein statisches Dokument, das vorab erstellt wurde, sondern entwickelte sich im Dialog. Das LLM fungierte dabei als Diskussionspartner, der Konzepte hinterfragte, Alternativen vorschlug und bei der Präzisierung half.
Implementierungsphase#
Nach Abschluss der Spezifikation wurde der Code in einem Durchgang generiert. Die klare, umfassende Spezifikation ermöglichte dies ohne größere Korrekturschleifen. Das Ergebnis umfasste etwa 2.000 Zeilen Python-Code und 500 Zeilen JSON-Konfiguration, verteilt auf 12 Dateien.
Die Architektur wurde bewusst flach gehalten: sechs Python-Module (app.py, config.py, llm_client.py, models.py, prompt_builder.py, token_counter.py) statt einer verschachtelten Struktur. Diese Entscheidung war eine direkte Konsequenz aus Erfahrungen mit dem komplexeren Vorgänger, der etwa 2.250 Zeilen Code in einer tieferen Modulhierarchie hatte.
Iterationen#
Nach der initialen Generierung folgten drei Haupt-Iterationen:
Iteration 1 – Intensitätsanpassung: Erste Tests zeigten, dass die Stilmerkmale nicht deutlich genug umgesetzt wurden. Im Dialog mit dem LLM wurde das Problem analysiert und die Lösung entwickelt: explizite Intensitätsstufen mit klaren Anweisungen an das ausführende LLM (“einen Hauch hinzufügen” bis “maximal übertrieben”).
Iteration 2 – History und Hash-Code: Als sich zeigte, dass das Tool gut funktioniert, entstand der Wunsch, erfolgreiche Einstellungen zu persistieren. In einem Durchgang wurden eine History-Funktion und der Export/Import von Einstellungen als Hash-Code ergänzt.
Die gesamte Entwicklung dauerte etwa zwei Stunden: eine Stunde für die Spezifikation, der Rest für Implementierung, Deployment und Erweiterungen. Das Projekt wurde an einem Tag nebenher durchgeführt.
Methodische Erkenntnisse#
Spezifikation als Schlüsselfaktor#
Die zentrale Erkenntnis dieses Experiments: Die Qualität der Spezifikation bestimmt die Qualität des generierten Codes. Das Verhältnis von Spezifikationsaufwand zu Implementierungsaufwand verschob sich im Vergleich zu früheren Projekten deutlich. Die eigentliche Entwicklungsarbeit fand zunehmend in der Konzeptionsphase statt – die Code-Generierung wurde zum Ausführungsschritt einer bereits durchdachten Lösung.
Ein direkter Vergleich verdeutlicht dies: Hätte der Entwicklungsprozess mit einer vagen Anforderung wie “baue mir ein Tool für Stiltransformation” begonnen, wären vermutlich mehrere Iterationsschleifen nötig gewesen, um Konzeptfragen zu klären, die nun bereits in der Spezifikationsphase beantwortet wurden.
Dialog als Spezifikationsmethode#
Die Spezifikation entstand nicht als isoliertes Dokument, sondern im Dialog mit dem LLM. Diese Methode erwies sich als produktiv: Das LLM half bei der Präzisierung von Anforderungen, schlug Alternativen vor und identifizierte Lücken im Konzept. Die resultierende Spezifikation war umfassender und konsistenter als sie vermutlich in reiner Eigenarbeit gewesen wäre.
Auch die Problemlösung in späteren Iterationen folgte diesem Muster. Bei den Intensitätsstufen etwa kam das Problemverständnis vom Entwickler (die Stile werden nicht deutlich genug umgesetzt), die technische Lösung entstand jedoch in der Zusammenarbeit mit dem LLM.
Lerntransfer zwischen Projekten#
Das Experiment zeigt, wie Erkenntnisse aus früheren Projekten in neue einfließen. Die flache Architektur war eine bewusste Reaktion auf die Erfahrung mit dem komplexeren Vorgänger. Die Konzentration auf Synthese statt Analyse adressierte die Erkenntnis, dass Nutzer mehr direkte Kontrolle wünschen.
Aktives Gegensteuern bei Overengineering#
Trotz des konzeptionell einfacheren Ansatzes (nur Synthese, keine Analyse) erreichte das neue Tool eine ähnliche Codegröße wie der Vorgänger. Dies deutet darauf hin, dass aktives Gegensteuern gegen Overengineering durch das LLM nötig war. LLMs tendieren dazu, Abstraktionen und Strukturen hinzuzufügen, die für den konkreten Anwendungsfall nicht erforderlich sind.
Diese Tendenz zum Overengineering zeigt sich in verschiedenen Formen: zusätzliche Abstraktionsschichten, die keine klare Funktion erfüllen; generische Interfaces, die nur eine konkrete Implementierung haben; komplexe Konfigurationsmechanismen für Funktionen, die wahrscheinlich nie geändert werden. Das Gegensteuern erfordert eine klare Vorstellung davon, was tatsächlich benötigt wird – ein weiterer Grund, warum eine präzise Spezifikation so wichtig ist.
Prompt-Engineering im Tool selbst#
Ein interessanter Aspekt dieses Projekts ist, dass es selbst Prompt-Engineering betreibt: Der Prompt-Builder generiert Prompts für das ausführende LLM basierend auf den Regler-Einstellungen. Die Qualität dieser generierten Prompts bestimmt die Qualität der Texttransformation.
Die Intensitätsstufen sind ein Beispiel für iterativ verbessertes Prompt-Engineering. Die initiale Version verwendete abstrakte Intensitätsangaben, die das ausführende LLM nicht konsistent umsetzte. Die überarbeitete Version enthält konkrete Handlungsanweisungen: “Füge einen Hauch davon hinzu” für niedrige Intensität, “Wende dies MAXIMAL und übertrieben an! Treibe es auf die Spitze!” für höchste Intensität.
Ähnlich präzise sind die Neutralisierungs-Prompts formuliert. Die Dimension “Tonalität” etwa wird übersetzt in: “Entferne alle emotionalen Färbungen wie Ironie, Sarkasmus, Wut, Freude oder Trauer. Der Text soll emotional neutral sein.” Diese Konkretheit reduziert Interpretationsspielraum und führt zu konsistenteren Ergebnissen.
Evolution der Methodik#
Mit zunehmender Erfahrung werden die Spezifikationen länger und präziser. Das Verhältnis von Spezifikation zu Code bei diesem Projekt (1.400 Zeilen / 50.000 Zeichen für 2.000 Zeilen Code) ist höher als bei früheren Experimenten. Diese Entwicklung reflektiert die Erkenntnis, dass Präzision in der Spezifikation Zeit bei der Implementierung spart.
Die Evolution lässt sich auch qualitativ beschreiben: Frühere Spezifikationen beschrieben primär das “Was” (welche Funktionen das Tool haben soll), spätere Spezifikationen beschreiben zunehmend auch das “Wie” (technische Architektur, Datenmodelle, UI-Struktur) und das “Warum nicht” (bewusst ausgeklammerte Features, vermiedene Komplexität). Diese Vollständigkeit reduziert Interpretationsspielraum und verhindert, dass das LLM eigene Annahmen trifft, die später korrigiert werden müssen.
Aktueller Stand und Ausblick#
Das Tool wird derzeit von mehreren Personen mit unterschiedlichen Perspektiven evaluiert. Diese Evaluation ist bewusst breit angelegt: Technisch versierte Nutzer testen die Grenzen der Regler-Kombinationen, während weniger technische Nutzer die Usability und Verständlichkeit bewerten. Erste Erkenntnisse zeigen, dass das Tool auch für feinere Stilanpassungen gut funktioniert – nicht nur für drastische Transformationen wie “mache diesen Text zum Märchen”.
Die zweistufige Architektur (Neutralisierung → Stilisierung) scheint dazu beizutragen, dass die gewünschten Stile klarer erreicht werden. Die Hypothese dahinter: Wenn der Ausgangstext bereits starke stilistische Prägungen hat, kann das ausführende LLM in Konflikt geraten zwischen dem Originalstil und dem gewünschten Zielstil. Die vorgeschaltete Neutralisierung reduziert diesen Konflikt. Ob diese Hypothese empirisch haltbar ist, wird die Evaluation zeigen.
Ein konkretes Beispiel: Ein emotional aufgeladener Beschwerdetext soll in einen sachlichen Bericht transformiert werden. Ohne Neutralisierung “schimmert” die ursprüngliche Emotionalität oft durch. Mit vorgeschalteter Neutralisierung (Dimensionen: Tonalität, Subjektivität, Intensifizierer) wird der Text zunächst auf seinen Informationsgehalt reduziert, bevor die Stilisierung zum sachlichen Bericht erfolgt. Die Ergebnisse sind konsistenter.
Potenzielle Weiterentwicklung#
Für einen hypothetischen Produktiveinsatz wäre eine Vereinfachung der UI nötig. 34 Regler bieten zwar feine Kontrolle, überfordern aber durchschnittliche Nutzer. Mehrere Ansätze sind denkbar:
Vereinfachte Modi: Ein “Einfach”-Modus könnte nur die wichtigsten 5-8 Regler anzeigen und die Details hinter einem “Erweitert”-Schalter verbergen.
Intelligente Presets: Statt statischer Presets könnten kontextabhängige Vorschläge basierend auf dem Eingabetext generiert werden (“Dieser Text wirkt sehr informell – möchten Sie ihn formeller gestalten?”).
Zielstil-Beschreibung: Nutzer könnten den gewünschten Stil in natürlicher Sprache beschreiben (“professionell, aber nicht steif”), und das System würde die passenden Regler-Einstellungen ableiten.
Diese Erweiterungen wären allerdings selbst wieder Gegenstand von Experimenten – ob sie tatsächlich zu besserer Usability führen, müsste evaluiert werden.
Fazit#
Das Experiment bestätigt und erweitert Erkenntnisse aus früheren Projekten. Die methodische Haupterkenntnis – Spezifikation als Schlüsselfaktor – ist nicht überraschend, aber ihre quantitative Ausprägung (Spezifikation fast so umfangreich wie Code) verdient Beachtung. Sie deutet darauf hin, dass sich die Rolle des Entwicklers bei LLM-gestütztem Coding verschiebt: von der Implementierung zur Konzeption, von der Programmierung zur präzisen Anforderungsdefinition.
Die Trennung in Spezifikationsphase und Implementierungsphase hat sich als produktiv erwiesen. Sie ermöglicht es, konzeptionelle Fragen zu klären, bevor Code generiert wird, und reduziert so aufwändige Korrekturschleifen. Der Dialog mit dem LLM ist dabei nicht nur für die Implementierung nützlich, sondern bereits für die Erarbeitung der Spezifikation selbst.
Metriken#
| Kategorie | Wert |
|---|---|
| Code-Umfang | ~2.000 Zeilen Python |
| Konfiguration | ~500 Zeilen JSON |
| Dateien | 12 |
| Spezifikation | ~1.400 Zeilen, ~50.000 Zeichen |
| Gesamtzeit | ~2 Stunden |
| Zeitverteilung | ~1h Spezifikation, Rest Implementierung/Deployment/Erweiterungen |
| Sessions | 1 Tag |
| Haupt-Iterationen | 3 |
| Verhältnis Spec:Code | ~1:1,5 (bezogen auf Zeichen) |
| Regler | 34 in 7 Kategorien |
| Presets | 23 |
| Neutralisierungsdimensionen | 10 (7 sichere, 3 experimentelle) |
Technische Übersicht#
Stack: Python, Gradio 6, OpenAI-kompatible API (vLLM), tiktoken
Module:
- app.py – Hauptanwendung und UI
- config.py – Konfigurationsmanagement
- llm_client.py – API-Kommunikation mit Retry-Logik
- models.py – Datenmodelle (Regler, Einstellungen, Ergebnisse)
- prompt_builder.py – Generierung der LLM-Prompts
- token_counter.py – Token-Zählung
Deployment: Docker mit docker-compose, konfigurierbar über Umgebungsvariablen