Ingénierie · transparente
Méthodologie
Comment OpenCaseLaw trouve, pondère et vérifie les décisions judiciaires suisses — de bout en bout, chaque pondération et chaque seuil étant divulgués. Cette page est la référence faisant foi ; ce que vous lisez ailleurs devrait concorder avec ce qui est consigné ici — à défaut, c'est le code qui prévaut.
1. Corpus et mise à jour
Le corpus compte 1’050’000+ décisions judiciaires suisses publiées, issues de 118 tribunaux, en trois langues (DE 499’642 ; FR 459’825 ; IT 90’776), couvrant la période 1875 – 2026. Huit juridictions fédérales (BGer 192’049 ; BVGer 108’135 ; BGE 50’459 ; ainsi que BStGer, BPatGer, MKG et autres), 26 juridictions cantonales ainsi que la CEDH-Suisse (~2’800 décisions issues de traductions ATF, HUDOC, Chambre/Grande Chambre/Comité).
Chaque décision est obtenue de sa source primaire — lorsque disponible, du propre portail de publication du tribunal (les 26 cantons sont récupérés directement via LexWork / SIL / ZH OpenData / TI RL, avec un repli PDF LexFind qui comble les actes manquants dans 4 cantons), et pour les mises à jour fédérales du back-end de recherche officiel du TF. 40 des 51 shards d'entscheidsuche.ch sont reproduits par nos propres scrapers directs ; les données d'entscheidsuche ne l'emportent à la déduplication que là où nous n'avons pas d'équivalent en propre et en temps réel. Les sept shards exclusifs à entscheidsuche restants (≈142’000 enregistrements : vd_findinfo, vd_omni, ch_vb, sg_gerichte, be_bvd, be_weitere, be_steuerrekurs) sont des archives historiques figées — leurs portails en amont ont été désactivés, remplacés ou temporairement déconnectés. Nous relisons entscheidsuche chaque semaine comme garde-fou d'exhaustivité ; les six derniers passages hebdomadaires n'ont produit aucune nouvelle décision. Voir Couverture → archives complémentaires pour le détail complet. Le pipeline se met à jour selon trois calendriers :
- Toutes les 15 minutes les jours ouvrables de 05:00 à 16:00 UTC — le scrutateur BGer publie les nouvelles décisions fédérales de manière incrémentale dans la base de données en direct, quelques minutes après leur publication par le tribunal.
- Quotidiennement à 01:00 UTC — chaque scraper cantonal actif s'exécute ; une politique de défaillance souple est liée aux 5 scrapers fédéraux critiques et à un seuil de taux d'erreur de 15 % sur les autres.
- Quotidiennement à 03:30 UTC — reconstruction complète du FTS5, reconstruction du graphe de citations, contrôle qualité, mise à jour des statistiques, bascule atomique sans interruption de service. Les workers continuent de servir la base existante jusqu'à ce que la nouvelle ait passé le contrôle d'intégrité.
Le corpus complet est également publié au format Parquet sur Hugging Face (voilaj/swiss-caselaw) sous CC0 ; le delta quotidien est ajouté automatiquement.
2. Index plein texte
SQLite FTS5 avec le tokenizer unicode61 remove_diacritics 2. La table des décisions alimente l'index via ces colonnes, avec des pondérations BM25 par colonne calibrées empiriquement contre un golden set de 100 requêtes :
| Colonne | Pondération BM25 | Justification |
|---|---|---|
| title | 6.0 | Signal le plus fort — concis, délibéré. |
| regeste | 5.5 | Résumé thématique du tribunal lui-même. |
| docket_number | 2.0 | Pour la récupération exacte par numéro de dossier. |
| full_text | 1.2 | Long, bruité — ancre, pas moteur. |
| court / canton / language / decision_id | 0.8 | Métadonnées, pas de contenu. |
L'index est reconstruit de manière atomique : build_fts5.py écrit dans decisions.db.tmp puis remplace le fichier via os.replace(). Les workers utilisant l'URI SQLite ?immutable=1 conservent leurs descripteurs de fichier ouverts sur l'ancien inode jusqu'à ce qu'ils se reconnectent — la bascule reste invisible pour les lecteurs actifs.
build_fts5.py · pondérations BM25 configurées dans mcp_server.py aux alentours des lignes 431–442 · tokenizer diacritique reproduit dans decision_structure.db pour la recherche par paragraphe.
3. Compréhension de la requête
Une requête en langage naturel n'atteint jamais FTS5 telle quelle. Elle passe d'abord par un assainissement (_sanitize_fts5) : les apostrophes, tirets et points sans caractère de mot sont réduits à des espaces ; les tokens réservés (AND, OR, NOT, NEAR) ne sont conservés que s'ils ont des opérandes des deux côtés ; le seul token juridique suisse « OR » (abréviation du droit des obligations) est toujours forcé entre guillemets, faute de quoi il serait interprété comme un opérateur booléen.
En parallèle, la requête est transmise à Claude Haiku 4.5 pour une analyse structurée de 2 secondes : références légales, termes doctrinaux en DE/FR/IT, mentions d'ATF faisant autorité, synonymes et domaine juridique sont extraits sous forme de JSON déterministe. La sortie est mise en cache selon la requête en minuscules pour la durée de la session.
Trois normalisations rendent le vocabulaire juridique suisse interrogeable à travers ses variantes stylistiques, orthographiques et translinguistiques :
- Tokenisation insensible aux diacritiques. Le tokenizer FTS5
unicode61 remove_diacritics 2supprime les diacritiques côté index comme côté requête — ainsi « Prüfung », « PRÜFUNG » et une requête sur « Prufung » atteignent toutes la même liste de résultats. - Fusion des graphies d'umlaut. La graphie sans diacritique
ae/oe/ue(répandue dans les arrêts plus anciens et dans tout environnement pré-Unicode) est réduite àa/o/u, que le tokenizer unifie ensuite avec lesä/ö/üdépouillés de leurs diacritiques. Ainsi « Pruefung » trouve aussi « Prüfung ». - Expansion synonymique par LLM. L'analyse structurée de Claude Haiku génère 2 à 4 termes juridiques alternatifs en DE/FR/IT par requête, à la volée — non à partir d'une table statique. Ainsi « qualité pour recourir » est reliée à « Beschwerdebefugnis » / « Beschwerdelegitimation » / « legittimazione », même si votre requête n'en contenait qu'un.
4. Récupération et fusion
La requête se déploie en 10 à 12 stratégies qui s'exécutent comme des requêtes FTS5/graphe indépendantes. Chaque stratégie porte une pondération ; le résultat de rang 1 d'une stratégie et celui d'une autre sont fusionnés au moyen de la Reciprocal Rank Fusion (constante de rang RRF = 60).
| Stratégie | Pondération | Effet |
|---|---|---|
| nl_and | 1.8 | AND en langage naturel sur les termes. |
| raw | 1.5 | Requête mot pour mot. |
| regeste_focus | 1.4 | Limité à la colonne du regeste. |
| nl_or | 1.2 | Repli OR (arrêt précoce attentif au coût). |
| structured_doctrine | 1.1–3.5 | Termes doctrinaux issus de l'analyse Haiku. |
| quoted_explicit | 1.1 | Correspondance de phrase si des guillemets sont détectés. |
| nl_or_expanded | 1.0 | OR + expansion synonyme / umlaut / composé. |
| title_focus | 0.95 | Correspondance limitée à la colonne du titre. |
| doctrine_regeste / doctrine_title | 2.5 / 1.6 | Stratégies de traduction de termes. |
Au-delà de FTS5, le même pool RRF reçoit également :
- Candidats issus du graphe législatif — décisions liées aux articles de loi extraits de la requête, injectées comme classement parallèle.
- Recherches BGE directes — si la requête (ou son analyse) nomme une référence ATF précise, cette décision est épinglée en tête.
- Correspondances de numéro de dossier — les chaînes telles que
6B_1234/2025sont dirigées vers une recherche exacte de numéro de dossier, ce qui court-circuite l'essentiel du pipeline.
Le pool de candidats fusionné est dimensionné dynamiquement (par défaut ~300–400 pour une requête top-50) et plafonné à 2’500 lignes avant le début de la repondération.
5. Repondération
Chaque candidat reçoit un vecteur de signaux ; le score final est une combinaison linéaire calibrée contre le golden set. Pondérations des signaux (_rerank_rows) :
| Signal | Pondération | Plafonds / remarques |
|---|---|---|
| Score RRF | 32.0 | Agrégat de tous les rangs de stratégie. |
| Numéro de dossier exact / partiel | 6.0 / 2.0 | Correspondance au niveau de la chaîne du numéro de dossier. |
| Couverture du titre | 3.0 | Proportion des tokens de la requête dans le titre. |
| Couverture du regeste | 3.0 | Idem, contre le regeste. |
| Mentions de lois | 3.5 · 0.5 · 2.0 | Liens de décision à article. |
| Correspondances de citation | 2.4 · 0.30 · 1.2 | Preuves de citation inter-résultats. |
| Autorité (citations entrantes) | 0.03 · 1.0 | Pourquoi un arrêt de principe remonte. |
| Concordance linguistique | +2.0 | Lorsque la langue du résultat correspond à celle de la requête. |
| Couverture étendue | 1.5 / 0.8 | Crédit pour les correspondances synonyme + composé. |
| Heuristiques de domaine juridictionnel | ±0.2 – +1.7 | Pondération asile-BVGer + cour suprême-BGer en cas d'intention concordante. |
Après le scoreur linéaire, une passe de repondération par LLM est déclenchée si nécessaire :
- Modèle : Claude Haiku 4.5 ; Top-N : 15 ; délai : 3 s ; pondération : 3.0 avec décroissance linéaire
w × max(0, 1 − rang/15). - Un seuil de confiance évite entièrement l'appel lorsque le score lexical top-1 atteint déjà ≥ 2× le score top-2 (la réponse est sans ambiguïté ; la repondération n'est que du coût).
- La passe est en outre ignorée pour les requêtes de numéro de dossier (une correspondance exacte n'a pas besoin de LLM).
6. Références ponctuelles en service · mai 2026
Chacun des cinq meilleurs résultats de recherche et chacun des trois meilleurs résultats d'arrêts de principe porte un champ pinpoint qui désigne le considérant le plus pertinent pour la requête (paragraphe contenant la décision juridique). Le résolveur s'appuie sur un index FTS5 de paragraphes par décision dans decision_structure.db (≈8,8 mio. de paragraphes répartis sur 807’000 décisions). La conception à deux étapes :
- Passe par phrase. La requête est exécutée comme phrase FTS5 exacte — haute précision lorsque la formulation de l'utilisateur correspond à celle du tribunal.
- Passe OR sac-de-mots. Si la phrase ne donne rien, les mêmes tokens sont lancés en requête OR, pour une récupération plus large.
Un scoreur de confiance (_score_pinpoint_confidence) combine trois signaux indépendants pour qualifier le résultat :
- Écart BM25. Score de rang 1 sur celui de rang 2 ; un ratio > 1,5 donne « élevé », > 1,2 donne « moyen ».
- Force absolue. Pour les correspondances à ligne unique sans rang 2 de comparaison : |BM25| absolu > 2,0 pour « élevé », > 1,0 pour « moyen ». Une version antérieure de cette branche utilisait pour les correspondances à ligne unique une valeur sentinelle de 999,0, qui promouvait silencieusement les correspondances ténues à « élevé » ; c'était le bug de fausse confiance corrigé en mai 2026.
- Couverture de tokens. Tokens uniques de la requête (> 2 caractères, ~70 mots vides génériques du discours juridique suisse étant filtrés) apparaissant dans le paragraphe trouvé. Les requêtes multi-tokens avec une couverture < 50 % sont entièrement supprimées ; < 70 % est plafonné à « moyen ».
Secours sémantique infrastructure déployée · corpus ~33 % encodé intervient lorsque la passe lexicale ne donne rien. La requête est encodée avec sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 (117 mio. de paramètres, vecteurs normalisés à 384 dimensions, multilingue DE/FR/IT/RM plus 47 autres) ; la similarité cosinus est calculée sur les < 300 plongements de paragraphes de la décision ; un résultat apparaît en « élevé » si le cosinus ≥ 0,70, en « moyen » à ≥ 0,55, sinon aucun. Un mode hybride, activé une fois l'encodage achevé, fait tourner les deux signaux et traite la concordance sur le même considérant comme preuve inter-signaux — la confiance est portée à « élevé », source: "hybrid_agreement".
L'URL de pinpoint est construite de sorte que le navigateur défile automatiquement jusqu'au paragraphe trouvé et le met en évidence : ?highlight=<phrase mot pour mot>&e=2.3#e-2-3. Le paramètre de requête pilote une mise en évidence côté serveur ; le fragment de hachage déclenche le défilement automatique natif du navigateur.
7. Graphe de citations
Le texte de chaque décision est analysé à la recherche de citations d'autres décisions (références ATF, numéros de dossier BGer, références BVGer). La couche brute contient 8,65 mio. d'arêtes ; le résolveur en élève 8,09 mio. (92,9 %) vers des identifiants de décision canoniques. Les 7 % restants sont des décisions réelles que nous ne détenons pas encore (ATF anciens d'avant 1875, publications judiciaires retirées, références altérées par l'OCR).
La résolution s'effectue en quatre passes en cascade :
- Jointure standard par numéro de dossier. Numéro de dossier normalisé de la citation = numéro de dossier normalisé de la cible. Couvre plus de 80 % des arêtes.
- Préfixe BGE bidirectionnel. Trouve les correspondances, que la citation contienne ou non le préfixe
BGE, la cible pouvant être stockée sous l'une ou l'autre forme. Cette seule correction de mars 2026 a presque triplé la résolution des BGE. - Numéros BGE nus. Les cibles correspondant à
volume division pagesont traitées comme des références BGE nues et préfixées deBGE. - Résolution de pin-cite. Si une citation contient un numéro de page absent de nos cibles (p. ex.
BGE 125 V 352, où la première page est 351), nous cherchons la plus grande première page ≤ 352 au sein du même (volume, division) et à moins de 30 pages. La confiance est abaissée de 0,10 pour refléter l'inférence.
Autorité la plus citée (calculée en direct depuis reference_graph.db le 2026-05-11) : BGE 125 V 351 avec 85’108 citations entrantes, suivi de BGE 134 V 231 et BGE 122 V 157, chacun de l'ordre de dizaines de milliers. Ces décomptes reviennent comme signal d'autorité dans la repondération de recherche (pondération 0,03 par citation, plafonnée à 1,0) — d'où la remontée des arrêts de principe classiques, même lorsque leurs correspondances linguistiques ne sont pas plus fortes que celles des autres candidats.
8. Assurance qualité
Chaque publication nocturne est protégée par un cadre d'AQ à 4 couches. L4 (le contrôle de publication, qui exécute le sous-ensemble CRITIQUE de L1) est le verrou dur : en cas d'échec, la bascule est refusée et les utilisateurs conservent le corpus de la veille jusqu'à ce que le problème soit examiné. Les autres couches sont des garde-fous continus — L2 s'exécute à chaque commit en CI, L3 toutes les 5 minutes sur le serveur en direct.
- L1 — Contrôles de jeu de données (63 sur 20 modules). Dérive par tribunal, détection de doublons, détection de texte court / OCR, taux de résolution du graphe de citations, couverture des liens de loi, plausibilité date / numéro de dossier, décompte des champs manquants ainsi que vérification LLM par échantillonnage sur une sélection tournante.
- L2 — suite pytest (actuellement 516 réussis). Les tests unitaires couvrent chaque primitive d'analyse, de déduplication et de classement ; les nouvelles régressions sont ajoutées comme nouveaux tests selon le schéma « incident → test de régression ».
- L3 — test de fumée (toutes les 5 minutes). Sonde de santé de production :
/health, une page de décision-ancre et le pipeline d'export PDF. Trois échecs consécutifs escaladent en une alerte INVESTIGATE. - L4 — contrôle de publication. Parmi les contrôles L1, ceux marqués CRITIQUES doivent passer avant que le nouveau corpus soit commité et poussé ; si l'un échoue, la publication refuse la bascule et les données de la veille restent en ligne. Le tableau de bord à /quality.html montre le dernier statut et l'historique de chaque contrôle.
Au-delà de l'AQ, les workflows LLM citant des décisions passent par un audit final à cinq voies (attest_response) qui intercepte quatre classes d'hallucination — citation inventée, extrait inventé, texte de loi inventé, date inventée — ainsi qu'un cinquième juge d'ancrage facultatif, qui vérifie si l'affirmation que le LLM a attribuée à un paragraphe cité est effectivement étayée par le texte de ce paragraphe.
9. Testé et rejeté
Les décisions techniques inspirent davantage confiance lorsque les impasses sont visibles. Les techniques suivantes ont été mesurées sur le même golden set que le reste et n'ont pas mérité leur place :
- Repondération par cross-encoder (
cross-encoder/mmarco-mMiniLMv2-L12-H384-v1comme substitut actuel) rejeté Les cross-encoders multilingues entraînés sur du QA web générique ne se transféraient pas proprement au vocabulaire juridique suisse sur le golden set — ils nuisaient au MRR au lieu d'aider. Le helper reste dans le code derrière_apply_cross_encoder_boostspour de futures expériences de fine-tuning, mais n'est pas appelé en production. - Vecteurs denses BGE-M3 préfabriqués rejeté Le corpus encodé, le RRF vectoriel ajouté comme quatrième stratégie. Aucune amélioration par rapport à BM25 + RRF + Haiku, à quelque pondération vectorielle que ce soit. La
vectors.dba été supprimée en mars 2026. Les plongements sémantiques par paragraphe livrés cette semaine sont une intervention distincte et plus étroite — ils ne scorent qu'au sein des paragraphes d'une seule décision, là où BM25 a des limites bien connues. - Top-N Haiku plus grand rejeté Repondérer le top-30, le top-50 au lieu du top-15 n'a apporté aucun gain de MRR et une hausse linéaire des coûts. Le top-15 est le point optimal empirique pour la repondération pilotée par la confiance.
- Pool de candidats plus grand rejeté Agrandir le pool au-delà de ~400 candidats (pour les requêtes standard) n'a pas déplacé le rappel de manière notable ; la combinaison FTS5 + RRF fait déjà remonter les bons candidats dès les premières centaines de résultats. La limite de 2’500 demeure comme plafond de sécurité.
10. Accès libre
Le corpus est CC0 (dédicace au domaine public). Le code est sous MIT, hébergé sur github.com/jonashertner/opencaselaw — chaque pondération, chaque seuil et chaque heuristique de cette page est auditable dans le code source. Pas de comptes, pas de cookies ; ce que le serveur enregistre, y compris les requêtes de recherche, et pour combien de temps, est documenté dans la politique de confidentialité : /datenschutz/.
Accès programmatique :
- API REST. mcp.opencaselaw.ch/docs — OpenAPI 3.0.3 avec 47 points de terminaison (recherche, récupération, arrêts de principe, tendances, graphe de citations, actes législatifs, commentaires, Botschaft, exports, références ponctuelles, attestation).
- Serveur MCP. mcp.opencaselaw.ch/sse — transport Server-Sent Events, exposant 38 outils. Connecté par Claude, ChatGPT (OpenAI MCP), Cursor, Microsoft Copilot Studio, Perplexity et d'autres.
- Jeu de données Hugging Face. voilaj/swiss-caselaw — shards Parquet, publiés quotidiennement en delta.
- Add-in Word. opencaselaw.ch/word/ — add-in Office pour Microsoft Word ; expose le même stack recherche + pinpoint à l'intérieur du document.
Cette page reflète le système en direct. Si vous repérez un écart entre ce qui est décrit ici et ce que dit le code source, c'est le code source qui prévaut — ouvrez un ticket et nous corrigerons l'un des deux.