Dinoer

Llevar a cabo una campaña de investigación

El manifiesto, los dos archivos que escribe una campaña, los campos que hacen útil el informe y cómo interrogar con campagne.py un corpus ya recopilado.

campagne.py se sitúa por encima del núcleo de navegación: búsqueda con SearXNG, recopilación HTTP ligera, paso a rpa.py/shot.py solo para las páginas que necesitan un navegador, y un informe que un modelo redacta a partir de las páginas recopiladas.

# Instalación por clon de Git
/opt/dinoer/venv/bin/python3 /opt/dinoer/campagne.py --manifeste manifeste.json
# Paquete .deb
dinoer-campaign --manifeste manifeste.json

El manifiesto

Solo id_campagne y cibles son obligatorios; los demás campos tienen un valor por defecto. El ejemplo documentado en campagne.py:

{
  "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
}

Cuatro tipos de objetivo: query (una búsqueda en SearXNG), url (una página fija), produit (recopilar varias páginas candidatas sobre algo con nombre y hacer que el modelo elija la que de verdad trata de ello), table_reference (crear o reutilizar una lista guardada de sitios de confianza para un tema y buscar después en cada uno).

Cada objetivo se procesa por separado: uno que falla no detiene a los demás, mientras que la lista de acciones de un escenario se detiene en el primer fallo.

Dos archivos, dos usos

operations.jsonl es el registro de operaciones que comparten todos los programas de Dinoer. Una campaña añade una línea por intento, con éxito o sin él, marcada con intention="dinoer-campagne".

<directorio de campañas>/<id_campagne>/collecte.jsonl tiene una línea por página conservada, y solo esas. El informe se construye a partir de él, y es el archivo que hay que abrir para comprobar que una afirmación procede de una página recopilada. El directorio de campañas es /var/log/dinoer/campagnes, salvo que DINOER_CAMPAGNES_DIR o la clave campagnes_dir de la configuración indiquen otro.

Reordenar las páginas antes de que el informe las corte

Por defecto, el paso del informe une las páginas en el orden en que se escribieron y corta a los 60 000 caracteres, sin clasificación. Dos campos opcionales del manifiesto reordenan las páginas; ninguno elimina ninguna:

{
  "motifs_annee": ["2026"],
  "motifs_mois": ["août", "aout", "/08", "-08-"],
  "sujet_synthese": "concerts and festivals, south Finistère, summer 2026"
}

motifs_annee/motifs_mois es un filtro de texto, sin modelo alguno: las páginas que claramente no mencionan el periodo pedido pasan al final. Incluya en la lista formas numéricas como /08, porque algunas páginas de agenda nunca escriben el mes con letras. sujet_synthese ordena las páginas por su cercanía de significado con esa frase, mediante embeddings de Ollama. Use los dos. En la campaña de referencia, el filtro de texto dejaba por sí solo el programa oficial de actos en el puesto 27 de las 29 páginas conservadas, más allá del corte; la clasificación por significado lo subió al puesto 4.

Hacer una pregunta abierta a un corpus ya recopilado

/opt/dinoer/venv/bin/python3 /opt/dinoer/campagne.py --extraire-cible "<question>" \
  --id-campagne <id> --format-extraction markdown

--extraire-cible acepta cualquier pregunta en lenguaje natural, no solo la búsqueda de un dato; --corpus <archivo> lo dirige a un collecte.jsonl situado en otro lugar. En la campaña territorial de agosto de 2026, 47 llamadas con una pregunta abierta dieron 17 respuestas positivas. En una página, la pregunta estrecha solo había conservado una fecha de un festival de cinco días; la abierta devolvió el programa entero y otros cuatro eventos del mismo texto.

Para agrupar los resultados que describen el mismo evento en varias páginas, lib/extraction.py::fusionner_evenements() los fusiona y conserva cada dirección de origen. Aplicada de nuevo a las 15 extracciones positivas de esa campaña, encontró los mismos 11 eventos que el recuento hecho a mano. Una página que describe varios eventos se cita en cada grupo al que pertenece.

El modelo que redacta el informe se mantiene fuera de la web

En cada llamada, Dinoer retira al modelo sus herramientas websearch y webfetch, sea cual sea el directorio desde el que se lanza la campaña. Sin esto, un modelo lanzó una vez por su cuenta doce búsquedas web, invisibles en el texto que devolvió. Qué cubre esto, y la brecha que queda →

Ritmo y actualidad

delai_min_secondes/delai_max_secondes fijan una pausa aleatoria antes de cada consulta a SearXNG y antes de cada página obtenida. revisite_apres_jours (30 por defecto) impide que una campaña vuelva a obtener una página recopilada hace poco. Las mismas reglas, completas →

La caché de búsqueda

lib/cache_recherche.py guarda en ChromaDB las páginas recopiladas para cada consulta. Una consulta posterior de significado cercano se sirve desde ella, sin nueva búsqueda ni nueva obtención; esas páginas llevan "source": "cache" en el corpus. --desactiver-cache omite la caché en una ejecución; --purger-cache y --purger-cache-avant-jours N la vacían. Si ChromaDB u Ollama no están disponibles, la caché simplemente falla y la campaña continúa.

En resumen

  • Solo id_campagne y cibles son obligatorios.
  • Cuatro tipos de objetivo: query, url, produit, table_reference.
  • collecte.jsonl es el archivo al que remite una afirmación: ábralo en lugar de fiarse solo del informe.
  • Indique juntos motifs_annee/motifs_mois y sujet_synthese para que el informe no corte las páginas útiles.
  • Una pregunta abierta a --extraire-cible, página por página, saca más de un corpus que una estrecha.
  • El modelo que redacta el informe se mantiene fuera de la web, salvo por bash.