Interrogez vos manuels
Moteur de recherche hybride et contextuel pour la conformité
Vos équipes posent une question sur vos procédures ou vos règlements et reçoivent une réponse qui cite le passage exact et la page d'où il vient. Quand les documents ne le disent pas, la réponse le dit.
Le problème réglé
Le RAG naïf découpe le texte au nombre de caractères et classe par similarité cosinus seulement. Il brouille les identifiants exacts comme 21 CFR Part 11 ou SOP-QA-017, coupe une clause du document dont elle provient et laisse le modèle libre de mal recopier un chiffre ou de citer une page qu'il n'a jamais vue. En contexte de conformité, ce n'est pas un problème de qualité, c'est un risque juridique.
Ce qui a été mesuré
- Mesuré
- Cible
6 / 6
réponses à citations vérifiées sur les questions enregistrées
mesuré1 / 1
question hors corpus déclinée, sans deviner
mesuré441 Mo
de mémoire au pic, deux modèles locaux chargés, instance de 512 Mo
mesuré0,00 $
de coût marginal par question sur la pile gratuite
mesuré
Comment ça marchepour les équipes techniques
En termes techniques
Un moteur de recherche pour procédures opératoires normalisées et manuels réglementaires, qui ne répond qu'en citant la source. La recherche vectorielle et la recherche par mots-clés s'exécutent dans une seule requête PostgreSQL et sont fusionnées, un cross-encoder retient les trois meilleurs passages, et chaque citation renvoyée par le modèle est vérifiée contre le document stocké avant que quiconque la voie. Quand les documents ne le disent pas, la réponse le dit. La démo publique fonctionne à coût nul sur des modèles gratuits.
Architecture
PyMuPDF extrait les blocs de mise en page de chaque page, puis un segmenteur structurel les regroupe le long des titres en segments de 400 jetons avec un chevauchement de 60 jetons. Suivant le patron Contextual Retrieval d'Anthropic, chaque segment est stocké avec un préambule de mise en situation, et son embedding comme sa colonne tsvector générée sont construits à partir de ce texte. Une seule requête SQL exécute une recherche cosinus pgvector sur un index HNSW (m=16, ef_construction=64) et une recherche plein texte sur un index GIN, puis fusionne les deux listes par Reciprocal Rank Fusion (k=60) dans une CTE. Un cross-encoder lit la question avec chacun des candidats fusionnés, jusqu'à 50, et en garde 3. Le modèle doit renvoyer un JSON strict contenant une réponse, un score de confiance et des citations textuelles, ou un verdict explicite de contexte insuffisant. Le serveur réancre chaque citation dans le texte stocké du segment, écarte ce qui ne correspond pas, tire les titres et les numéros de page de la base de données et refuse de livrer une réponse dont aucune citation ne survit. La génération passe par une chaîne ordonnée de modèles OpenRouter gratuits avec repli, et Claude, les embeddings OpenAI et Cohere Rerank v3.5 s'activent par simple paramètre.
Pile technique
- Python 3.12
- FastAPI
- Pydantic v2
- PostgreSQL
- pgvector (HNSW)
- tsvector (GIN)
- Reciprocal Rank Fusion
- fastembed (ONNX)
- OpenRouter
- PyMuPDF
- Render
Éléments de preuve
- Sept exécutions réelles enregistrées le 14 octobre 2026 et publiées telles que le service les a renvoyées. Elles montrent que le pipeline fonctionne, pas à quelle fréquence il a raison, et un test retrouve chaque citation enregistrée mot pour mot dans le PDF d'exemple, à la page citée.
- Une citation ne peut pas être inventée. Elle est comparée au segment stocké, jamais au préambule généré, après normalisation Unicode et typographique, et le modèle ne fournit jamais de numéro de page.
- La suite hermétique n'a besoin ni de réseau, ni de base de données, ni de clé API, et s'exécute avec ruff à chaque push. Des tests optionnels sur un vrai PostgreSQL vérifient que la fusion SQL concorde avec son jumeau Python à 1e-9 près et qu'EXPLAIN utilise l'index HNSW.
- Tient dans une instance gratuite de 512 Mo. Trois changements de mémoire, dont la désactivation de l'arène mémoire d'ONNX Runtime, ont fait passer le service de 856 Mo à 441 Mo au pic, les deux modèles locaux chargés.
- Démo verrouillée par conception pour l'Internet public. Le téléversement exige la clé de l'opérateur, les questions sont plafonnées par visiteur et par jour, et l'allocation gratuite du jour est lue chez OpenRouter même, si bien qu'un redémarrage ne peut pas l'effacer.
- Sur les sept exécutions, la médiane est de 1,4 s pour la recherche (embedding, SQL, reclassement) et de 6,8 s de bout en bout, sur des modèles gratuits. La requête SQL elle-même prend environ 170 ms, et la génération représente l'essentiel du reste.
Pipeline de requête
- 01
Ingestion
Blocs PyMuPDF, segments de 400 jetons selon les titres, préambule contextuel
- 02
Indexation
pgvector HNSW et index GIN sur un tsvector généré, une seule table
- 03
Recherche
Cosinus dense et plein texte dans une seule requête SQL
- 04
Fusion
CTE de Reciprocal Rank Fusion, k=60, jusqu'à 50 candidats
- 05
Reclassement
Cross-encoder qui lit question et passage ensemble, en garde 3
- 06
Vérification
Réponse JSON stricte, citations réancrées dans le texte stocké, pages tirées de la base