---
title: "Snipeflip: ein Angebotsjäger, bei dem die KI am Ende weniger tut als am ersten Tag"
date: 2026-08-06
author: "Damjan Savić"
canonical: https://damjan-savic.com/de/wissen/ai-deal-finder
language: de
keywords: "KI-Agenten, Claude Code, Automatisierung, Web-Scraping, Telegram"
video: https://www.youtube.com/watch?v=dqrlof_o0H0
---
# Snipeflip: ein Angebotsjäger, bei dem die KI am Ende weniger tut als am ersten Tag

**Kurz gesagt** — Ein Suchagent, der alle fünfzehn Minuten Inserate holt, sie durch vier Filterstufen schickt und mit einer Bewertung von 0 bis 100 auf Telegram meldet. Die KI sitzt in der Mitte und macht weniger als am ersten Tag — davor stehen deterministische Regeln, die kein Modell brauchen.

---

Ich sage dem Ding genau, wonach ich suche. Es durchsucht Kleinanzeigen, liest jedes Inserat, das zurückkommt, und gibt jedem eine Bewertung von 0 bis 100. Ich sehe mir die über 70 an.

Eines davon: 45 Euro für ein Gerät, das normalerweise für 80 bis 100 verkauft wird. Ein grafikfähiger Taschenrechner, TI-Nspire CX II-T CAS. Neu liegt er bei etwa 170 Euro, gebraucht bei rund 105.

Das ist ein langweiliger Gegenstand. Und er ist einer der besten überhaupt, um ihn zu jagen — aus einem Grund, der nichts mit Taschenrechnern zu tun hat: **Niemand, der so etwas kauft, hat es sich ausgesucht.** Schulen und Hochschulen schreiben das exakte Modell vor. Wenn dein Kurs diese Maschine verlangt, ist ein günstigeres Gerät, das dieselbe Mathematik kann, keine Option.

Also gibt es jedes Jahr Leute, die genau eine bestimmte Sache zu genau einem bestimmten Zeitpunkt brauchen — und Leute, die gerade fertig geworden sind und genau diese Sache in einer Schublade liegen haben.

## Was ich zeige

Das Problem an Gebrauchtmärkten ist nicht, dass es keine guten Angebote gäbe. Es ist, dass gute Angebote ungefähr zwanzig Minuten halten. Man hat drei Möglichkeiten: von Hand nachladen, was den Tag kostet; hinnehmen, dass man das meiste verpasst; oder etwas schreiben, das für einen hinsieht.

Ich habe das Dritte geschrieben, und dazu gehört Klartext: Das ist **eine** Plattform, mit niedriger Frequenz, für **einen** Nutzer, nämlich mich.

Was ich zeige, ist die Architektur — wie die Zeitsteuerung funktioniert, wie die Filterung funktioniert, wie die Bewertung funktioniert und wie das Ergebnis auf mein Telefon kommt. Was ich nicht zeige, ist, wie man irgendetwas umgeht. Der Teil bleibt aus dem Video und aus dem schriftlichen Leitfaden heraus.

Wenn du so etwas baust: **lies zuerst die Nutzungsbedingungen von dem, worauf du es richtest.** Sie sind überall verschieden, und manche sind erheblich strenger als diese hier.

## Phase 1 · Server

PostgreSQL, Node, ein Prozessmanager, nginx. Claude Code richtet ein, ich sehe zu — in diesem Fall im Auto-Modus, damit nicht bei jedem Schritt eine Rückfrage kommt.

Die Bewertung ruft `api.deepseek.com` auf statt eines Claude-Schlüssels. Das ist eine Kostenentscheidung, kein Qualitätsurteil, und sie hat später Folgen, siehe Fehler drei.

Ein Blocker kam sofort: Die gewählte Domain [zeigte noch auf Vercel](/de/wissen/vercel-to-root-server), und certbot kann kein Zertifikat ausstellen, solange die A-Records nicht auf den Server zeigen. Also zuerst DNS umstellen, `www` als CNAME auf die Hauptdomain, propagieren lassen, dann das Zertifikat.

### Der Prozessaufbau

Das ist der Teil, auf den ich hinweisen will. **Drei getrennte Prozesse:**

| Prozess | Port | Speicherlimit |
| --- | --- | --- |
| WebSocket-Server | 3002 | 200 MB |
| Web-Anwendung | 3003 | 500 MB |
| Scraper-Worker | — | 1 GB |

Sie sind nicht ein Prozess, und **die Startreihenfolge zählt**: Der WebSocket muss oben sein, bevor der Scraper versucht, sich zu verbinden. In meiner eigenen Konfigurationsdatei steht genau dieser Satz in Großbuchstaben, weil ich es oft genug falsch hatte, um es aufzuschreiben.

Der Scraper bekommt am meisten Speicher, weil er geparstes HTML im Speicher hält, während er eine Charge abarbeitet.

## Phase 2 · Die Jagd

Das ist der Teil, den die meisten falsch machen, und er hat nichts mit KI zu tun. Ein Zielprodukt ist kein Suchbegriff, sondern vier Dinge:

1. **Der Name.** `TI-Nspire CX II-T CAS`. Nicht „Taschenrechner", nicht „Grafikrechner". Das exakte Modell, weil das exakte Modell vorgeschrieben ist.
2. **Pflichtbegriffe.** Wörter, die vorkommen müssen, sonst ist es nicht die Sache.
3. **Ausschlussbegriffe.** Der wichtige Punkt.
4. **Radius.** Wie weit ich zu fahren bereit bin.

Zu den Ausschlussbegriffen: Wer auf einem Gebrauchtmarkt nach einem Taschenrechner sucht, bekommt überwiegend Hüllen, Taschen, Ersatzteile und als defekt verkaufte Geräte zurück. Das sind keine knappen Treffer, das ist eine andere Produktkategorie, die zufällig denselben Namen teilt. Also stand `Tasche`, `Hülle`, `defekt` auf der Ausschlussliste.

### Der Tasche-Fehler

Und damit war ich bei dem Fehler, der mich am meisten Zeit gekostet hat.

Das deutsche Wort für das Gerät ist **Taschenrechner**. Auf der Ausschlussliste stand **Tasche**. Ein Teilstring-Vergleich gegen die eigene Ausschlussliste hat damit **jedes einzelne Inserat für genau das gesuchte Produkt** stillschweigend weggeworfen. Der Filter, der Zubehör entfernen sollte, hat die Sache selbst entfernt.

Die Behebung ist ein Vergleich an Wortgrenzen statt an Teilstrings. Über der Funktion steht in meinem Repository ein Kommentar mit genau diesen beiden Wörtern, weil ich das nie wieder debuggen will.

Die verallgemeinerte Form davon: **Deutsch bildet Komposita so, wie andere Sprachen Sätze bilden.** Wer deutschen Text nach Teilstrings filtert, löscht früher oder später sein eigenes Produkt.

## Phase 3 · Sammeln

Alle fünfzehn Minuten, höchstens fünf Suchlinks parallel.

Das Erste im Auftrag ist ein **Mutex** — ein Boolean und ein Zeitstempel. Läuft der vorherige Durchlauf noch, protokolliert dieser, wie lange der alte schon läuft, und beendet sich. Er reiht sich nicht ein, er läuft nicht daneben, er überspringt.

Das klingt selbstverständlich. Aufgeschrieben war es nicht selbstverständlich, als um drei Uhr morgens zwei überlappende Aufträge dieselben Inserate geschrieben haben und die Logs überhaupt keinen Sinn mehr ergaben.

Danach die **Deduplizierung**, und die ist absichtlich eine Sammeloperation. Jedes Inserat hat eine externe ID. Bevor irgendetwas anderes passiert, holt der Auftrag in **einer** Abfrage alle IDs, die er schon kennt, aktualisiert bei den bekannten den Zeitstempel des letzten Sehens und behält nur das, was wirklich neu ist.

Der Grund ist langweilig und wichtig: 50 Ergebnisse je Suchlink, mal fünf Links, mal 96 Durchläufe am Tag. Wer die einzeln prüft, hat einen Denial of Service gegen die eigene Datenbank gebaut.

Nur das Neue geht weiter an den Detail-Scraper: **drei gleichzeitig, 500 Millisekunden zwischen den Chargen, 30 Sekunden Zeitlimit.** Diese drei Zahlen sind die gesamte Ethik dieser Sache. Es ist ein Budget, kein Wettrennen.

## Phase 4 · Filterkette

Hier hatte ich erwartet, dass das Modell die Arbeit macht. Es macht sie nicht.

**Stufe 1 ist deterministisch.** Kein Modell, kein API-Aufruf, keine Kosten. Ausschlussbegriffe an Wortgrenzen, Zubehörmuster (denn „Hülle für X" ist nicht X), Pflichtbegriffe strikt, und eine Sprachprüfung. Das meiste, was hereinkommt, stirbt hier.

In dieser Datei steht ein Kommentar, den ich für mich selbst geschrieben habe: Diese Funktion ist der Hauptfilter und muss streng sein, weil **danach keine KI-Prüfung mehr kommt**.

Dieses „mehr" ist die Geschichte. Ich habe mit einem Sprachmodell angefangen, das jedes einzelne Inserat bewertet hat. Dann bin ich wegen des Volumens auf ein günstigeres Modell gewechselt. Dann habe ich Bildanalyse ergänzt, damit der Zustand aus den Fotos beurteilt werden kann. Und die habe ich zweimal wieder ausgeschaltet.

**Stufe 2 ist die Stelle, an der sich das Modell seinen Platz verdient.** Alles, was Stufe 1 überlebt, bekommt eine strukturierte Bewertung: Zustand, Vollständigkeit — bei einem Taschenrechner heißt das Kabel, Hülle, Anleitung —, ob der Preis zu dem passt, was beschrieben ist, und Verkäufersignale aus der Detailseite. Heraus kommt eine Zahl von 0 bis 100 **mit einer Begründung**.

**Stufe 3 ist der Preisbezug.** Zweimal am Tag, um 8 und um 18 Uhr, rechnet ein getrennter Auftrag den Durchschnittspreis je Produkt aus dem neu, was er tatsächlich gesehen hat. „Günstig" heißt damit günstig verglichen mit diesem Markt in diesem Monat, nicht verglichen mit einer Zahl, die ich einmal eingetippt habe.

**Stufe 4 ist die Schwelle.** Ab 50 gibt es eine Benachrichtigung, alles darunter landet im Feed und wartet. Ab 80 stehe ich tatsächlich auf.

![Diagramm · Die vier Stufen der Filterkette und was jede davon kostet](/media/website/article/figures/ai-deal-finder-filters.avif)

## Phase 5 · Meldung

Zwei Kanäle: Telegram, weil ich es tatsächlich lese, und Web Push für den Browser.

Die Meldung trägt die Bewertung, den Preis, die Entfernung **und die Begründung, die das Modell gegeben hat** — nicht nur den Link. Wenn ich die App öffnen muss, um zu entscheiden, ob ich die App öffne, hat die ganze Sache versagt.

Die Übersicht aktualisiert sich über WebSockets: Ein neues Inserat erscheint, die Bewertung wird angehängt, die Kennzeichnung ändert sich. Kein Nachladen.

Und jedes Inserat behält einen Preisverlauf. Wenn derselbe Artikel drei Tage später günstiger neu eingestellt wird, ist das sichtbar — und das ist meistens der Moment, den Verkäufer anzuschreiben.

## Wofür es taugt

Gebaut habe ich es, um Taschenrechner weiterzuverkaufen. Dann wollte ein Freund einen Mac. Ich habe ihm zu einem MacBook Pro mit M1 geraten statt zu etwas Neuerem, wir haben es eingetragen — dieselben vier Felder, keine Codeänderung — und er hat am Ende eines mit Originalverpackung und vollständigem Zubehör gefunden.

Das ist der Punkt, an dem ich aufgehört habe, es ein Werkzeug zum Weiterverkaufen zu nennen. Ein anderes Produkt, nicht ein anderes Problem. Und die zweite Verwendung ist die, die ich in der Öffentlichkeit vertrete.

## Fünf Fehler

**Eins: Ich habe Inserate bewertet, bevor ich die Inserate hatte.** Eine Zeit lang lief die Bewertung, bevor die Detailseite geholt war. Das Modell hat also einen Titel und ein Vorschaubild bewertet und mir anschließend selbstbewusst etwas über den Zustand eines Gegenstands erzählt, dessen Beschreibung es nie gesehen hatte. Die Behebung ist **ein** Commit: erst die Details holen, dann bewerten. Die Bewertungen wurden dadurch spürbar besser, und an dieser Verbesserung war kein Modell beteiligt.

**Zwei: Die Seite hat sich unter mir verändert.** In diesem Repository gibt es eine Funktion, deren einzige Aufgabe es ist, die Bilder aus einem Inserat zu holen. Sie hat vier verschiedene Wege dafür: zuerst strukturierte Daten in den Galerie-Elementen, dann CSS-Hintergrundbilder, dann das allgemeine Produktschema der Seite, und wenn alle drei leer bleiben, direkt die Bild-Tags samt ihrer Lazy-Loading-Attribute. Ich habe das nicht entworfen. Es hat sich angesammelt. Jede Schicht ist ein Morgen, an dem etwas, das gestern funktioniert hat, ein leeres Array zurückgegeben hat.

**Drei: Bildanalyse zweimal abgeschaltet.** Ich hatte sie gebaut, damit die Bewertung Fotos ansehen und den Zustand beurteilen kann — bei einem gebrauchten Rechner geht es vor allem um Display und Tastatur, das schien es wert. Dann habe ich aus Kostengründen den Anbieter gewechselt, und der neue nimmt keine Bildeingabe. Es gibt zwei Commits im Abstand von Wochen, die beide dasselbe abschalten. Der zweite existiert, weil ich den ersten vergessen hatte.

**Vier: Portkollisionen.** Fünf Commits mit demselben Fehler darin. Robusterer PM2-Neustart. Portaufräumen im Deploy-Skript. Wiederholungslogik im WebSocket-Server. Sauberes Herunterfahren. Sequenzieller Dienststart. Das ist nicht ein Fehler — das ist ein Fehler, den ich viermal schlecht behoben habe, bevor ich ihn richtig behoben habe. Die tatsächliche Behebung war, zu akzeptieren, dass drei Prozesse eine **definierte Startreihenfolge und ein Kill-Timeout** brauchen. Keine clevere Wiederholungslogik.

**Fünf: Das Build-Werkzeug — und eine `.env` im Repository.** Turbopack, vier Produktions-Builds mit Standalone-Ausgabe, dann abgeschaltet. Und eine eingecheckte `.env`-Datei. Dazu ist nicht viel zu sagen, außer dass es passiert ist.

## Drei Regeln

**Eins: erst deterministisch, dann das Modell.** Jeden Filter, den du als Regel schreiben kannst, schreib als Regel. Das Modell bekommt, was übrig bleibt. Das ist billiger, schneller und prüfbar — und wenn es schiefgeht, kannst du die Zeile lesen, die es getan hat. Ein regulärer Ausdruck hat noch nie eine Begründung halluziniert. Er frisst bereitwillig deine ganze Produktkategorie, aber er tut es auf eine Art, die du finden kannst.

**Zwei: Nimm an, dass die Seite sich ändert.** Wenn deine Eingabe von der Website eines anderen kommt, ist sie keine Schnittstelle und wird dich nicht warnen. Schreib die Extraktion von Anfang an als Kette von Rückfallebenen — und mach ein leeres Ergebnis laut. Still zurückgegebene leere Arrays sind das schlimmste Fehlerbild überhaupt, weil alles weiterläuft und die Qualität einfach leise sinkt. Vier Rückfallebenen für ein Bild sind keine Übertechnisierung, so sieht ein Jahr Betrieb aus.

**Drei: Jeder automatisierte Abruf braucht ein Budget.** Nebenläufigkeitsgrenzen, Pausen zwischen den Chargen, ein Abstand zwischen den Läufen und eine Sperre, damit sie sich nicht überlappen. Nicht weil dich jemand dazu zwingt, sondern weil ein System, das ruhig bleibt, weiterläuft — und eines, das rennt, blockiert wird. Und dann hast du gar nichts.

Diese drei Regeln sind der Grund, warum die KI in diesem KI-Projekt am Ende weniger tut als am ersten Tag. Das ist kein Versagen des Modells. Das ist, was die Aufgabe tatsächlich gebraucht hat.

## Fazit

Ich bin angetreten, ein KI-Werkzeug zu bauen, und habe am Ende ein Filtersystem gebaut, in dessen Mitte ein Sprachmodell genau eine Aufgabe erledigt, in der es wirklich gut ist — hinter Regeln, die es nicht brauchen.

Und die andere Hälfte: Ein Taschenrechner, den jemand zwei Jahre gebraucht und seither nicht angefasst hat, ist ein völlig brauchbarer Taschenrechner. Irgendwer fängt im September diesen Kurs an. Ein fünf Jahre alter Mac ist immer noch ein schneller Rechner. Nichts davon muss noch einmal hergestellt werden, es muss benutzt werden.

Das ist ein Suchproblem. Und Suchprobleme sind genau die Sorte, die man auf einen Server legen und danach vergessen kann.

## Kapitel der Aufnahme

- [0:48 — Warum ein langweiliger Gegenstand ein gutes Ziel ist](https://youtu.be/dqrlof_o0H0?t=48)
- [1:36 — Was ich zeige und was nicht](https://youtu.be/dqrlof_o0H0?t=96)
- [2:28 — Phase 1: Server und Prozessaufbau](https://youtu.be/dqrlof_o0H0?t=148)
- [5:55 — Phase 2: die vier Felder, und der Tasche-Fehler](https://youtu.be/dqrlof_o0H0?t=355)
- [7:29 — Phase 3: Mutex, Deduplizierung, Budget](https://youtu.be/dqrlof_o0H0?t=449)
- [8:19 — Phase 4: die Filterkette](https://youtu.be/dqrlof_o0H0?t=499)
- [10:44 — Phase 5: Telegram, Web Push, Preisverlauf](https://youtu.be/dqrlof_o0H0?t=644)
- [12:23 — Die fünf Fehler](https://youtu.be/dqrlof_o0H0?t=743)
- [14:47 — Die drei Regeln](https://youtu.be/dqrlof_o0H0?t=887)

## Häufige Fragen

### Was war der Tasche-Fehler?

Auf der Ausschlussliste stand Tasche, um Hüllen und Taschen auszusortieren. Das gesuchte Produkt heißt Taschenrechner. Ein Teilstring-Vergleich hat damit jedes einzelne Inserat für genau das gesuchte Gerät stillschweigend weggeworfen. Die Behebung ist ein Vergleich an Wortgrenzen. Verallgemeinert: Deutsch bildet Komposita so, wie andere Sprachen Sätze bilden — wer deutschen Text nach Teilstrings filtert, löscht früher oder später sein eigenes Produkt.

### Warum kommt das Modell erst in Stufe 2?

Weil Stufe 1 deterministisch ist und das meiste dort stirbt: Ausschlussbegriffe an Wortgrenzen, Zubehörmuster, Pflichtbegriffe und eine Sprachprüfung — kein Modellaufruf, keine Kosten. Erst was das überlebt, bekommt eine strukturierte Bewertung mit Begründung. Ein regulärer Ausdruck hat noch nie eine Begründung halluziniert; er frisst bereitwillig deine ganze Produktkategorie, aber auf eine Art, die du finden kannst.

### Was heißt „Budget" bei einem automatisierten Abruf?

Drei gleichzeitige Detailabrufe, 500 Millisekunden zwischen den Chargen, 30 Sekunden Zeitlimit, fünfzehn Minuten Abstand zwischen den Läufen und ein Mutex, damit sie sich nicht überlappen. Nicht weil dich jemand dazu zwingt, sondern weil ein System, das ruhig bleibt, weiterläuft — und eines, das rennt, blockiert wird.

---

Snipeflip: ein Angebotsjäger, bei dem die KI am Ende weniger tut als am ersten Tag — https://damjan-savic.com/de/wissen/ai-deal-finder
KI-generierter Inhalt: https://damjan-savic.com/de/ki-transparenz
© 2026 Damjan Savić. https://damjan-savic.com
