3 Punkte von GN⁺ 2024-08-30 | 1 Kommentare | Auf WhatsApp teilen
  • Ein Entwickler wollte die Implementierung einer ASCII-Art-Komponente verstehen, die er im Web gesehen hatte, und gab minifiziertes JavaScript in ChatGPT ein; dabei wurde der obfuskierte Ablauf in eine lesbare Struktur aufgelöst
  • Das Original war eine React-Komponente, die Spalten und Zeilen passend zur Fenstergröße berechnete und mit setInterval ein String-Grid mit 60 fps aktualisierte
  • ChatGPT erklärte die Auswahl des Zeichensatzes, die koordinaten- und zeitbasierte Zeichenberechnung sowie den Ablauf zur Aktualisierung von div.textContent und schrieb den Code anschließend als gleichwertigen TypeScript/React-Code neu
  • Zunächst schien die Implementierung anders zu sein, weil Blockzeichen wie ░▒▓█ fehlten; eine erneute Prüfung des Originalskripts zeigte jedoch, dass es sich um ein Copy-and-paste-Encoding-Problem handelte
  • Nach der Aktualisierung mit dem richtigen Zeichensatz entsprach die Ausgabe der Originalkomponente und zeigte, dass ChatGPT praktisch genutzt werden kann, um minifizierten Code zu verstehen und neu zu implementieren

Eine ASCII-Art-Implementierung in minifiziertem JavaScript finden

  • Um herauszufinden, wie der ASCII-Art-Block auf der Seite des reactive.network hackathon implementiert war, wurde der Quellcode untersucht
  • Der relevante Code war minifiziert und daher für Menschen nicht direkt gut lesbar; normalerweise hätte man ihn manuell entschlüsseln oder eine .map-Datei suchen müssen, um das Original wiederherzustellen
  • Diesmal wurde der gesamte minifizierte Code in ChatGPT eingefügt, mit der Bitte, die Funktionsweise des Codes zu erklären

Die aus dem minifizierten Code erkennbare Struktur

  • Der Code war so aufgebaut, dass er mit JavaScript und React dynamische Textinhalte erzeugte
  • Die wichtigsten Elemente waren:
    • Mathematische Funktionen wie Math.floor, Math.abs und Math.min wurden mit kurzen Aliasen verwendet
    • Es gab einen Zeichensatz aus "reactive.network REACTIVE.NETWORK" sowie Sonderzeichen und Blockzeichen
    • Je nach Ergebnis von Date.now() % 3 wurde der zu verwendende Zeichensatz ausgewählt
    • Auf Basis der Länge des ausgewählten Zeichensatzes wurde für jede Koordinate der Index des anzuzeigenden Zeichens berechnet
  • Die Kernfunktion eT nahm Positionskoordinaten und einen Zeitwert entgegen und gab ein einzelnes anzuzeigendes Zeichen zurück
    • Der Zeitwert wurde mit t.time * 8e-5 umgerechnet
    • Die Koordinaten wurden mithilfe des kleineren Werts aus Spalten und Zeilen sowie des Seitenverhältnisses korrigiert
    • Der Zeichenindex wurde durch Kombination des Ergebnisses von Math.cos(position.x * position.x - position.y * position.y) mit dem Zeitwert bestimmt

Rendering-Ansatz der React-Komponente

  • Die Komponente referenzierte mit useRef das div, in das der Text eingefügt werden sollte, und speicherte mit useState die height und width des Fensters
  • Der erste useEffect setzte die Anfangsgröße über window.innerHeight und window.innerWidth und aktualisierte die Größe, wenn ein resize-Event auftrat
  • Der zweite useEffect berechnete anhand der Bildschirmgröße die Größe des ASCII-Grids
    • cols ist Math.floor(width / 12) * 1.6
    • rows ist Math.floor(height / 12)
    • aspect ist cols / rows
  • setInterval wurde im Abstand von 1_000 / 60 ausgeführt und erzeugte den String passend zu 60 fps neu
  • In jedem Frame wurden alle Zeilen und Spalten durchlaufen, die berechneten Zeichen aneinandergehängt und der finale String in element.textContent geschrieben

Neufassung als gut lesbarer TypeScript-Code

  • Der Entwickler bat anschließend darum, gleichwertigen Code in TypeScript zu implementieren, ihn aber für Menschen lesbar zu machen
  • Der von ChatGPT erzeugte Code löste die Rollen des minifizierten Codes in folgende Namen auf:
    • selectedCharacterSet
    • characterSetLength
    • calculateCharacter
    • Typ Size
    • React-Komponente AsciiArtGenerator
  • Schwer verständliche Variablennamen wurden durch Namen ersetzt, die die Implementierungsabsicht sichtbar machen
    • calculateCharacter berechnet anhand von Koordinaten, Spalten und Zeilen, Seitenverhältnis und Zeitwert das anzuzeigende Zeichen
    • textRef verweist auf das div, in dem der eigentliche Text steht
    • size und setSize repräsentieren den Zustand der Fenstergröße
  • Der neu geschriebene Code war deutlich besser lesbar als der ursprüngliche minifizierte Code und reichte aus, um die Implementierungsweise der Komponente zu lernen

Missverständnis um fehlende Zeichen und Encoding-Problem

  • Beim ersten Vergleich sah es so aus, als hätte das LLM einige Implementierungsdetails übersehen
  • Insbesondere wurde angenommen, dass im Ergebnis von ChatGPT Zeichen wie ░▒▓█, die in der Originalkomponente zu sehen waren, fehlten
  • Später wies ein Nutzer im HN-Forum darauf hin, dass es sich um ein Copy-and-paste-Problem handeln könnte; eine zusätzliche Prüfung ergab, dass der Originalcode andere Zeichen enthielt als die, die in ChatGPT eingefügt worden waren
  • Nach dem direkten Herunterladen des Skripts waren die richtigen Zeichen verfügbar, und als Ursache wurde ein Encoding-Unterschied bestätigt
  • Nachdem der Zeichensatz korrigiert worden war, entsprach die Ausgabe der Originalkomponente

1 Kommentare

 
GN⁺ 2024-08-30
Meinungen auf Hacker News
  • Ich bin der Entwickler von HumanifyJS und habe für diesen Zweck ein eigenes LLM-basiertes Tool gebaut.
    Es nutzt LLMs auf AST-Ebene, um sicherzustellen, dass der Code auch nach der Deobfuskation weiter funktioniert: https://github.com/jehna/humanify

    • Ich frage mich, ob es schwierig wäre, eine Funktion zum Neubenennen von Grund auf hinzuzufügen.
      Gemeint ist eine Funktion, die aus normalem Code alle aussagekräftigen Namen des Nutzers entfernt und dann anhand des Algorithmus und der verbleibenden Namen, zum Beispiel eingebauter Namen, neue Namen auswählt.
      Beim Refactoring mache ich das manchmal manuell mit einem LLM; es ist nützlich, um standardnähere Begriffe wie antiparallel_line statt parallel_line_opposite_direction zu finden oder stärker verallgemeinerbare Namen wie find_instance_in_list statt find_animal_instance_in_animals zu entdecken.
    • Mehr Tools sollten auf AST-Basis gebaut werden, großartige Arbeit.
      Ehrlich gesagt warte ich immer noch auf Versionsverwaltung auf AST-Ebene.
    • Ich frage mich, welche Fragen ihr dem LLM stellt.
      Gebt ihr ihm die ganze Funktion und fragt wiederholt „Was sollte umbenannt werden?“, bis alles geändert ist, oder lasst ihr es alles auf einmal machen, parst dann die Ausgabe und prüft, ob der AST weiterhin übereinstimmt?
    • Ich frage mich, ob es auch bei riesigen Dateien funktioniert.
      Ich meine etwa 50.000 Zeilen.
      Edit: Ich lasse gerade erst einmal eine JS-Datei mit nur 1,2k Zeilen im OpenAI-Modus laufen, und nach 20 Minuten ist sie erst zu 70 % fertig. Selbst wenn es theoretisch mit einer Datei von 50.000 Zeilen funktionieren würde, sollte man es wohl besser nicht versuchen.
  • JS-Minifizierung ist ziemlich mechanisch und einfach, daher sollte auch das Rückgängigmachen vergleichsweise leicht sein.
    Natürlich ist es im Allgemeinen immer noch mühsam genug, um es nicht von Hand zu machen, aber die Transformation selbst ist ziemlich begrenzt, sodass man sie mit Notizen zum Nachverfolgen der obfuskierten Bezeichner lesen kann.
    Allgemeineres Dekomprimieren oder Deobfuskieren scheint weiterhin ein offenes Problem zu sein. Ich habe früher einige absichtlich obfuskierte Programme geschrieben, und meiner Erfahrung nach hat ChatGPT sie nicht einmal oberflächlich verstanden.
    Zum Beispiel gab es einen Kommentar, der mit GPT-4 versuchte, den Code eines 160-Byte-Brainfuck-Interpreters in C aus einem Gist zu erklären [1], aber die „verdeutlichte Version“ hatte keinerlei Ähnlichkeit mit dem Originalcode.
    [1] https://gist.github.com/lifthrasiir/596667#gistcomment-47512...

    • Nur weil eine Aufgabe einfach ist, muss die Umkehraufgabe nicht ebenfalls einfach sein.
      Beispiele sind Multiplikation/Faktorisierung, Ableiten/Integrieren oder Erinnerung an die Vergangenheit/Vorhersage der Zukunft.
      Code-Deobfuskation ist eindeutig eines dieser schwierigen inversen Probleme und kann durch Bugs, ungenutzte oder irrelevante Routinen sowie fehlerhafte Implementierungen, die zufällig richtige Ergebnisse liefern, leicht noch schwieriger werden.
      In diesem Sinne ist es erfreulich, wenn ChatGPT bei der Deobfuskation brauchbare Ergebnisse erzielt; a priori würde ich das aber nicht erwarten. Es ist gut, dass ihr an einem schwierigeren Problem die Grenzen von ChatGPT ausgelotet habt, aber ich sehe das, was ihr gefunden habt, nur als Obergrenze. Das Beispiel aus dem Original halte ich nicht für trivial.
    • let userSignedInTimestamp = new Date() in let x = new Date() zu ändern, ist trivial; in die Gegenrichtung muss man aber wahrscheinlich den umgebenden Code lesen und verstehen, um zu sehen, in welchem Kontext x verwendet wird.
      Außerdem ist auch der restliche Code minifiziert, was es noch schwieriger macht, und selbst wenn man alles richtig macht, bleibt es letztlich eine verlustbehaftete Transformation; der Variablenname könnte Eigenschaften enthalten haben, die im Code nicht ausdrücklich stehen.
    • Ein Farbfoto in Schwarz-Weiß umzuwandeln ist ziemlich einfach, es wieder in die ursprünglichen Farben zurückzuverwandeln aber nicht.
      Das liegt daran, dass dabei Daten verloren gehen.
      Minifizierung funktioniert auf die gleiche Weise. Viele Informationen, die zum Verstehen des Codes nötig sind, gehen verloren, und diese Informationen wiederzugewinnen kann sehr knifflig sein.
    • Einen beschreibenden Variablennamen wie timeFactor in ein viel kürzeres i zu ändern, kann mechanisch und einfach sein, aber der Prozess ist destruktiv und lässt sich vergleichsweise schwer rückgängig machen.
      Ohne ein ziemlich ausgefeiltes Verständnis davon, was der Code tatsächlich tut, ist das nicht möglich. Genau diesen Teil hat das LLM hier geleistet, und das ist zwar nicht überraschend, aber beeindruckend. Diese allzu reflexhafte Herabwürdigung gefällt mir nicht.
    • Ich habe mit Claude 3.5 Sonnet einen ersten zufälligen Versuch gemacht: https://claude.site/artifacts/246c1b1a-3088-447a-a526-b1e716...
      Da ich nicht am PC bin, konnte ich es nicht testen.
  • Sollte im Titel nicht ChatGPT oder der Modellname GPT-4 stehen, statt OpenAI, dem Firmennamen?

    • Wenn man bedenkt, dass OpenAI den einst allgemeinen technischen Begriff GPT zu einer Marke gemacht hat, hat es eine gewisse ironische Fairness, OpenAI wie einen Produktnamen zu verwenden.
  • LLMs sind hervorragend bei Texttransformationen
    Das ist ihre zentrale Stärke, die meiner Ansicht nach nicht ausreichend genutzt wird.

    • Es ist nicht nur eine zentrale Stärke, sondern genau das, wofür Transformer entwickelt wurden; kontrovers gesagt könnte man sogar sagen, dass sie nichts anderes können.
      Andere Fähigkeiten wie Schlussfolgern oder das Bewahren von Wissen sind nur Nebenwirkungen dieser übermenschlichen Fähigkeit, Text zu reproduzieren, ohne die beabsichtigte Bedeutung zu „verstehen“.
    • Besonders stark sind sie bei nahezu linearen Transformationen, bei denen sich die Reihenfolge kaum ändert und Beziehungen zwischen Elementen nicht tief berücksichtigt werden müssen.
      Zum Beispiel können sie keine Listen sortieren, aber Sprachen übersetzen. Denn eine fast korrekt sortierte Liste ist falsch, während wir eine fast korrekte Übersetzung oft akzeptieren.
    • Mit den passenden Tools sollte es möglich sein, eine Codebase fast per Knopfdruck in andere Sprachen und Frameworks zu übertragen.
      Wenn ein Team überlegt, ob es sich lohnt, große Teile von Python-Code nach Elixir zu übertragen, muss es nicht länger nur raten.
      Vor Kurzem habe ich ein Python-Skript nach JavaScript übersetzen lassen; es war einwandfrei, und mit ein paar Guardrails ließe sich das vermutlich skalieren.
    • Ist das nicht schon der wichtigste Business-Anwendungsfall?
      Wir nutzen es hauptsächlich, um strukturierte Daten aus anderen Formaten zu extrahieren.
    • Das Problem muss eines sein, bei dem man sich entweder nicht um das Risiko von Halluzinationen kümmern muss oder bei dem man die Ausgabe auch dann validieren kann, wenn die Daten noch nicht in einem nützlichen Format vorliegen.
      Außerdem darf einem das Wissen oder die Fähigkeit fehlen, es mit awk, Python, Perl usw. schneller zu erledigen.
  • Einige Entwickler, die Komprimierung als Mittel zur Obfuskation genutzt haben, werden sicher verärgert sein.
    Das erinnert mich an ein Code-„Verschlüsselungs“-Tool in alten ColdFusion-Versionen. Der Algorithmus war sehr schwach, daher dauerte es nicht lange, bis jemand ein Entschlüsselungs-Tool baute.
    Trotzdem mochten manche Leute das nicht, weil sie glaubten, das Tool sei sicher, und es nutzten, um Code zu verkaufen, ohne den Source offenzulegen. Das war Ende der 90er/Anfang der 2000er, bevor Open Source zum überwältigenden Standard wurde.

    • Ich denke, bald wird es für solche Einsatzfälle ein Wettrüsten geben.
      Websites veranstalten Wettbewerbe, die teilweise auf obfuskiertem clientseitigem Code beruhen, und clevere Teilnehmer können ihn jetzt in ChatGPT werfen, um ihre Gewinnchancen zu erhöhen.
      Das ist erst der Anfang.
  • Das ist ein Beispiel für dem Menschen überlegene intellektuelle Leistungsfähigkeit.
    Das lässt sich nicht bestreiten. Diese Aufgabe ist intellektuell und hat nichts mit bloßem Auswendiglernen zu tun. Im Web gibt es auch nicht Unmengen an Paaren aus komprimiertem und dekomprimiertem Code, von denen ein LLM lernen könnte.
    Das LLM versteht, was es dekomprimiert, und ist in diesem speziellen Thema im Allgemeinen Menschen überlegen.

    • Das ist einfach nur Texttransformation.
      Kann man sicher sein, dass es im Web nicht viele Paare aus komprimiertem und unkomprimiertem Code gibt? Sie lassen sich leicht aus bestehendem JS erzeugen und als Trainingsset verwenden, und es gibt bereits Unmengen an unkomprimiertem JS, das im Training verwendet wurde.
    • Ich bin optimistisch in Bezug auf AI, aber ich bin nicht sicher, ob das ein Beispiel für das genannte Phänomen ist.
      Der Grund, warum Menschen komprimierten Code schwer verstehen, sind undurchsichtige Variablennamen, ungewohnte Schleifen, minimierter Whitespace usw. Computer finden solche Dinge nicht schwierig, und genau deshalb komprimieren wir ja überhaupt. Die Attention-Architektur sollte bei solchen Aufgaben gut sein.
      Ich denke auch, dass es enorm viel komprimierten/unkomprimierten Code gibt. Genau dafür sind Source-Map-Dateien da. Wenn man sieht, wie viel OpenAI in Web-Browsing und Softwareentwicklung investiert hat, wäre es nicht überraschend, wenn komprimierte/unkomprimierte Daten im Training enthalten waren.
    • Ich habe keine Expertise in AI oder im Verständnis von Intelligenz, aber könnte man nicht auch sagen, dass ein Taschenrechner Arithmetik wirklich versteht und eine dem Menschen überlegene intellektuelle Leistungsfähigkeit besitzt?
    • Warum sollte man die Tatsache, dass Menschen Tools zur Dekomprimierung gebaut haben, nicht in den Score der Menschen einrechnen?
      Dass ein Computer eine 1000-stellige Zahl sofort multipliziert, ist ein Beispiel dafür, dass Menschen zuerst ein solches Tool gebaut und damit erfolgreich multipliziert haben. Was hat daran sonst noch intellektuell Erfolg? Der Computer hat sich schließlich nicht selbst gebaut.
      Wenn man die Grenze menschlicher Intelligenz an der Schädeldecke zieht und die Tools, die diese Intelligenz erschafft und nutzt, nicht als Schritte im Problemlösungsprozess mitzählt, muss man auch akzeptieren, dass Menschen nicht intelligent genug sind, um ins All zu fliegen, Operationen durchzuführen oder die meisten Mahlzeiten zuzubereiten.
    • GPT-4 hat mehr Code konsumiert, als alle deine Vorfahren in ihrem Leben je sehen werden, und versteht die inhärenten Muster zwischen Code und seiner komprimierten Version.
      Die abstrakte Form von Code ohne Variablennamen zu erkennen und menschenlesbare Variablennamen auf ähnliche Muster abzubilden, die es im riesigen Internet gesehen hat, wirkt nicht absurd.
  • Interessant, und es scheint deutlich besser geworden zu sein.
    Vor etwas mehr als einem Jahr bekam ich eine E-Mail mit der Bitte um Hilfe bei einem Custom-Plugin einer gehackten WordPress-Site und wollte GPT für die Deobfuskierung von Malware-Code ausprobieren. Damals kam ich viel schneller voran, indem ich den Code selbst Schritt für Schritt nachvollzog.
    Nach der Lektüre dieses Beitrags habe ich es erneut versucht [0]. Es lieferte verständliche Ergebnisse, aber der Code war so obfuskiert, dass er über das Window-Objekt effektiv nicht lesbare Strings evaluiert; das allein reichte also nicht aus.
    Ein Teil des Berichts, den ich damals schickte, lautete sinngemäß: Ich konnte mich durch stark obfuskierten JavaScript-Code arbeiten, erkennen, wann der Page Load detektiert wird, eine nur scheinbar konstante Berechnung aufrufen und die Logik entschlüsseln, die die Seite auf eine bösartige Website weiterleitet.
    [0] https://chatgpt.com/share/f51fbd50-8df0-49e9-86ef-fc972bca6b...

  • Arbeitet jemand an einem Decompiler-LLM?
    Es scheint, als könnte man damit sämtlichen Code wie Open Source machen.
    In diesem Fall wären Trainingsdaten leicht zu erstellen. Man müsste kostenlosen GitHub-Code massenhaft mit verschiedenen Compilern bauen und das Modell darauf trainieren, die Kompilierung rückgängig zu machen. Das ist ein Fall, in dem synthetische Trainingsdaten passend und ziemlich einfach zu erzeugen wären.
    Den Decompiler müsste man nur darauf trainieren, die Kompilierung rückgängig zu machen; für Aufgaben wie das Hinzufügen von Kommentaren könnte man bestehende, größere Code-LLMs verwenden.

    • Die potenziellen Auswirkungen sind enorm.
      Nicht nur Open-Sourcing: Man kann sich vorstellen, proprietäre Apps leicht zu dekompilieren und zu verändern, um Bugs zu beheben oder Funktionen hinzuzufügen. Besonders für Programme, die schon lange nicht mehr gepflegt werden, könnte das eine große Befreiung sein.
      Aus rechtlichen Gründen dürfte ein solches Verhalten in großen Modellen wahrscheinlich eine gesperrte Funktion werden.
    • Den Sourcecode zu haben ist nur der erste Schritt; man braucht auch das Recht, diesen Code zu lesen, zu ändern, auszuführen und die geänderte Version weiterzuverbreiten.
      Solche Rechte kann nur der Autor des Codes gewähren.
    • Zustimmung. Ich halte „KI generiert/versteht Sourcecode“ für ein großes Ablenkungsmanöver.
      Wenn KI Code wirklich gut versteht, wird sie einfach Binaries erstellen oder reparieren.
      Ich glaube, sobald es echte KI-Programmierer gibt, werden sie sich nicht um menschenlesbaren Code kümmern, sondern alles in Maschinensprache programmieren. Wenn sie die Aufgabe bekommen, ein bestehendes System zu warten, werden sie den gesamten Software- und Hardware-Stack als Ganzes verstehen.
      Ähnlich wie ein Diffusionsmodell zur Bilderzeugung sich nicht darum kümmert, echte Zeichentechniken zu lernen.
    • Es gibt ein LLM, das von x86 nach C dekompiliert: https://github.com/albertan017/LLM4Decompile
    • Entminifizieren ist kein Dekompilieren.
      Es geht eher darum, Variablen und Funktionen umzubenennen und Zeilenumbrüche einzufügen.
  • Das ist der Art, wie ich LLMs häufig nutze, sehr ähnlich [0].
    Wenn ich mich mit eigener Kraft durch Code lesen muss, nutze ich sie als ersten Schritt zur Entzifferung.
    Es ist unglaublich befreiend, spaghettiartigen, völlig chaotischen Code hineinzukopieren, ihn lesbarer aufbereiten zu lassen und von dort aus weiterzumachen.
    Wie der Autor ebenfalls festgestellt hat, übersehen LLMs gelegentlich Details, aber diese Stellen kann ich selbst abfangen.
    Ein weiterer Anwendungsfall ist, wenn ich verstehe, was der Code tut, mir aber nicht recht in den Kopf will, warum er so aufgebaut ist. Das gilt besonders, wenn die Person, die den Code geschrieben hat, nicht mehr im Unternehmen ist. Dann füge ich die Methode in einen LLM-Chat ein, erkläre, was sie tut, und frage anschließend, warum bestimmte Dinge auf eine bestimmte Weise gelöst sein könnten.
    Es ist nicht immer perfekt, aber ziemlich oft liefert es Erklärungen, die tatsächlich Sinn ergeben, einer Überprüfung standhalten und neue Einsichten liefern. Ein- oder zweimal hat es mich auch davon abgehalten, etwas so zu refaktorieren, dass es später Probleme gemacht hätte.
    [0] Ich nutze ChatGPT und seit Kurzem mehrere andere Modelle, hauptsächlich Claude-Varianten, über openwebUI als Frontend, um die Unterschiede zu sehen. Man kann auch interessante Setups bauen, bei denen verschiedene Modelle die Antworten der jeweils anderen prüfen.

  • Wenn der entminifizierte Code nicht mit dem minifizierten Code übereinstimmt, dann sinkt der Nutzen stark, wie in der Passage am Ende: „Die LLM-Antwort scheint einige Implementierungsdetails übersehen zu haben“.
    Vor allem, weil man in vielen Fällen den Code nicht einfach wie im Artikel ausführen und die Unterschiede überprüfen kann.
    Nachtrag: Wenn ich den Thread unten lese, scheint es wohl ein Encoding-Problem gewesen zu sein. Trotzdem macht mir die Korrektheit weiterhin etwas Sorgen.

    • Für tatsächliches Umbenennen sollte man ein anderes Tool wie HumanifyJS verwenden: https://github.com/jehna/humanify
    • Der entminifizierte Code sieht dem Original ziemlich ähnlich.
      In einigen Fällen hat ChatGPT zusätzlich zum Entminifizieren sogar eigenes Refactoring vorgenommen.
      Der Originalcode ruft handleResize nicht sofort auf, sondern enthält dessen Inhalt inline. Vermutlich hat der Minifier tatsächlich Inlining gemacht. Der einzige echte Unterschied hier ist, dass die Bedingung if (typeof window < "u") fehlt.
    • Diese Stelle bedeutet, dass in der von ChatGPT erzeugten Version einige Zeichen aus dem Originalbeispiel fehlen.
      Konkret geht es um Blockzeichen; HN scheint das Einfügen von Unicode-Zeichen nicht zu erlauben. In der Originalversion sind sie sichtbar, in der von ChatGPT erzeugten Version nicht.
      Es ist allerdings gut möglich, dass ich nicht den gesamten nötigen Kontext mitgegeben habe.
      Die gesamte Ausgabe wegen ein paar fehlender Zeichen abzuwerten, wirkt eher wie übergenaues Haarspalten. Abgesehen davon ist die Ausgabe aus meiner Sicht identisch.
    • Außerdem wurde das Modell auch angewiesen, den Code von JavaScript in TypeScript neu zu implementieren.
      Wenn man es nur gebeten hätte, Variablen- und Methodennamen zu ändern, wäre es dem Original vermutlich näher gekommen.