Arquitectura
¿Por qué Dinoer está construido de la manera en que lo está?: ¿qué garantiza, a qué se niega y cuáles son sus límites? Estos últimos ya han sido definidos con base en evidencia real.
Cómo se desarrolla una campaña de investigación
manifest.json
│ campagne.py
▼
SearXNG ── consultas
▼
descarga HTTP simple ──► navegador real (solo si la página no basta)
▼
collecte.jsonl + operations.jsonl
▼
OpenCode ── clasificación, luego informe
▼
rapport_<timestamp>.md
- El manifiesto es un archivo JSON de objetivos: una
querypara SearXNG, unaurlfija, unproduit(encontrar la única página que de verdad es la página del producto) o unatable_reference(una lista de dominios de confianza para un tema). Solo son obligatorios el identificador de la campaña y los objetivos. - Descubrimiento. Un objetivo
queryva a la instancia de SearXNG que hayas configurado. Esa instancia consulta a su vez otros motores de búsqueda, algo que Dinoer no controla. - Recopilación. Cada página se obtiene con una simple petición HTTP, una vez leído su
robots.txt. Solo una página que no basta pasa a un navegador real, un proceso de Playwright por llamada. Un objetivo que falla no detiene a los demás. - El corpus. Un
collecte.jsonlpor campaña, una línea por extracción correcta, yoperations.jsonl, que registra cada intento, tenga éxito o no. - El informe. Las páginas se ordenan (con embeddings locales cuando indicas un tema), se recortan a un presupuesto de tamaño y se entregan a OpenCode, que escribe el cuerpo de
rapport_<timestamp>.md. La lista de fuentes la añade Dinoer mismo; el modelo nunca la escribe.
shot.py y rpa.py son diferentes: llamadas independientes, cada una con un proceso,
que finalizan tan pronto como han sido atendidas. ¿Por qué →
Qué sale de su máquina, y por cuál puerta
| Qué | Adónde va | Quién decide | En el código |
|---|---|---|---|
| Consultas de descubrimiento | la instancia de SearXNG que usted configuró, que a su vez consulta otros motores | usted, con DINOER_SEARXNG_URL | lib/searxng.py |
| Solicitudes de páginas | cada sitio directamente, con lectura previa de su robots.txt | los objetivos en su manifiesto | lib/fetch_leger.py, rpa.py |
| Texto recopilado | el modelo de OpenCode, alojado por defecto, para el informe, para la extracción específica y para elegir una página de producto (los primeros 1500 caracteres de cada candidato) | usted, con DINOER_OPENCODE_MODEL | lib/modeles.py, lib/synthese.py, lib/extraction.py, lib/selection_candidats.py |
| Un tema, para una tabla de referencia | el modelo de OpenCode, solo el tema, cuando aún no existe una tabla para este | el objetivo table_reference | lib/tables_reference.py |
| Embeddings | su Ollama local, http://localhost:11434: el texto permanece en la máquina a menos que usted apunte DINOER_OLLAMA_URL a otro lugar | usted | lib/vector.py |
| Una notificación | un servidor ntfy, https://ntfy.sh a menos que usted configure otro: el identificador de la campaña y el número de fuentes, nunca la ruta local del informe. Solo cuando se establece un tema ntfy | usted, con ntfy_topic, DINOER_NTFY_TOPIC, DINOER_NTFY_URL | campagne.py, lib/ntfy.py |
| Sus credenciales | ningún lugar: se resuelven dentro del proceso de Playwright, desde el directorio cifrado | — | lib/repertoire_chiffre.py |
Fuente: el código de la versión 1.0.1, leído el 25 de septiembre de 2026. Los modelos opencode/ están alojados: ese proveedor responde en opencode.ai/zen (según opencode models --verbose, OpenCode 1.18.32). Lo que el modelo puede hacer después con la web está en la página de confianza, con el límite que no cubre.