Architecture
Pourquoi Dinoer est conçu de cette manière : ce qu'il garantit, ce qu'il refuse et quelles sont ses limites – limites déjà réduites sur la base de preuves réelles.
Comment se déroule une campagne de recherche
manifest.json
│ campagne.py
▼
SearXNG ── requêtes
▼
récupération HTTP simple ──► navigateur réel (seulement si la page ne suffit pas)
▼
collecte.jsonl + operations.jsonl
▼
OpenCode ── classement, puis rapport
▼
rapport_<timestamp>.md
- Le manifeste est un fichier JSON de cibles : une
querypour SearXNG, uneurlfixe, unproduit(trouver l’unique page qui est réellement la page du produit) ou unetable_reference(une liste de domaines de confiance pour un sujet). Seuls l’identifiant de la campagne et les cibles sont obligatoires. - Découverte. Une cible
querypart vers l’instance SearXNG que vous avez configurée. Cette instance interroge à son tour d’autres moteurs de recherche, ce que Dinoer ne contrôle pas. - Collecte. Chaque page est récupérée par une simple requête HTTP, une fois son
robots.txtlu. Seule une page qui ne suffit pas passe à un navigateur réel, un processus Playwright par appel. Une cible qui échoue n’arrête pas les autres. - Le corpus. Un
collecte.jsonlpar campagne, une ligne par extraction réussie, etoperations.jsonl, qui consigne chaque tentative, réussie ou non. - Le rapport. Les pages sont classées (par plongements locaux quand vous donnez un sujet), tronquées à un budget de taille, puis remises à OpenCode, qui écrit le corps de
rapport_<timestamp>.md. La liste des sources est ajoutée par Dinoer lui-même ; le modèle ne l’écrit jamais.
shot.py et rpa.py sont différents : des appels indépendants, chacun avec son propre processus,
qui se terminent dès qu’ils ont répondu. Pourquoi →
Qu’est-ce qui sort de votre machine, et par quelle porte ?
| Quoi | Où ça va | Qui décide | Dans le code |
|---|---|---|---|
| Requêtes de découverte | l’instance SearXNG que vous avez configurée, qui interroge à son tour d’autres moteurs | vous, avec DINOER_SEARXNG_URL | lib/searxng.py |
| Requêtes de pages | chaque site directement, son robots.txt est lu en premier | les cibles dans votre manifeste | lib/fetch_leger.py, rpa.py |
| Texte collecté | le modèle d’OpenCode, hébergé par défaut, pour le rapport, pour l’extraction ciblée et pour choisir une page produit (les 1500 premiers caractères de chaque candidat) | vous, avec DINOER_OPENCODE_MODEL | lib/modeles.py, lib/synthese.py, lib/extraction.py, lib/selection_candidats.py |
| Un sujet, pour un tableau de référence | le modèle d’OpenCode, le sujet uniquement, lorsqu’aucun tableau n’existe pour lui | la cible table_reference | lib/tables_reference.py |
| Embeddings | votre Ollama local, http://localhost:11434: le texte reste sur la machine sauf si vous pointez DINOER_OLLAMA_URL ailleurs | vous | lib/vector.py |
| Une notification | un serveur ntfy, https://ntfy.sh sauf si vous en configurez un autre : l’identifiant de la campagne et le nombre de sources, jamais le chemin local du rapport. Uniquement lorsqu’un topic ntfy est défini | vous, avec ntfy_topic, DINOER_NTFY_TOPIC, DINOER_NTFY_URL | campagne.py, lib/ntfy.py |
| Vos identifiants | nulle part : résolus à l’intérieur du processus Playwright, à partir du répertoire chiffré | — | lib/repertoire_chiffre.py |
Source : le code de la version 1.0.1, lu le 25 septembre 2026. Les modèles opencode/ sont hébergés : ce fournisseur répond à opencode.ai/zen (d’après opencode models --verbose, OpenCode 1.18.32). Ce que le modèle a ensuite le droit de faire avec le web est décrit sur la page de confiance, avec la limite qu’elle ne couvre pas.