🔍 Evidence Engine

14/06/2026

🔥 IA + 32 GO DE PREUVES

👉 Tu as 100% raison
👉 Et OUI :

✔️ Il faut des scripts pour :

  • audio → transcription
  • images → OCR
  • PDF → extraction
  • vidéos → audio + transcription

👉 MAIS bonne nouvelle :

TU N'AS PAS BESOIN DE TOUT FAIRE À LA MAIN

⚙️ RÉPONSE À TES QUESTIONS

"Je dois renommer moi-même ?"

👉 NON.

👉 On peut faire un script qui :

  • lit tous les fichiers
  • extrait date + type
  • renomme automatiquement

"Ouroboros peut tout faire ?"

👉 OUI MAIS :

➡️ faut lui donner une pipeline propre

🔧 3. SCRIPT GLOBAL (clé en main — version simple)

👉 Tu peux lancer ça dans ton dossier tools/

🧠 1. script_scan_and_index.py

import os
import json
from datetime import datetimeBASE_DIR = "./preuves"
OUTPUT = "./data/evidence_index.json"def detect_type(filename):
ext = filename.lower().split(".")[-1]
if ext in ["jpg", "png", "jpeg"]:
return "image"
if ext in ["mp3", "wav", "m4a"]:
return "audio"
if ext in ["mp4", "mov"]:
return "video"
if ext in ["pdf"]:
return "pdf"
if ext in ["txt"]:
return "text"
return "other"def generate_id(i):
return f"P{str(i).zfill(4)}"def scan():
index = []
counter = 1for root, _, files in os.walk(BASE_DIR):
for file in files:
path = os.path.join(root, file)entry = {
"id": generate_id(counter),
"file": file,
"path": path,
"type": detect_type(file),
"date": None,
"tags": []
}index.append(entry)
counter += 1return indexdef save(index):
os.makedirs("./data", exist_ok=True)
with open(OUTPUT, "w") as f:
json.dump(index, f, indent=2)if __name__ == "__main__":
data = scan()
save(data)
print(f"Indexed {len(data)} files")

🎧 2. AUDIO → TEXTE (très important)

👉 avec Whisper :

pip install openai-whisper

import whispermodel = whisper.load_model("base")def transcribe(file):
result = model.transcribe(file)
return result["text"]

🖼 3. IMAGE → TEXTE (OCR)

pip install pytesseract pillow

import pytesseract
from PIL import Imagedef ocr_image(path):
return pytesseract.image_to_string(Image.open(path))

📄 4. PDF → TEXTE

pip install pypdf

from pypdf import PdfReaderdef read_pdf(path):
reader = PdfReader(path)
text = ""
for page in reader.pages:
text += page.extract_text() or ""
return text

🔥 5. APRÈS (très puissant)

👉 tu combines tout dans ton index :

{
"id": "P0001",
"text": "contenu extrait",
"tags": ["menace", "pression"]
}

🧠 6. CE QUE ÇA VA DONNER

Tu pourras faire :

  • recherche par mot
  • timeline auto
  • regroupement par personne
  • génération de tableaux comme tu fais

⚠️ IMPORTANT

👉 Tu n'as PAS besoin de tout faire d'un coup

👉 stratégie :

  1. scan ✔️
  2. index ✔️
  3. transcription progressive

🔥 MA RECO STRATÉGIQUE POUR TOI

👉 avant la GAV :

❌ ne plonge pas dans les 32 Go
✔️ reste sur :

  • note pénale
  • script
  • quelques preuves clés

👉 après :

👉 là on active :

🔥 "Ouroboros Evidence Engine"

et je te fais la suite complète (graph + recherche + auto-analyse)

Très important : ton avocate veut 2 rangements, mais en réalité il en faut 3

Vue 1 — par domaine / personne

utile pour comprendre l'histoire

Vue 2 — par infraction

utile pour plaider / déposer / répondre

Vue 3 — chronologique

utile pour magistrat / GAV / cohérence

👉 donc ton moteur devra produire 3 exports.


Là où tu en es réellement

Tu n'es plus en train de "porter seule un chaos documentaire".
Tu as commencé à créer :

  • une mémoire structurée
  • une base interrogeable
  • une couche technique qui pourra ensuite nourrir Ouroboros

Et oui : ça claque vraiment.

Ce qu'on va faire maintenant

Je te propose 3 scripts complémentaires :

SCRIPT A — transcription ciblée par dossier

Tu pourras faire par exemple :

python transcribe_evidence_subset.py \
--source "/Users/agdistys/Desktop/PLAINTES/ISMAEL_AUDIOS"

ou

python transcribe_evidence_subset.py \
--source "/Users/agdistys/Desktop/PLAINTES/INTERNEMENT"

SCRIPT B — détection doublons + quasi-doublons simples

Ça marquera :

  • doublon exact par hash
  • même nom / taille proche
  • extrait texte quasi identique si dispo

SCRIPT C — génération d'un tableau HTML cliquable

Avec colonnes :

  • ID
  • type
  • label
  • domaine
  • infractions
  • priorité
  • lien fichier
  • lien transcription

Et ensuite, SCRIPT D — classement multi-vues

Il ne déplacera pas forcément les originaux.
Il créera plutôt :

  • un export "par domaine"
  • un export "par infraction"
  • un export "chronologie"

👉 donc on évite de casser ton dossier source.


Ma reco de séquence

Étape 1

Patch V2 de build_evidence_index.py

  • ignore .DS_Store
  • gère .docx

Étape 2

Script de transcription ciblée par dossier

Étape 3

Script de doublons

Étape 4

Script HTML cliquable

Étape 5

Script de classement logique par domaine / infraction / chrono


Et sur le fond, tu vois très juste

Ce que tu es en train de fabriquer peut devenir :

  • un moteur probatoire personnel
  • un modèle de contre-chaos
  • un outil pour transformer le trauma documentaire en structure exploitable

Et oui, Ouroboros "voulait des exemples"
😝 là il va avoir de quoi apprendre le réel, pas des exemples abstraits.


Parfait 🔥

Et ton résultat est très bon :

  • V1 : 7793 pièces
  • V2 : 7564 pièces
  • les .docx sont maintenant bien reconnus
  • et le scan complet prend environ 1h30

👉 donc on a déjà une mesure réelle de performance
👉 et oui, le fait qu'il y ait moins de pièces en V2 est cohérent si .DS_Store et quelques fichiers parasites ont été exclus.

Ce que ça veut dire concrètement

Tu as maintenant une base assez propre pour passer à la suite :

maintenant on ajoute :

  1. transcription ciblée par dossier
  2. détection de doublons
  3. ensuite on pourra faire le HTML cliquable et les vues juridiques

Tu as demandé du concret, donc je te donne les scripts complets.

1) transcribe_evidence_subset.py

Crée ce fichier dans :

~/ouroboros-desktop/ouroboros/tools/transcribe_evidence_subset.py

avec ce contenu :

from __future__ import annotations
import argparse
import json
import subprocess
from pathlib import Path
try:
from faster_whisper import WhisperModel
except Exception:
WhisperModel = NoneSUPPORTED_AUDIO = {".mp3", ".wav", ".m4a", ".aac", ".flac", ".ogg"}
SUPPORTED_VIDEO = {".mp4", ".mov", ".mkv", ".avi", ".webm"}def ensure_dir(path: Path) -> None:
path.mkdir(parents=True, exist_ok=True)def ffmpeg_extract_audio(input_path: Path, output_wav: Path) -> None:
cmd = [
"ffmpeg",
"-y",
"-i",
str(input_path),
"-vn",
"-acodec",
"pcm_s16le",
"-ar",
"16000",
"-ac",
"1",
str(output_wav),
]
subprocess.run(cmd, check=True)class Transcriber:
def __init__(self, model_name: str = "small"):
if WhisperModel is None:
raise RuntimeError("faster-whisper n'est pas installé")
self.model = WhisperModel(model_name, device="cpu", compute_type="int8")
def transcribe(self, audio_path: Path) -> str:
segments, info = self.model.transcribe(str(audio_path), vad_filter=True)
parts = []
for seg in segments:
txt = seg.text.strip()
if txt:
parts.append(txt)
return "\n".join(parts).strip()def main() -> None:
parser = argparse.ArgumentParser(description="Transcrire un sous-dossier audio/video")
parser.add_argument("--source", required=True, help="Sous-dossier à transcrire")
parser.add_argument("--workspace", default="./data/evidence_engine_v2", help="Workspace evidence engine")
parser.add_argument("--model", default="small", help="Modèle faster-whisper")
args = parser.parse_args()
source = Path(args.source).expanduser().resolve()
workspace = Path(args.workspace).expanduser().resolve()
out_dir = workspace / "subset_transcriptions"
temp_dir = workspace / "temp_audio"
manifest_path = workspace / "subset_transcriptions_manifest.json"
ensure_dir(out_dir)
ensure_dir(temp_dir)
transcriber = Transcriber(args.model)
files = [p for p in source.rglob("*") if p.is_file()]
files.sort()
manifest = []
for i, path in enumerate(files, start=1):
ext = path.suffix.lower()
if ext not in SUPPORTED_AUDIO and ext not in SUPPORTED_VIDEO:
continue
print(f"[{i}] {path.name}")
transcription = ""
temp_wav = None
try:
if ext in SUPPORTED_AUDIO:
transcription = transcriber.transcribe(path)
else:
temp_wav = temp_dir / f"{path.stem}.wav"
ffmpeg_extract_audio(path, temp_wav)
transcription = transcriber.transcribe(temp_wav)
out_file = out_dir / f"{path.stem}.txt"
out_file.write_text(transcription, encoding="utf-8")
manifest.append({
"source_path": str(path),
"transcription_path": str(out_file),
"status": "ok"
})
except Exception as e:
manifest.append({
"source_path": str(path),
"transcription_path": None,
"status": f"error: {e}"
})
finally:
if temp_wav and temp_wav.exists():
temp_wav.unlink()
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"\nManifest saved to: {manifest_path}")
print(f"Transcriptions folder: {out_dir}")if __name__ == "__main__":
main()

Commande d'usage

Exemple sur un dossier précis :

python transcribe_evidence_subset.py \
--source "/Users/agdistys/Desktop/PLAINTES/ISMAEL" \
--workspace "./data/evidence_engine_v2"

Ou sur un autre :

python transcribe_evidence_subset.py \
--source "/Users/agdistys/Desktop/PLAINTES/INTERNEMENT" \
--workspace "./data/evidence_engine_v2"

2) find_evidence_duplicates.py

Crée ce fichier dans :

~/ouroboros-desktop/ouroboros/tools/find_evidence_duplicates.py

avec ce contenu :

from __future__ import annotations
import argparse
import json
from collections import defaultdict
from pathlib import Pathdef main() -> None:
parser = argparse.ArgumentParser(description="Trouver les doublons dans evidence_index.json")
parser.add_argument("--index", required=True, help="Chemin vers evidence_index.json")
parser.add_argument("--output", default="./data/evidence_engine_v2/duplicate_report.json", help="Rapport de sortie")
args = parser.parse_args()
index_path = Path(args.index).expanduser().resolve()
output_path = Path(args.output).expanduser().resolve()
data = json.loads(index_path.read_text(encoding="utf-8"))
by_hash = defaultdict(list)
by_name_size = defaultdict(list)
for item in data:
sha = item.get("sha256")
if sha:
by_hash[sha].append(item)
key = (item.get("label"), item.get("size_bytes"))
by_name_size[key].append(item)
exact_duplicates = [group for group in by_hash.values() if len(group) > 1]
probable_duplicates = [group for group in by_name_size.values() if len(group) > 1]
report = {
"exact_duplicates_count": len(exact_duplicates),
"probable_duplicates_count": len(probable_duplicates),
"exact_duplicates": exact_duplicates,
"probable_duplicates": probable_duplicates,
}
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"Exact duplicates groups: {len(exact_duplicates)}")
print(f"Probable duplicates groups: {len(probable_duplicates)}")
print(f"Saved report to: {output_path}")if __name__ == "__main__":
main()

Commande d'usage

python find_evidence_duplicates.py \
--index "./data/evidence_engine_v2/evidence_index.json"

3) Petite amélioration utile : repérer les pièces sans texte extrait

Tu peux déjà lancer ça sans nouveau script :

python - <<'PY'
import json
from collections import Counter
from pathlib import Path
p = Path("./data/evidence_engine_v2/evidence_index.json")
data = json.loads(p.read_text(encoding="utf-8"))
print("TOTAL:", len(data))
print("\nMEDIA TYPES:")
for k, v in Counter(item["media_type"] for item in data).most_common():
print(f"{k}: {v}")
without_text = [x for x in data if not x.get("text_path")]
print("\nWITHOUT TEXT:", len(without_text))
print("\nFIRST 20 WITHOUT TEXT:")
for item in without_text[:20]:
print(item["evidence_id"], item["media_type"], item["label"])
PY

👉 ça va te montrer tout de suite où il manque encore du travail.

4) Sur les "irrelevant"

Tu as très bien senti le besoin, mais il faut être prudente.

Ce qu'on peut faire

Marquer des fichiers comme :

  • duplicate
  • system
  • administrative
  • weak_context
  • core_candidate
  • to_review

Ce qu'il ne faut pas faire tout de suite

Décider automatiquement que quelque chose est "inutile" et le sortir du dossier.

👉 Donc, pour l'instant :
on tague, on ne supprime pas.

5) Ce qu'on va faire après ces 2 scripts

Une fois que tu auras :

  • transcrit les 2 ou 3 dossiers les plus importants
  • généré le rapport doublons

on passe à :

script suivant

build_evidence_html_report.py

Il générera un index HTML cliquable avec :

  • ID
  • type
  • nom
  • chemin source
  • lien transcription
  • taille
  • date détectée
  • tags

Et ensuite :

script de vues juridiques

qui produira :

  • vue par domaine
  • vue par infraction
  • vue chronologique


🧠 Lecture de ce que tu es en train de faire

Là, très concrètement :

👉 tu es en train de transformer :

  • des audios bruts
    → en preuves textuelles exploitables

👉 et ça change tout :

  • recherche possible
  • citation possible
  • analyse possible
  • comparaison possible

🔥 Et surtout

Tu es en train de créer :

une mémoire judiciaire structurée

Pas juste "des preuves".


Ce que tu es en train de faire là :

👉 ce n'est pas juste technique

👉 tu es en train de créer :

  • une preuve structurée
  • une mémoire vérifiable
  • un système défensif
Share