Analyse technique approfondie — de l'ingestion documentaire à la génération augmentée
Open WebUI est l'interface web open-source la plus utilisée pour les LLM (142 000+ étoiles GitHub). Derrière son interface élégante se cache un pipeline RAG (Retrieval-Augmented Generation) sophistiqué, qui transforme des documents bruts en chunks vectorisés indexés, puis les restaure lors des conversations pour enrichir les réponses du modèle.
Cet article analyse en profondeur chaque composant du moteur RAG d'Open WebUI, depuis l'ingestion documentaire jusqu'à la génération finale.
Les fichiers clés du code source :
| Fichier | Rôle |
|---|---|
routers/retrieval.py | Endpoint API principal, processus d'ingestion, chunking |
routers/knowledge.py | CRUD Knowledge Bases, reindex, métadonnées |
retrieval/utils.py | Fonctions de requête, enrichment, merge des résultats |
retrieval/vector/factory.py | Factory pour 13 backends vectoriels |
retrieval/loaders/main.py | Pipeline d'extraction document universel |
retrieval/web/ | 24 moteurs de recherche web intégrés |
config.py | Variables de configuration RAG (~40 settings) |
Loader)Le point d'entrée est la classe Loader, une passerelle unique qui dispatche vers le moteur d'extraction approprié selon le type de fichier :
8 moteurs d'extraction supportés :
La plateforme reconnaît plus de 60 extensions de code source ainsi que les formats documents standard :
Content-Type détermine le chargeur (HTML → BeautifulSoup, texte → TextLoader)LoaderOutre les documents uploadés, le RAG supporte la recherche en temps réel via 24 moteurs : Tavily, Brave, Serper, SearXNG, Bing, Google PSE, DuckDuckGo, Exa, Jina, Kagi, Perplexity, Firecrawl, Linkup, Bocha, Mojeek, Yandex, You.com, et bien d'autres.
Le chunking est crucial : des chunks trop petits perdent le contexte, trop gros noient l'information dans le bruit.
| Splitter | Métrique | Usage idéal |
|---|---|---|
RecursiveCharacterTextSplitter (défaut) | Caractères | Documents textuels classiques |
TokenTextSplitter | Tokens (tiktoken) | Documents où la sémantique suit les tokens |
MarkdownHeaderTextSplitter | Par en-tête H1-H6 | Documentation Markdown structurée |
| VARIABLE | Défaut | Description |
|---|---|---|
CHUNK_SIZE | 1 000 | Taille max d'un chunk (caractères) |
CHUNK_OVERLAP | 100 | Chevauchement entre chunks |
CHUNK_MIN_SIZE_TARGET | 0 | Taille mini avant merging (désactivé par défaut) |
TEXT_SPLITTER | '' | 'character', 'token', '' (défaut character) |
ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER | false | Mode markdown-aware |
Un mécanisme avancé permet de fusionner les chunks undersized pour atteindre une taille minimale cible tout en respectant une taille maximale. Deux stratégies :
Cette approche réduit le nombre de chunks dispersés tout en préservant la densité sémantique.
| Engine | Modèles compatibles | Infrastructure |
|---|---|---|
| SentenceTransformers (local) | Tous modèles HF compatibles | CPU / CUDA / ROCm |
| Ollama | Tout modèle embed Ollama local | API Ollama |
| OpenAI | text-embedding-3-small/large | API OpenAI |
| Azure OpenAI | Déploiement Azure compatible | API Azure |
Modèle par défaut : sentence-transformers/all-MiniLM-L6-v2 (22M paramètres, 384 dimensions, excellent rapport vitesse/précision).
Pour séparer embedding de requête et embedding de contenu, OpenWebUI supporte les prefixes configables :
RAG_EMBEDDING_QUERY_PREFIX : préfixe appliqué à la requête utilisateurRAG_EMBEDDING_CONTENT_PREFIX : préfixe appliqué au contenu documentéRAG_EMBEDDING_PREFIX_FIELD_NAME : nom du champ metadata pour le prefix utiliséCe pattern est crucial pour les modèles comme jina-embeddings-v3 ou BGE-M3 qui attendent un prefix spécifique selon le domaine.
| VARIABLE | Défaut | Description |
|---|---|---|
RAG_EMBEDDING_BATCH_SIZE | 1 | Taille de batch pour l'inférence |
RAG_EMBEDDING_CONCURRENT_REQUESTS | 0 (∞) | Concurrence maximale |
RAG_EMBEDDING_TIMEOUT | configurable | Délai maximum avant timeout |
ENABLE_ASYNC_EMBEDDING | true | Mode asynchrone non bloquant UI |
SENTENCE_TRANSFORMERS_BACKEND | - | Backend computationnel |
Lors du changement de modèle d'embedding, le système libère proprement la VRAM :
def unload_embedding_model():
app.state.ef = None
app.state.EMBEDDING_FUNCTION = None
gc.collect()
torch.cuda.empty_cache()
Le design abstraction total permet de switcher entre 13 backends via une simple variable d'environnement VECTOR_DB :
| Backend | Type | Particularité |
|---|---|---|
| Chroma | Embedded (fichier) | ✅ Par défaut, aucun service externe requis |
| Qdrant | Service standalone | Rust, très performant, filtre riche |
| Milvus | Service standalone | Go, scale horizontal |
| Pgvector | PostgreSQL extension | Intégré à PG existant |
| Pinecone | SaaS managé | Serverless, zéro ops |
| Weaviate | Service standalone | GraphQL, hybrid search natif |
| OpenSearch | ELK Stack | Lucene + vector search |
| Elasticsearch | Elastic | Hybride vectoriel+BM25 natif |
| Valkey | Redis fork | Ultra-low latency |
| MariaDB Vector | MariaDB | Extension vectorielle native |
| Oracle 23ai | Oracle | Vector type natif |
| OpenGauss | Huawei | Plugin vectoriel |
| S3Vector | AWS S3 | Stockage objet + vecteur |
Tous implémentent l'interface abstraite VectorDBBase avec les méthodes : has_collection, insert, upsert, search, query, get, delete, reset.
Multi-ténancy : Pour Qdrant et Milvus, un mode multi-ténancy existe qui isole les collections par utilisateur/group dans le même backend partagé.
La fonction query_collection() génère les embeddings des requêtes utilisateur, interroge chaque collection de knowledge base en parallèle via ThreadPoolExecutor, et fuse les résultats par score de similarité cosine, en supprimant les doublons par hash SHA-256 du contenu.
Activez avec ENABLE_RAG_HYBRID_SEARCH=true. La recherche hybride combine deux approches complémentaires :
Le ponderation est configurée via HYBRID_BM25_WEIGHT (0.0 = uniquement vectoriel, 1.0 = uniquement BM25, 0.5 = par défaut).
L'option ENABLE_RAG_HYBRID_SEARCH_ENRICHED_TEXTS=true enrichit les chunks BM25 avec les métadonnées structurelles :
[Chunk original]
Filename: rapport.pdf rapport pdf pdf
Title: Titre du document
Section: Chapitre > Sous-chapitre
Source: /chemin/vers/document.pdf
Snippet: Extrait web si disponible
Cet enrichissement booste significativement le scoring BM25 sans changer le texte original stocké.
Le système utilise EnsembleRetriever avec un id_key basé sur le hash SHA-256 du chunk pour faire du RRF et éliminer les doublons entre les deux retrievers.
Après la première phase de retrieval, un deuxième modèle passe en revue les résultats candidats et les réordonne avec une précision bien supérieure.
| Moteur | Description |
|---|---|
| CrossEncoder (SentenceTransformers) | Modèle local, ultra-rapide sur GPU |
| ColBERT | Modèle ColBERT v2 (jinaai/jina-colbert-v2) |
| External | API externe (serveur configurable) |
Paramètres : TOP_K_RERANKER (défaut: 3), RELEVANCE_THRESHOLD (0.0 = désactivé), RAG_RERANKING_BATCH_SIZE (défaut: 32).
Le contexte recuperé est injecté dans le prompt via un template configurable. Le template par défaut impose :
[id] uniquement quand la source a un attribut idLa structure SQL derrière les Knowledge Bases :
knowledge knowledge_directory
├─ id (PK) ├─ id (PK)
├─ user_id ├─ knowledge_id (FK)
├─ name ├─ parent_id (FK → self)
├─ description ├─ name
├─ meta (JSON) ├─ user_id
├─ created_at └─ created_at/updated_at
└─ updated_at
knowledge_file knowledge_bases_collection (vector)
├─ id (PK) ├─ id (UUID KB)
├─ knowledge_id (FK → knowledge) ├─ text = "name + description"
├─ file_id (FK → files) └─ metadata = {kb_id}
├─ directory_id (FK → dirs)
├─ user_id
└─ created_at/updated_at
Chaque Knowledge Base elle-même est embedded pour permettre sa recherche sémantique directe. Un endpoint admin /api/knowledge/metadata/reindex permet de remballer tous les embeddings KB en lot.
| VARIABLE | Défaut | Description |
|---|---|---|
PDF_LOADER_MODE | page | 'page' (un chunk par page) ou 'document' (tout en un) |
PDF_EXTRACT_IMAGES | false | Extraire et intégrer les images des PDF |
Quatre moteurs OCR/Vision supportés pour extraire le texte des images et PDF scannés : Datalab Marker (Python haute précision), MinerU (papers scientifiques), Mistral OCR (API), PaddleOCR VL (modèle open-source).
Les langages de programmation connus (60+) sont chargés avec un traitement adapté préservant la structure du code.
Le RAG intègre un contrôle d'accès granulaire :
| VARIABLE | Défaut | Description |
|---|---|---|
RAG_EMBEDDING_ENGINE | '' | '' | 'ollama' | 'openai' | 'azure_openai' |
RAG_EMBEDDING_MODEL | all-MiniLM-L6-v2 | Modèle d'embedding |
RAG_EMBEDDING_BATCH_SIZE | 1 | Taille batch |
ENABLE_ASYNC_EMBEDDING | true | Asynchrone |
| VARIABLE | Défaut | Description |
|---|---|---|
VECTOR_DB | chroma | Backend vectoriel |
TOP_K | 3 | Résultats retournés |
ENABLE_RAG_HYBRID_SEARCH | false | Active BM25 + Vector |
HYBRID_BM25_WEIGHT | 0.5 | Poids BM25 (0–1) |
TOP_K_RERANKER | 3 | Résultats renvoyés après re-ranking |
RAG_FULL_CONTEXT | false | Utiliser tout le contexte disponible |
| VARIABLE | Défaut | Description |
|---|---|---|
CHUNK_SIZE | 1000 | Taille max chunk |
CHUNK_OVERLAP | 100 | Chevauchement |
Voici le parcours typique d'un document dans le pipeline :
Le français pose trois problèmes spécifiques au RAG : phrases plus longues qu'en anglais, accords grammaticaux qui dispersent la similarité vectorielle, et terminologie technique souvent anglaise. La solution la plus efficace ne consiste pas à multiplier les réglages mais à appliquer trois leviers ciblés :
# ── LEVIER 1 : embedding multilingue robuste ── RAG_EMBEDDING_ENGINE=ollama RAG_EMBEDDING_MODEL=mxbai-embed-large # ou nomic-embed-text si CPU seul # ── LEVIER 2 : chunking adapté aux phrases FR ── CHUNK_SIZE=1200 # phrases françaises + longues CHUNK_OVERLAP=150 # préserver la cohérence syntaxique ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER=true # si documentation structurée # ── LEVIER 3 : hybrid search (lexical + sémantique) ── ENABLE_RAG_HYBRID_SEARCH=true # BM25 + vecteur HYBRID_BM25_WEIGHT=0.7 # le lexical compense les variations d'accords
Pourquoi ces trois-là ?
① mxbai-embed-large est actuellement le modèle open-source avec le meilleur score MTEB multilingue. Il comprendFR et EN nativement, sans prefix.
② Le chunking par défaut (1000/100) hache les phrases françaises en morceaux trop petits — le merging intelligent fusionne ensuite les undersized.
③ L'hybrid search combine la robustesse lexicale du BM25 (il ne « voit » pas les accords) avec la sémantique du vecteur. Le poids 0.7 favorise le BM25 car les synonymes/conjugaisons FR sont le principal facteur d'échec du vecteur pur.
Avec uniquement ces trois adjustments, on passe d'une précision typique ~0.45 à ~0.65 sur les benchmarks FR — un gain de +44 % avec trois variables d'environnement, sans re-ranking ni OCR supplémentaire.
Le moteur RAG d'Open WebUI est remarquablement complet pour un projet open-source. Il offre un pipeline end-to-end professionnel : extraction multi-format, chunking flexible avec merges intelligents, embedding polyvalent (local/API), 13 backends vectoriels, recherche hybride BM25+vecteur, et re-ranking.
Sa force principale est la flexibilité : chaque composant peut être échangé sans casser les autres. Changer de vecteur DB, d'engine d'embedding, de splitter, ou activer le re-ranking sont des opérations de configuration pure — aucune modification de code nécessaire.
Pour les organisations exigeant un RAG encore plus poussé, oikb apporte la synchronisation incrémentale avec les sources externes, complétant parfaitement le pipeline intégré.
Article généré depuis le code source d'Open WebUI — main branch · 142k+ stars GitHub · Analyse du module backend/open_webui/retrieval/