Eine Recherchekampagne durchführen
Das Manifest, die zwei Dateien, die eine Kampagne schreibt, die Felder, die den Bericht brauchbar machen, und wie man mit campagne.py einen bereits gesammelten Korpus befragt.
campagne.py sitzt über dem Browserkern: Suche über SearXNG, leichte
HTTP-Erfassung, Wechsel zu rpa.py/shot.py nur für die Seiten, die einen
Browser brauchen, und ein Bericht, den ein Modell aus den gesammelten Seiten
schreibt.
# Installation per Git-Klon
/opt/dinoer/venv/bin/python3 /opt/dinoer/campagne.py --manifeste manifeste.json
# .deb-Paket
dinoer-campaign --manifeste manifeste.json
Das Manifest
Pflicht sind nur id_campagne und cibles; alle anderen Felder haben einen
Standardwert. Das Beispiel, das in campagne.py dokumentiert ist:
{
"id_campagne": "concerts-finistere-2026-07-28",
"cibles": [
{"type": "query", "valeur": "concerts finistere sud ete"},
{"type": "url", "valeur": "https://exemple.fr/agenda"},
{"type": "produit", "valeur": "boulangerie Corentin a Quimper", "max_candidats": 5},
{"type": "table_reference", "valeur": "mairies du Finistere sud", "cle_thematique": "mairies_finistere_sud"}
],
"max_resultats": 15,
"max_pages_par_hostname": 3,
"delai_min_secondes": 4.5,
"delai_max_secondes": 8.5,
"revisite_apres_jours": 30
}
Vier Zieltypen: query (eine SearXNG-Suche), url (eine feste Seite),
produit (mehrere Kandidatenseiten zu einer benannten Sache sammeln und das
Modell die auswählen lassen, die wirklich davon handelt), table_reference
(eine gespeicherte Liste vertrauenswürdiger Websites zu einem Thema anlegen
oder wiederverwenden und dann in jeder davon suchen).
Jedes Ziel wird für sich verarbeitet: Scheitert eines, laufen die anderen weiter, während die Aktionsliste eines Szenarios beim ersten Fehler anhält.
Zwei Dateien, zwei Zwecke
operations.jsonl ist das Betriebsjournal, das alle Dinoer-Programme
teilen. Eine Kampagne fügt pro Versuch eine Zeile hinzu, erfolgreich oder
nicht, markiert mit intention="dinoer-campagne".
<Kampagnenverzeichnis>/<id_campagne>/collecte.jsonl enthält eine Zeile
pro behaltener Seite, und nur diese. Daraus entsteht der Bericht, und diese
Datei öffnen Sie, um zu prüfen, dass eine Aussage von einer gesammelten Seite
stammt. Das Kampagnenverzeichnis ist /var/log/dinoer/campagnes, sofern
DINOER_CAMPAGNES_DIR oder der Schlüssel campagnes_dir der Konfiguration
nichts anderes angeben.
Die Seiten ordnen, bevor der Bericht sie abschneidet
Standardmäßig hängt der Berichtsschritt die Seiten in der Reihenfolge ihres Schreibens aneinander und schneidet bei 60.000 Zeichen ab, ohne Rangfolge. Zwei optionale Felder des Manifests ordnen die Seiten neu; keines entfernt welche:
{
"motifs_annee": ["2026"],
"motifs_mois": ["août", "aout", "/08", "-08-"],
"sujet_synthese": "concerts and festivals, south Finistère, summer 2026"
}
motifs_annee/motifs_mois ist ein Textfilter ohne Modell: Seiten, die den
gesuchten Zeitraum offensichtlich nicht erwähnen, rücken ans Ende. Nehmen Sie
Zahlenformen wie /08 in die Liste auf, denn manche Kalenderseiten schreiben
den Monat nie aus. sujet_synthese ordnet die Seiten nach ihrer inhaltlichen
Nähe zu diesem Satz, mit Embeddings von Ollama. Setzen Sie beide. Bei der
Referenzkampagne ließ der Textfilter allein das offizielle
Veranstaltungsprogramm auf Platz 27 der 29 behaltenen Seiten, hinter dem
Schnitt; die Rangfolge nach Bedeutung holte es auf Platz 4.
Einem bereits gesammelten Korpus eine offene Frage stellen
/opt/dinoer/venv/bin/python3 /opt/dinoer/campagne.py --extraire-cible "<question>" \
--id-campagne <id> --format-extraction markdown
--extraire-cible nimmt jede Frage in natürlicher Sprache an, nicht nur die
Suche nach einer einzelnen Angabe; --corpus <Datei> lenkt es auf eine
collecte.jsonl an anderer Stelle. Bei der regionalen Kampagne vom August
2026 lieferten 47 Aufrufe mit einer offenen Frage 17 positive Antworten. Auf
einer Seite hatte die enge Frage von einem fünftägigen Festival nur ein Datum
behalten; die offene lieferte das ganze Programm und vier weitere
Veranstaltungen aus demselben Text.
Um Ergebnisse zu gruppieren, die dasselbe Ereignis auf mehreren Seiten
beschreiben, führt lib/extraction.py::fusionner_evenements() sie zusammen und
behält jede Quelladresse. Mit den 15 positiven Extraktionen dieser Kampagne
nachgespielt, fand sie dieselben 11 Veranstaltungen wie die Zählung von Hand.
Eine Seite, die mehrere Ereignisse beschreibt, wird in jeder Gruppe genannt,
zu der sie gehört.
Das Modell, das den Bericht schreibt, bleibt dem Web fern
Bei jedem Aufruf entzieht Dinoer dem Modell die Werkzeuge websearch und
webfetch, gleich aus welchem Verzeichnis die Kampagne gestartet wird. Ohne
das hat ein Modell einmal von sich aus zwölf Websuchen gestartet, unsichtbar im
zurückgegebenen Text.
Was das abdeckt, und die Lücke, die bleibt →
Takt und Aktualität
delai_min_secondes/delai_max_secondes legen eine zufällige Pause vor jeder
SearXNG-Anfrage und vor jeder abgerufenen Seite fest. revisite_apres_jours
(standardmäßig 30) verhindert, dass eine Kampagne eine kürzlich erfasste Seite
erneut abruft. Dieselben Regeln, vollständig →
Der Such-Cache
lib/cache_recherche.py speichert in ChromaDB die für jede Anfrage gesammelten
Seiten. Eine spätere, inhaltlich ähnliche Anfrage wird daraus bedient, ohne
neue Suche und ohne neuen Abruf; diese Seiten tragen im Korpus
"source": "cache". --desactiver-cache umgeht den Cache für einen Lauf;
--purger-cache und --purger-cache-avant-jours N leeren ihn. Sind ChromaDB
oder Ollama nicht verfügbar, wird der Cache einfach verfehlt und die Kampagne
läuft weiter.
Kurz gesagt
- Pflicht sind nur
id_campagneundcibles. - Vier Zieltypen:
query,url,produit,table_reference. collecte.jsonlist die Datei, auf die sich eine Aussage zurückführen lässt: Öffnen Sie sie, statt sich nur auf den Bericht zu verlassen.- Setzen Sie
motifs_annee/motifs_moisundsujet_synthesezusammen, damit der Bericht die nützlichen Seiten nicht abschneidet. - Eine offene Frage an
--extraire-cible, Seite für Seite, holt mehr aus einem Korpus heraus als eine enge. - Das Modell, das den Bericht schreibt, wird vom Web ferngehalten, außer über
bash.