Eine tägliche GitHub $\rightarrow$ lokale LLM Pipeline, die meine Projektkarten schreibt
Der Abschnitt „Projects“ auf dieser Seite wird nicht manuell gepflegt. Ein Job prüft einmal täglich meine GitHub-Repositories, stellt fest, was sich geändert hat, lässt ein lokales Sprachmodell eine kurze Beschreibung jedes geänderten Projekts erstellen und speichert das Ergebnis. Ich veröffentliche diejenigen, die es wert sind, gezeigt zu werden; der Rest bleibt als Entwürfe gespeichert.
So ist es verkabelt – und was noch wichtiger ist, die drei Regeln, die verhindern, dass es etwas Dummes tut.
Der Ablauf
cron (03:30)
→ GET /api/internal/repos (was ich bereits weiß: repo → letzter SHA)
→ GET api.github.com/user/repos (feingranularer, LESE-SCHUTZ-Token)
→ diff by default-branch SHA (nur geänderte Repos gehen weiter)
→ fetch README + languages
→ POST {OLLAMA}/api/chat (gemma, format: json, think: false)
→ POST /api/internal/projects (Merge in SQLite)
Das LLM ist lokal – eine Ollama-Instanz, die Gemma im LAN ausführt. Kein Code, README oder Repo-Metadaten verlassen jemals das Netzwerk. Das Modell wird in einem strengen System-Prompt angewiesen, sich wie ein „präziser technischer Texter“ zu verhalten und nur ein JSON-Objekt zurückzugeben:
{ "title": "…", "blurb": "…", "tags": ["…"], "highlight": true }
Ollamas format: "json" garantiert eine parsierbare Ausgabe, und think: false deaktiviert den Denkprozess des Modells – schneller (~5–6 s/repo) und sauberer.
Regel 1 — nur das anfassen, was sich geändert hat
Jedes von mir gesehene Repo ist in einer Tabelle repo_state mit seinem letzten SHA des Standardzweigs erfasst. Jeder Durchlauf vergleicht den Live-SHA mit dem gespeicherten; unveränderte Repos werden bevor das Modell überhaupt aufgerufen wird, übersprungen. Das LLM ist der teure Schritt, daher läuft es nur für Repos, die sich tatsächlich bewegt haben. Ein zweiter Durchlauf Minuten später tut nichts und ruft das Modell null Mal auf.
Regel 2 — niemals meine Bearbeitungen überschreiben
Die Ausgabe des Modells sind automatische Daten. In SQLite hat jedes Projekt zwei parallele Sätze von Feldern: die automatischen Werte (von GitHub/LLM) und die Überschreibungswerte (was ich im Admin eingegeben habe). Die öffentliche Seite zeigt den Überschreibungswert an, falls dieser existiert, andernfalls den automatischen Wert.
Der Merge-Endpunkt erzwingt dies. Wenn ein Repo sich ändert und die Pipeline neue Daten postet, aktualisiert es die automatischen Spalten und lässt *_override, published und sort_order unberührt. So kann ich einen Blurb von Hand umschreiben, und der Durchlauf von morgen wird die GitHub-Metadaten darunter aktualisieren, ohne meine Worte jemals zu berühren. Das LLM ist eine Vorschlagsmaschine, nicht die Quelle der Wahrheit.
Regel 3 — privat bleibt privat
Alle meine Quell-Repos sind privat, und ein privates README kann Client- oder interne Kontexte enthalten, die ich niemals auf einer öffentlichen Seite veröffentlichen möchte. Daher ist der Import standardmäßig vertraulich: ein neu entdecktes Repo wird als ungeöffentlichter Entwurf (published = 0) eingefügt. Nichts, was die Pipeline erstellt, ist auf der Seite sichtbar, bevor ich den Schalter im Admin explizit umlege. Der Token selbst ist lese-schreibgeschützt (Contents + Metadata, keine Schreibberechtigungen) – er kann nachsehen, aber nicht pushen.
Wenn das Modell ausgefallen ist
Da das LLM in diesem Offline-Batch-Pfad und nicht im Request-Pfad sitzt, ist ein Ausfall für Besucher kein Ereignis – die Seite bedient weiterhin aus SQLite. Die Pipeline behandelt dies auch elegant: Ist Ollama nicht erreichbar, wird dieses Repo übersprungen (sein SHA wird nicht erhöht, daher wird es beim nächsten Durchlauf erneut versucht) und ich erhalte eine einzige Telegram-Benachrichtigung mit einer Liste der Übersprungen. Keine Abstürze, keine halbfertigen Daten, keine stillen Lücken.
Warum sich Mühe geben
Ich veröffentliche ständig kleine Tools. Ein Portfolio manuell aktuell zu halten ist genau die Art von lästiger Arbeit, die leise verrottet. Das Verlagern des langweiligen Teils – „was habe ich gebaut und was ist eine einzeilige Beschreibung“ – auf eine Maschine, die jede Nacht läuft, während alle redaktionellen und Datenschutzentscheidungen fest bei mir bleiben.
Die interessante Automatisierung sitzt niemals zwischen Ihnen und dieser Seite. Das ist das ganze Design.
Transparenz: Die meisten Beiträge werden mit KI-Unterstützung verfasst, und die nicht-englischen Versionen werden von einem lokalen LLM maschinell übersetzt und anschließend geprüft. Fehler entdeckt? Bitte gib mir Bescheid.