CommunitySchreiben & Editierengithub.com

lukethecat/aperture

Eine Fähigkeit für KI-Agenten, die bessere tägliche Berichte kuratieren — Startseiten scannen, jede Entscheidung aufzeichnen und aus Feedback lernen.

Was ist aperture?

Aperture ist eine Fähigkeit für KI-Agenten, die deterministische und überprüfbare Nachrichtenkuratierung ermöglicht, indem sie tägliche Berichte erstellt, die auf dem Scannen von Frontseiten, Profil-basiertem Vorscreening, LLM-gestützter Überprüfung und einer deduplizierten Veröffentlichung basieren. Sie verwendet ein append-only Log (Tape), das jede Entscheidung – Quellen-Snapshots, Item-Bewertungen, Profilversionen und Benutzerfeedback – aufzeichnet, sodass Agenten jederzeit erklären können, warum ein bestimmter Artikel ausgewählt oder abgelehnt wurde. Die Fähigkeit unterstützt verschiedene Extraktionsprofile (RSS, generische Links, Regex, JSON-APIs) und ist plattformunabhängig, kompatibel mit Claude Code, Cursor, Codex und anderen Agenten. Ihr vierstufiger Pipeline-Prozess (Collect → Edit → Review → Publish) kombiniert deterministische Regeln mit LLM-Bewertungen, um einen weiten Trichter zu erhalten, und lernt kontinuierlich aus Benutzerfeedback durch eine versionierte Profilanpassung, die Kategorien, Schlüsselwörter und negative Gewichtungen aktualisiert. Dies gibt Entwicklern einen auditierbaren, reproduzierbaren Workflow, der zuverlässiger ist als statische RSS-Reader oder einmalige LLM-Zusammenfassungen.

Funktioniert mit~Claude Code✓Codex CLI~Cursor
npx skills add lukethecat/aperture

Installed? Explore more Schreiben & Editieren skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Skill: Aperture

TL;DR — Eine Fähigkeit für KI-Agenten, die bessere tägliche Berichte erstellen und mehr darüber erfahren möchten, was ihre Nutzer wirklich wollen. Aperture scannt Quellen-Startseiten, vergleicht sie mit dem Vortag, filtert sie mit einem gewichteten Profil vor, prüft sie mit einem LLM, entfernt Duplikate, veröffentlicht und entwickelt das Profil auf Basis von Nutzer-Feedback weiter. Alle Entscheidungen werden in einem nur-anhängbaren Log (Tape) festgehalten.

Diese Fähigkeit ist implementierungsunabhängig. Ein Agent kann sie ausführen, indem er diese Datei liest, die mitgelieferte Python-Referenzimplementierung aufruft oder die Muster in seine eigene Laufzeitumgebung portiert.


1. Zweck

Agenten eine deterministische, prüfbare Nachrichten-Kurationsfähigkeit zu geben, die statische RSS-Reader und einmalige LLM-Zusammenfasser ersetzt:

  1. Die Startseite scannen — das „Lesen der Startseite“ als erstklassige Operation behandeln, nicht nur als Feed-Polling.
  2. Jede Entscheidung aufzeichnen — jede Quellen-Schnappschuss, jedes Element, jeden Ablehnungsgrund, jede Profilversion und jeden Bericht in einem nur-anhängbaren Log für Prüfung und Wiederholung speichern.
  3. Aus Nutzer-Feedback lernen — den Geschmack des Agenten durch explizites Feedback aktualisieren und jede Änderung versioniert und rückgängig machbar halten.

Wenn ein Agent diese Fähigkeit ausführt, kann er die Fragen „warum wurde dies ausgewählt?“ und „was wäre gestern anders gewesen?“ aus dem Tape beantworten.


2. Kernkonzepte

KonzeptBedeutung
Ressort (Vertical)Ein konfiguriertes Nachrichtengebiet (z.B. Technik, Auto, KI-Politik). Jedes Ressort hat seine eigenen Quellen, sein Profil und sein Tape.
TapeNur-anhängbares JSONL-Log. Eine Datei pro Ressort. Jeder Quellen-Schnappschuss, jedes Element, jede Profilversion, jedes Feedback und jeder Bericht ist ein Datensatz.
ProfilDer „Geschmack“ des Ressorts: gewichtete Schlüsselwörter, Kategorien mit Boni und negative Begriffe. Versioniert; jede Änderung wird protokolliert.
ScanDie Listenseite jeder Quelle abrufen, Titel/Links extrahieren, URLs normalisieren, mit dem vorherigen Schnappschuss vergleichen (diff).
ReflexionsschleifeNutzer-Feedback → analysierte Profiloperationen → Versionserhöhung → Evolutionsdatensatz → erneute Überprüfung kürzlich gepoolter Elemente.

3. Vierstufige Pipeline

┌─────────┐    ┌──────────┐    ┌─────────┐    ┌──────────┐
│ sammeln │ -> │ bearbeiten│ -> │ prüfen  │ -> │ veröffent-│
│ (scan)  │    │(Vorfilter│    │  (LLM)  │    │ lichen   │
│         │    │  regeln) │    │         │    │ (Dedup + │
│         │    │          │    │         │    │  Bericht)│
└─────────┘    └──────────┘    └─────────┘    └──────────┘
     │               │               │               │
     └───────────────┴───────────────┴───────────────┘
                         │
                    NUR-ANHÄNGBARES TAPE

3.1 Sammeln — Die Startseite scannen

Ziel: Die heutigen Kandidaten-Elemente aus jeder konfigurierten Quelle produzieren.

Schritte:

  1. Für jede Quelle list_url abrufen.
  2. {title, url}-Paare mit dem extract_profile der Quelle extrahieren:
    • rss — <item>-Blöcke analysieren.
    • generic_links — <a href>-Tags analysieren.
    • regex — Einen regulären Ausdruck mit benannten Gruppen für Titel/URL/Datum anwenden.
    • json_api — Einen JSON-Pfad navigieren und Titel/URL-Schlüssel lesen.
  3. Jede URL normalisieren:
    • Fragmente und Tracking-Parameter entfernen (utm_*, fbclid, etc.).
    • Nachgestellten Schrägstrich und führendes www. entfernen.
    • https-Schema erzwingen, Host in Kleinbuchstaben.
  4. Gegen den vorherigen Startseiten-Schnappschuss für diese Quelle differenzieren (aus Tape gelesen).
    • Neue url_norm-Werte werden zu Kandidaten.
    • Den vollständigen aktuellen Schnappschuss als neuen frontpage-Tape-Datensatz speichern.
  5. Quellengesundheit aktualisieren:
    • Erfolg → fail_count zurücksetzen.
    • Fehlschlag → fail_count erhöhen; bei ≥ 3 aufeinanderfolgenden Fehlschlägen warnen.

Warum gegen die Startseite differenzieren, anstatt Veröffentlichungsdaten zu analysieren? Weil viele Quellen unzuverlässige Zeitstempel oder Anti-Scraping-Layouts haben. „Neu auf der Startseite“ ist ein robuster Indikator für „Nachrichten im heutigen Zeitfenster“.

Zeitfenster-Wächter (deterministisch, nicht LLM): Für Pull-Quellen, die tatsächlich ein pubDate (RSS/Atom) bereitstellen, wendet der Scanner vor dem Diff einen harten Cutoff an. Das Standardfenster beträgt 36 Stunden; mit extract_profile.window_hours überschreibbar. Elemente, die älter als das Fenster sind, werden verworfen. Elemente mit fehlendem oder nicht analysierbarem Datum werden standardmäßig ausgeschlossen (missing_date_policy: exclude), da ein Datum, das die Engine nicht überprüfen kann, ein Datum ist, dem sie nicht vertrauen kann. Setze missing_date_policy: include nur für Quellen, bei denen bekannt ist, dass die Daten unzuverlässig sind und der Startseiten-Diff das primäre Signal ist.

Diese Regel existiert, weil die Durchsetzung des Datumsfensters Buchhaltung ist, kein Urteil. Sie muss deterministisch und prüfbar sein – niemals an ein LLM delegiert werden, das dazu neigt, Daten falsch zu lesen und alte Geschichten in den heutigen Bericht zu mischen.

3.2 Bearbeiten — Vorfilter mit dem Profil

Ziel: Kandidaten mit kostengünstigen Regeln bewerten; den breiten Trichter beibehalten.

Formel:

score = Summe(gewichtete Schlüsselwort-Treffer)
      + Summe(Kategorie-Übereinstimmungsboni)
      - Summe(gewichtete negative Treffer)

Schritte:

  1. Das neueste Profil des Ressorts laden.
  2. Für jeden Kandidatentitel:
    • Schlüsselwörter und Kategorien abgleichen (Wortgrenze für ASCII-Begriffe).
    • Negative abziehen.
    • last_hit für jeden passenden Begriff aufzeichnen.
  3. Schwellwert: Standardmäßig bestehen score >= 2; niedrigere Bewertungen werden mit reject_reason: low_score abgelehnt.
  4. Jeden Kandidaten als item-Datensatz auf das Tape schreiben:
    • Bestanden → stage: prescreened.
    • Abgelehnt → stage: rejected, mit reject_reason.

Abgelehnte Elemente sind der Treibstoff für die Kalibrierung. Lösche sie niemals.

3.3 Prüfen — Strukturierter zweiter Durchlauf

Ziel: Sicherstellen, dass vorgefilterte Elemente echte Nachrichten, themenrelevant und keine Werbung/Jobs sind.

Schritte:

  1. Vorgefilterte Elemente nach Punktzahl sortieren (absteigend).
  2. Die Darstellung pro Quelle begrenzen (z.B. max. 4 pro Quelle), um eine Monopolstellung zu vermeiden.
  3. Für jedes Element den Artikeltext abrufen (erste ~1000 Zeichen) und ein Veröffentlichungsdatum über eine Fallback-Kette extrahieren:
    • JSON-LD datePublished
    • <meta article:published_time>
  4. Einen Batch-Prompt an den konfigurierten LLM-Anbieter senden. Für jedes Element abfragen:
    • is_news — keine Anzeige/Job/Ankündigung
    • is_ad, is_job — Ausschlusskriterien
    • vertical_fit — Relevanzbewertung von 0 bis 1
    • summary — Zusammenfassung in 2-3 Sätzen
  5. Ablehnungsgründe anwenden:
    • not_news, is_ad, is_job, low_fit.
  6. Ergebnisse als item-Datensätze mit stage: verified oder stage: rejected auf das Tape schreiben.

Wenn kein LLM-Anbieter konfiguriert ist: Die Pipeline läuft im --dry Nur-Regel-Modus und befördert vorgefilterte Elemente direkt.

3.4 Veröffentlichen — Deduplizieren und Berichten

Ziel: Duplikate entfernen und einen lesbaren täglichen Bericht erstellen.

Schritte:

  1. Kürzlich gepoolte Elemente laden (letzte 14 Tage, heute ausgenommen).
  2. Zweistufige Deduplizierung für geprüfte Elemente:
    • Exakte url_norm-Übereinstimmung → überspringen.
    • Titel-simhash mit Hamming-Distanz ≤ 3 → gleiches Ereignis; zu einem Cluster gruppieren.
  3. Für jedes Cluster das Element mit der höchsten Punktzahl als Hauptelement auswählen; andere werden zu verwandten Quellen.
  4. Cluster nach Quellenanzahl und Punktzahl sortieren.
  5. Den Bericht formatieren. Eine Statusfußzeile anhängen:
    • gescannte Quellen, vorgefilterte, abgelehnte, gepoolte Elemente, Anzahl der Berichtselemente
    • Warnungen zur Quellengesundheit
    • Phasenzeitmessungen
  6. Einen report-Datensatz auf das Tape schreiben.

4. Reflexionsschleife (Selbstentwicklung)

Auslöser: Ein Nutzer liest einen Bericht und gibt Feedback, z.B.:

„Mehr KI-Sicherheitsgeschichten, weniger gesponserte Beiträge, und behalte die EU-Regulierung im Auge.“

Schritte:

  1. Feedback in Profiloperationen parsen:
    • add_keyword: {term, weight}
    • adjust_weight: {term, delta}
    • remove_keyword: {term}
    • add_negative: {term, weight}
    • adjust_negative_weight: {term, delta}
    • remove_negative: {term}
  2. Operationen auf das Profil anwenden; Version erhöhen.
  3. Einen evolution-Tape-Datensatz mit den Operationen und dem ursprünglichen Feedbacktext schreiben.
  4. Kürzlich gepoolte Elemente (letzte 7 Tage) gegen die neuen Negativ-/niedrigeren Gewichtungen erneut prüfen.
    • Falls jetzt gefiltert würden, die Anzahl dem Nutzer melden.
  5. Eine Bestätigungszusammenfassung zurückgeben.

Verfallsregel (wöchentlich):

  • origin: learned-Begriffe, die 30 Tage lang nicht getroffen haben, verlieren 1 Gewichtungspunkt.
  • Gewichtung 0 → in eine Löschvorschlagsliste verschieben; Nutzer vor Entfernung fragen.
  • origin: manual-Begriffe werden niemals automatisch gelöscht, nur gewarnt.

5. Taxonomie der Quellenakquisition

Eine Quelle kann auf fünf Arten erworben werden. Die Engine behandelt sie einheitlich, sobald sie das Tape erreichen. Das Quellenregister des Berichts stellt jede Quelle als status [name](url) · method dar, damit ein Leser auf einen Blick sehen kann, wie die Quelle erworben wurde.

TypMechanismusWann verwendenRegisteranzeige
pullRSS/API/curl per CronStabile Feeds und APIs🟢 [Hacker News](https://news.ycombinator.com/rss) · pull
scanStartseiten-Diff + VorfilterSPAs, unregelmäßige Seiten🟢 [TechCrunch](https://techcrunch.com) · scan
pushWebhook/SSE/BridgeInterne Warnungen oder geschlossene Ökosysteme🟢 [Internal Alerts](...) · push
searchAd-hoc-SucheNeuquellen-Entdeckung oder Aufholen bei Großereignissen🟢 [Search](...) · search
human-feedNutzer gibt einen LinkExklusive Tipps und Signale aus erster Hand🟢 Weixin Tip · human-feed

Füge eine Quelle hinzu, indem du {id, name, list_url, extract_profile} in der Ressortkonfiguration registrierst. Eine Human-Feed-Quelle setzt extract_profile = { method = "human_feed" } und kann list_url weglassen.

Agent-unterstützter Human-Feed

Eine nützliche Variante ist der Agent-unterstützte Human-Feed: Der Agent selbst führt eine Plattform-Suche durch (z.B. auf X nach Beiträgen unter einem KI-Hashtag in den letzten 24–36 Stunden), kuratiert ≥1 Kandidaten und injiziert ihn über den Human-Feed-Kanal. Das injizierte Element durchläuft dennoch dieselben Phasen der Vorfilterung, Prüfung und Deduplizierung wie Pull/Scan-Elemente – der Human-Feed garantiert den Eintritt in den Kandidatenpool, keinen Freifahrtschein in den Bericht.

„Hör niemals bei der Nachricht auf“

Ein Social-Media-Beitrag, eine Schlagzeile oder ein Tipp ist ein Signal, kein Zitat. Jedes injizierte Element muss die ursprüngliche Artikel-URL auflösen, bevor es das Tape erreicht. Wenn der Agent nur eine Beitrags-URL finden kann, versucht scripts/x_hunt.py eine Suche nach bestem Wissen und Gewissen nach dem Originalartikel; falls dies fehlschlägt, wird der Kandidat verworfen und der Grund auf dem Tape aufgezeichnet. Der veröffentlichte Bericht verweist den Leser niemals auf einen Beitrag oder eine Schlagzeile als endgültige Quelle.

Referenzimplementierung (scripts/x_hunt.py)

scripts/x_hunt.py ist deterministische Buchhaltung: es schreibt ein bereits kuratiertes Element auf das Tape. Die Suche, das Lesen und die Auswahl sind die Beurteilungsarbeit des Agenten, die mit den eigenen WebSearch-/Tools des Agenten durchgeführt wird.

# Bevorzugter Pfad: Der Agent hat die Schlagzeile bereits zur ursprünglichen Artikel-URL aufgelöst.
python scripts/x_hunt.py --vertical ai-frontier --source-id ainativef_zh \
  --title "OpenAI kündigt GPT-5 an" \
  --url "https://openai.com/blog/introducing-gpt-5" \
  --inject

# Wenn nur eine X-Beitrags-URL verfügbar ist, versucht das Skript, die Original-URL aufzulösen.
python scripts/x_hunt.py --vertical ai-frontier --source-id ainativef_zh \
  --title "OpenAI kündigt GPT-5 an" \
  --url "https://x.com/OpenAI/status/1234567890" \
  --inject

# Automatisierte Suche nach bestem Wissen und Gewissen als Fallback (oft blockiert; sparsam verwenden).
python scripts/x_hunt.py --vertical ai-frontier \
  --query "KI künstliche Intelligenz site:x.com" --inject

# Kandidaten des Such-Fallbacks überprüfen, ohne zu injizieren
python scripts/x_hunt.py --vertical ai-frontier \
  --query "KI künstliche Intelligenz site:x.com"

Der injizierte Datensatz wird als item auf das Tape geschrieben mit:

  • source_id: die registrierte Quellen-ID (Standard owner_tips).
  • stage: scanned — es tritt in die Pipeline bei der Vorfilterung ein.
  • facilitated_by: agent — prüfbare Herkunft.

Verworfene Datensätze (nicht auflösbare Beitrags-URLs, unpassende Kandidaten usw.) werden ebenfalls mit stage: dropped und einem drop_reason an das Tape angehängt, sodass die Prüfspur auch das enthält, was es nicht geschafft hat.

Trennung der Verantwortlichkeiten: Beurteilung (Suche/Lesen/Auswahl/Quellenauflösung) gehört dem Agenten; Buchhaltung (URL normalisieren, ID zuweisen, an Tape anhängen) gehört dem Skript. Dies hält die Fähigkeit deterministisch und überlässt plattformspezifische Abrufstrategien den eigenen Fähigkeiten des Agenten.

Dieser Modus verwandelt die Plattform von einer passiven Inhaltsquelle in eine aktive Quellenjagd: wenn der Agent wiederholt gute Geschichten von derselben Publikation findet, kann ECHO fragen „„‘ als verfolgte Quelle hinzufügen?“ und bei Zustimmung die Publikation zu einer formellen Pull/Scan-Quelle erheben. Diese geschlossene Schleife ist die Selbstentwicklung der Fähigkeit in Aktion.


6. Tape-Datensatztypen

Jede Tape-Zeile ist ein JSON-Objekt mit einem type-Feld.

TypFelder
sourceid, name, vertical, list_url, extract_profile, health
frontpagesource_id, date, items[], count
profilevertical, version, keywords[], negatives[], categories[], reason
itemid, vertical, source_id, title, url, url_norm, stage, scores, reject_reason, simhash, cluster_id
reportdate, vertical, item_ids[], generated_at, stats
feedbackdate, vertical, text, applied_ops[], status
evolutionvertical, from_version, to_version, ops[], reason, date

7. Ausführen der Referenzimplementierung

# Nur-Regel-Testlauf (kein LLM erforderlich)
python -m engine.pipeline --dry --vertical tech --config config/example_vertical.toml

# Mit LLM-Prüfung
export APERTURE_LLM_API_KEY="sk-..."
python -m engine.pipeline --vertical tech --config config/example_vertical.toml

# Feedback anwenden
python -c "from engine.feedback import apply_feedback; apply_feedback('Mehr KI-Sicherheit, weniger Werbung', 'tech')"

Nach der Veröffentlichung optional ECHO ausführen, um dem Nutzer bis zu zwei Klarstellungsfragen zu stellen:

from engine.echo import ask

questions = ask("tech")
for q in questions:
    print(q["question"])

# Später eine Ein-Wort-Antwort anwenden: yes / no / skip / silence
from engine.echo import apply_answer
apply_answer(question_id, "tech", "yes")

8. ECHO — Proaktive Klarstellung

ECHO ist eine optionale proaktive Schicht, die dem Nutzer nach jedem Bericht bis zu zwei Ein-Wort-Klarstellungsfragen stellt. Es verwandelt passive Leser in aktive Profil-Trainer.

ECHO ist absichtlich

…

[für die Übersetzung gekürzt]

Verwandte Skills