Für Nicht-Codex-Modelle komprimiert die Open-Source-Codex-CLI den Kontext lokal: Ein LLM fasst die Konversation mithilfe eines Komprimierungs-Prompts zusammen. Wenn der komprimierte Kontext später verwendet wird, erhält responses.create() ihn mit einem Übergabe-Prompt, der die Zusammenfassung einrahmt. Beide Prompts sind im Quellcode einsehbar.
Für Codex-Modelle ruft die CLI stattdessen die compact()-API auf, die einen verschlüsselten Blob zurückgibt. Wir wissen nicht, ob sie intern ein LLM verwendet, welche Prompts sie nutzt oder ob es überhaupt einen Übergabe-Prompt gibt.
Im Folgenden zeige ich, wie eine einfache Prompt-Injection (2 API-Aufrufe, 35 Zeilen Python) enthüllt, dass der API-Komprimierungspfad tatsächlich ein LLM zur Zusammenfassung des Kontexts verwendet, mit einem eigenen Komprimierungs-Prompt und einem der Zusammenfassung vorangestellten Übergabe-Prompt. Die Prompts sind nahezu identisch mit den Open-Source-Versionen.
Schritt 1 — compact()
Ich rufe compact() mit einer präparierten Benutzernachricht auf. Auf der Serverseite verarbeitet ein Compactor-LLM unsere Eingabe mit seinem eigenen versteckten System-Prompt (den ich noch nie gesehen habe und herausfinden möchte).
Der Server scheint den Kontext des Compactors wie folgt zusammenzustellen:

Das Compactor-LLM liest seinen System-Prompt zusammen mit unserer Eingabe. Da unsere Eingabe eine Injection-Payload (roter Text oben) enthält, wird der Compactor dazu gebracht, seinen eigenen System-Prompt in seine Ausgabe aufzunehmen. Diese Klartext-Zusammenfassung existiert nur auf OpenAIs Servern. Wir sehen nur den verschlüsselten Blob:

An diesem Punkt haben wir keine Möglichkeit, den Inhalt des Blobs zu lesen. Er ist AES-verschlüsselt und der Schlüssel befindet sich auf OpenAIs Servern. Wir hoffen nur, dass der Compactor der Injection gehorcht und seinen Prompt in die Zusammenfassung geschrieben hat. Der einzige Weg, dies herauszufinden, ist Schritt 2.
Schritt 2 — create()
Ich übergebe den verschlüsselten Blob + eine zweite Benutzernachricht an responses.create(). Der Server entschlüsselt den Blob und stellt den Kontext des Modells zusammen.
Ich sende:

Das Modell scheint etwa Folgendes zu sehen:

Wenn Schritt 1 funktioniert hat, sollte der entschlüsselte Blob den Komprimierungs-Prompt enthalten (der durch unsere Injection preisgegeben wurde). Der Server stellt dem Blob außerdem einen Übergabe-Prompt voran. Wenn unsere Sonde das Modell erfolgreich dazu bringt, das Gesehene zu wiederholen, sollte die Ausgabe alle drei offenlegen: den System-Prompt, den Übergabe-Prompt und den Komprimierungs-Prompt.
Ausgabe
Unten ist die vollständige, unveränderte Ausgabe eines Durchlaufs von extract_prompts.py. Gelb = System-Prompt, grün = Übergabe-Prompt, pink = Komprimierungs-Prompt.

Woher wissen wir, dass dies die echten Prompts sind und nicht nur halluzinierter Text? Der extrahierte Komprimierungs-Prompt und der Übergabe-Prompt stimmen weitgehend mit den bekannten Prompts überein, die für Nicht-Codex-Modelle in der Open-Source-Codex-CLI verwendet werden (prompt.md, summary_prefix.md), was es unwahrscheinlich macht, dass das Modell sie von Grund auf erfunden hat. Die Ergebnisse variieren zwischen den Durchläufen.
Die Vermutete Pipeline
Alles zusammengenommen, hier ist unsere beste Vermutung, was compact() auf der Serverseite tut, basierend auf dem, was die Extraktion enthüllt hat.

Das Skript

Offene Frage
Warum verwendet die Codex-CLI zwei völlig unterschiedliche Komprimierungspfade (lokales LLM für Nicht-Codex-Modelle, verschlüsselte API für Codex-Modelle), wenn die zugrundeliegenden Prompts nahezu identisch sind? Und warum die Zusammenfassung überhaupt verschlüsseln?
Schwer zu sagen. Vielleicht enthält der verschlüsselte Blob mehr, als dieses einfache Experiment zeigen kann, z. B. etwas Spezifisches darüber, wie Tool-Ergebnisse komprimiert und wiederhergestellt werden. Aber ich habe mich nicht weiter damit befasst, es zu testen.





