PullMD Teil 3: Die API, und wie aus Reddit jede URL wurde – Einfach erklärt
Am Ende von Teil 2 lief das Werkzeug in seinem Container, also in einem versiegelten Päckchen aus Programm und allem, was es zum Laufen braucht. Es lief.
Nur stand nirgends aufgeschrieben, was man diesem Dienst eigentlich sagen kann, was zurückkommt und warum es so aussieht, wie es aussieht.
Darum geht es hier. Und darum, dass sich das Ganze zwei Tage nach dem ersten Umzug auf den Server schon wieder selbst überholt hat.
Eine einzige Klingel, mehr nicht
Ein Dienst wie dieser hat keinen Bildschirm und keine Knöpfe. Er hat eine API: die Schnittstelle, über die zwei Programme miteinander reden. Man schickt eine Frage an eine bestimmte Adresse, und es kommt eine Antwort zurück.
Die Aufgabenbeschreibung dafür war schon ziemlich genau. Sie steht so in der Session vom 2. März: eine einzige Adresse zum Anfragen, dazu ein paar Stellschrauben.
Welche Seite soll geholt werden, sollen die Kommentare mit, wie tief, wie viele, und in welchem Format hätte man es gern?
Zurück kommt Markdown. Das ist ein einfaches Textformat: Text mit Struktur, aber ohne Schnickschnack.
Dazu gab es noch einen zweiten, winzigen Eingang, der nur weiterleitet. Wer einen geteilten Link anklickt, landet auf der Startseite, mit der gewünschten Adresse schon im Gepäck.
Mehr war es nicht. Eine Klingel für die eigentliche Arbeit, eine zum Weiterleiten, dazu die Dateien der Oberfläche. Zwei Kleinigkeiten daran finde ich im Rückblick trotzdem wichtiger, als sie damals aussahen.
Kleinigkeit eins: die Zahl im Fehlerfall
Wenn etwas schiefgeht, antwortet der Dienst mit einer Nummer. Solche Nummern kennt man aus dem Netz, die berühmteste ist die 404: „gibt es nicht“.
Eine davon ist hier besonders. Die 502 heißt ausdrücklich „Reddit hat dichtgemacht“, nicht „bei mir ist was kaputt“.
Wer den Dienst automatisiert aufruft, kann also schon an der Zahl ablesen, ob ein zweiter Versuch überhaupt Sinn hat. Bei einer 400, wenn die Adresse fehlt oder Unsinn ist, hilft Wiederholen ja nicht.
Nebenbei: Diese Fehlermeldungen kommen als JSON zurück. Das ist ein Format, in dem Programme Daten austauschen, ordentlich sortiert und für Menschen gerade noch lesbar.
Kleinigkeit zwei: die Attrappe für Tests
Die zweite Kleinigkeit steht in der Liste gar nicht drin. Dem Server lässt sich beim Start eine Attrappe unterschieben. Statt wirklich bei Reddit anzuklopfen, gibt sie einfach vorbereitete Beispieldaten zurück.
Das klingt nach nichts. Es ist aber der Unterschied zwischen Tests, die in Millisekunden durchlaufen, und Tests, die man irgendwann abschaltet, weil sie nerven.
Und dann ist da noch der reine Textmodus, die Sorte Funktion, die man einbaut, weil man sie selbst braucht. Manchmal will ich einfach nur den Fließtext, ohne Sternchen und Rauten.
Dafür putzt der Dienst das Markdown wieder raus, das er gerade selbst erzeugt hat. Ein bisschen albern, im Alltag aber genau richtig.
Warum nicht der offizielle Weg zu Reddit
Eine Sache hatte ich schon im Konzept-Chat entschieden, und zwar bevor eine Zeile Code existierte.
„Also API fällt aus, die bekommt man nur noch auf Anfrage. Ja, wenn das mit JSON funktioniert, prima. Ansonsten einen normalen Browser imitieren?“
Die offizielle Schnittstelle von Reddit hätte eine Registrierung gebraucht, Schlüssel, ein Kontingent. Für ein Werkzeug, das ich mir selbst auf den Server stelle, ist das ziemlich viel Aufwand für ziemlich wenig.
Der andere Weg kostet nichts und braucht kein Konto: Reddit gibt fast jede Seite auch in Maschinenform heraus, wenn man die Adresse nur leicht abwandelt.
Und falls das blockiert wird, tut der Dienst halt so, als wäre er ein ganz normaler Browser, und liest die alte Reddit-Oberfläche. Das ist unelegant, aber es hält.
Im selben Absatz steht übrigens noch ein Halbsatz, den ich danach fast zwei Monate lang vergessen habe: „MCP später.“
MCP (Model Context Protocol) ist so etwas wie eine Steckdose, über die ein KI-Assistent fremde Werkzeuge direkt mitbenutzen kann. Dazu kommen wir in Teil 7.
Zwei Tage später: jede beliebige Adresse
Für Reddit lief das Ding jetzt ganz gut. Nur landet man im Alltag ja nicht nur auf Reddit.
Manchmal will man einfach irgendeine Seite mit dem Chat teilen: einen Blogartikel, eine Anleitung, was einem so über den Weg läuft.
Ein großer Plan steckte da nicht dahinter, es lag einfach nahe. Wenn das Ding schon eine Seite in sauberen Text verwandelt, warum dann nur eine einzige Sorte von Seiten?
Am 4. März stand der nächste Durchgang an, und der hat aus dem Reddit-Werkzeug etwas anderes gemacht. Sieben Aufgaben, gut eine halbe Stunde.
Vier fertige Bausteine kamen dazu, alle von anderen Leuten geschrieben und frei verfügbar. Dann ein Zwischenspeicher, ein neuer Weg für beliebige Webseiten, und eine Oberfläche, die nun jede Adresse annimmt und sich merkt, was schon durchgelaufen ist.
Der Server entscheidet seitdem selbst, ob eine Adresse zu Reddit gehört oder nicht.
Damit gibt es drei Wege durch den Dienst, und daran hat sich bis heute nichts geändert:
-
Erst wird nachgesehen, ob die Seite den sauberen Text von sich aus herausgibt. Manche können das, wenn ihr Betreiber die Funktion eingeschaltet hat. Dann ist die eigene Aufbereitung ohnehin überflüssig.
-
Ist es Reddit, geht es über den Maschinen-Weg von oben.
-
Alles andere landet bei Readability und Turndown.
Der Zwischenspeicher ist so ein Punkt, von dem ich damals nicht gedacht hätte, dass ich ihn später noch so oft brauche. Ein Cache ist erstmal nur ein Gedächtnis für Antworten, damit man dieselbe Seite nicht zweimal holen muss.
Hier landet jede Abfrage in einer SQLite-Datei, also in einer winzigen Datenbank, die einfach als eine einzige Datei auf der Platte liegt.
Damit gibt es zum ersten Mal eine Historie. Was habe ich eigentlich schon alles durch das Ding geschickt?
Aus dieser Tabelle wird später der Teilen-Link, und noch später die Statistik, die dafür sorgen soll, dass mir ein Fehler nicht noch einmal so lange entgeht.
Der Umzug auf den Server, mit zwei Stolpersteinen
Der Umzug auf den Docker-Host lief am selben Vormittag. 168 Kilobyte gingen per rsync hinüber, das ist ein Kopierwerkzeug, das nur überträgt, was sich tatsächlich geändert hat. Danach musste das Päckchen neu gebaut werden.
Einer der neuen Bausteine, ausgerechnet der für die kleine Datenbank, muss beim Bauen erst für die jeweilige Maschine übersetzt werden.
Dafür braucht es kurz eine ganze Werkzeugkiste in der Box. Also Werkzeug rein, übersetzen, Werkzeug wieder raus, damit das Päckchen klein bleibt.
Der erste Start ging trotzdem schief. Die Meldung war SQLITE_CANTOPEN, sinngemäß: „Ich komme an meine Datei nicht ran.“
Das Datenverzeichnis gehörte einem anderen Benutzer als dem, unter dem der Container läuft. Ein Handgriff an den Zugriffsrechten später lief es.
Der zweite Stolperstein war kurioser. Wikipedia kam als sauberes Markdown zurück, Hacker News auch. Ausgerechnet example.com nicht, dort stand nur Failed to extract page: fetch failed, also „konnte die Seite nicht holen“.
Schuld war ein fehlendes Stammzertifikat. Zertifikate sind so etwas wie die Ausweise, mit denen sich Webseiten als echt ausweisen, und der Unterbau des Programms bringt eine Liste der anerkannten Aussteller mit. In Node 20 fehlte genau der eine.
Ausgerechnet die Seite, die man zum Testen nimmt, weil sie immer geht.
Was oben im Text stand
Über jede Ausgabe schreibt der Dienst ein paar Zeilen mit Titel und Herkunft. Nach dem Umbau sah das so aus: der Titel der Seite, darunter fett die Domain, und dann erst der eigentliche Text.
Also nur „en.wikipedia.org“, nicht die vollständige Adresse der Seite. Bei Reddit stand sie schon drin, bei normalen Seiten eben nicht.
Das ist eine dieser Kleinigkeiten, die einem erst auffallen, wenn man das Werkzeug ein paar Wochen benutzt hat. Was daraus wurde, steht in Teil 5.
Teil 3 von 15 der PullMD Serie.
← PullMD Teil 2: Vom Spec zum laufenden Container | PullMD Teil 4: Der Skill, und was er wirklich bringt →