RAG pilote pour classe de 3 ème > liste initiale des matières × niveaux × tiers (10–20 shards pilotes).
Liste de 18 shards pilotes pour la classe de 3e (collège)
oici une liste de 18 shards pilotes pour la classe de 3e (collège), structurés par matière × chapitre-focus × tier. J’ai pris 6 matières « cœur » et 3 tiers (Top10, Mid50-80, Bottom10). Les IDs suivent la convention :
RAG:FR:Troisieme:{Matiere}:{Chapitre}:{Tier}.
| Shard ID | Matière | Niveau | Chapitre / Focus | Tier | Objectifs pédagogiques (résumé) | |-------------------------------------------------------------|------------------|------------|-------------------------------------------------|-------------|--------------------------------------------------------------------------------------------------| | RAG:FR:Troisieme:Maths:Equations_2ndDeg:Top10 | Mathématiques | 3e | Équations / factorisation (2nd degré simple) | Top10 | Résoudre vite, factoriser, liens avec paraboles, exercices défis. | | RAG:FR:Troisieme:Maths:Equations_2ndDeg:Mid50-80 | Mathématiques | 3e | Équations / factorisation (2nd degré simple) | Mid50-80 | Méthode pas-à-pas, erreurs fréquentes, exercices gradués. | | RAG:FR:Troisieme:Maths:Equations_2ndDeg:Bottom10 | Mathématiques | 3e | Équations / factorisation (2nd degré simple) | Bottom10 | Rappels vocab., mini-étapes, modèles d’exercice guidés. | | RAG:FR:Troisieme:Maths:Stats_Probas:Top10 | Mathématiques | 3e | Statistiques & probabilités | Top10 | Loi de probas composées, pièges, analyse critique de données. | | RAG:FR:Troisieme:Maths:Stats_Probas:Mid50-80 | Mathématiques | 3e | Statistiques & probabilités | Mid50-80 | Fréquences, médiane, moyenne, arbres simples. | | RAG:FR:Troisieme:Maths:Stats_Probas:Bottom10 | Mathématiques | 3e | Statistiques & probabilités | Bottom10 | Manipulations concrètes (tableaux), exemples du quotidien. | | RAG:FR:Troisieme:Francais:Argumentation_Brevet:Top10 | Français | 3e | Argumentation (écrit du brevet) | Top10 | Thèse/antithèse, connecteurs avancés, style, enrichissement lexical. | | RAG:FR:Troisieme:Francais:Argumentation_Brevet:Mid50-80 | Français | 3e | Argumentation (écrit du brevet) | Mid50-80 | Plan simple, connecteurs de base, relecture ciblée. | | RAG:FR:Troisieme:Francais:Argumentation_Brevet:Bottom10 | Français | 3e | Argumentation (écrit du brevet) | Bottom10 | Modèles-guides phrase par phrase, exemples concrets, orthographe clé. | | RAG:FR:Troisieme:Histoire:Seconde_Guerre_Mondiale:Top10 | Histoire-Géo/EMC | 3e | Seconde Guerre mondiale | Top10 | Problématiser, replacer acteurs/chronologie, documents croisés. | | RAG:FR:Troisieme:Histoire:Seconde_Guerre_Mondiale:Mid50-80 | Histoire-Géo/EMC | 3e | Seconde Guerre mondiale | Mid50-80 | Frises, causes/conséquences, étude documentaire guidée. | | RAG:FR:Troisieme:Histoire:Seconde_Guerre_Mondiale:Bottom10 | Histoire-Géo/EMC | 3e | Seconde Guerre mondiale | Bottom10 | Repères essentiels (dates/lieux), cartes annotées, QCM pas-à-pas. | | RAG:FR:Troisieme:Physique:Electricite_Loi_dOhm:Top10 | Physique-Chimie | 3e | Électricité (U, I, R) & loi d’Ohm | Top10 | Problèmes multi-étapes, équivalents, interprétation de graphes. | | RAG:FR:Troisieme:Physique:Electricite_Loi_dOhm:Mid50-80 | Physique-Chimie | 3e | Électricité (U, I, R) & loi d’Ohm | Mid50-80 | Méthode calculs de base, montages simples, unités. | | RAG:FR:Troisieme:Physique:Electricite_Loi_dOhm:Bottom10 | Physique-Chimie | 3e | Électricité (U, I, R) & loi d’Ohm | Bottom10 | Pas-à-pas avec schémas, manip’ concrètes, conversion d’unités. | | RAG:FR:Troisieme:SVT:Genetique_Information_Hereditaire:Top10| SVT | 3e | Génétique : ADN, gènes, allèles | Top10 | Croisements complexes, lecture d’arbres, exceptions usuelles. | | RAG:FR:Troisieme:SVT:Genetique_Information_Hereditaire:Mid50-80| SVT | 3e | Génétique : ADN, gènes, allèles | Mid50-80 | Vocabulaire clé, exercices standard, phénotype/génotype. | | RAG:FR:Troisieme:SVT:Genetique_Information_Hereditaire:Bottom10| SVT | 3e | Génétique : ADN, gènes, allèles | Bottom10 | Analogies simples (recettes), schémas légendés pas-à-pas. |
Remarques :
- J’ai privilégié des chapitres “pivot” 3e pour maximiser l’impact sur le brevet (maths, français, histoire), et les fondamentaux en physique et SVT.
- On pourra ajouter rapidement Anglais B1 (narration au passé / expression d’opinion) et Géographie (aménagement du territoire) si vous souhaitez passer à 24–27 shards.
Prochaines étapes :
- le schéma JSON des métadonnées par shard,
- les prompts d’assistant adaptés aux 3 tiers,
- un script d’ingestion (OpenAI hébergé ou VPS/pgvector) prêt à coller.
1- le schéma JSON des métadonnées par shard,
Voici un schéma JSON clair et prêt à l’emploi pour les métadonnées d’un shard (et de ses chunks) — compatible avec un usage « filtres plats » (File Search/Datasets) ou une base vectorielle (pgvector/Qdrant).
Il comporte deux niveaux : ShardHeader (niveau collection) et ChunkMetadata (niveau document/chunk).
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://votre-domaine.org/schemas/rag_shard_3e.json", "title": "RAG Shard (Troisième) – Schéma de métadonnées", "type": "object", "additionalProperties": false, "required": ["shard_header", "chunks"], "properties": { "shard_header": { "type": "object", "additionalProperties": false, "required": [ "shard_id", "matiere", "niveau", "chapitre", "tier", "langue", "version", "program_ref" ], "properties": { "shard_id": { "type": "string", "description": "Identifiant canonique du shard", "pattern": "^RAG:FR:Troisieme:[A-Za-z0-9_]+:[A-Za-z0-9_]+:(Top10|Mid50-80|Bottom10)$", "examples": ["RAG:FR:Troisieme:Maths:Equations_2ndDeg:Bottom10"] }, "matiere": { "type": "string", "enum": ["Maths","Francais","Histoire","Geographie","Histoire-Géo/EMC","Physique","SVT","Anglais"], "description": "Matière principale" }, "niveau": { "type": "string", "const": "Troisieme", "description": "Niveau scolaire (classe)" }, "chapitre": { "type": "string", "description": "Chapitre/focus pédagogique, snake_case", "examples": ["Equations_2ndDeg","Stats_Probas","Argumentation_Brevet"] }, "tier": { "type": "string", "enum": ["Top10","Mid50-80","Bottom10"], "description": "Segment de performance visé" }, "langue": { "type": "string", "enum": ["fr","mg","fr-mg"], "description": "Langue du contenu (fr=français, mg=malgache, fr-mg=bilingue)" }, "age_min": { "type": "integer", "minimum": 10, "maximum": 18, "default": 13 }, "age_max": { "type": "integer", "minimum": 10, "maximum": 20, "default": 15 }, "program_ref": { "type": "object", "additionalProperties": false, "required": ["pays","cycle","millésime"], "properties": { "pays": { "type": "string", "default": "FR" }, "cycle": { "type": "string", "enum": ["College"] }, "millésime": { "type": "string", "pattern": "^[0-9]{4}(-[0-9]{4})?$", "examples": ["2024","2024-2025"] }, "liens_officiels": { "type": "array", "items": { "type": "string", "format": "uri" } } } }, "version": { "type": "string", "description": "Version du shard (YYYY-MM-DD ou semver)", "examples": ["2025-10-02","1.0.0"] }, "auteur_org": { "type": "string", "description": "Organisation éditrice (ex. Arovy / Collège …)" }, "licence": { "type": "string", "default": "interne" }, "mots_cles": { "type": "array", "items": { "type": "string" } }, "competences_cible": { "type": "array", "description": "Compétences/référentiels (codes ou libellés)", "items": { "type": "string" } }, "objectifs_pedago": { "type": "array", "items": { "type": "string" }, "description": "Objectifs d’apprentissage du shard" }, "style_reponse": { "type": "object", "description": "Contraintes de sortie pour l’assistant", "properties": { "ton": { "type": "string", "enum": ["synthétique","pas-a-pas","socratique"] }, "granularite": { "type": "string", "enum": ["fine","moyenne","haute"] }, "longueur_max_tokens": { "type": "integer", "minimum": 128, "maximum": 4096 } }, "additionalProperties": false }, "garde_fous": { "type": "object", "description": "Contraintes sécurité/âge/contenus", "properties": { "age_gate": { "type": "boolean", "default": true }, "sensibles_interdits": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": false }, "observabilite": { "type": "object", "description": "Clés de suivi analytique", "properties": { "kpi_hit_rate_cible": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.8 }, "kpi_qualite_cible": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.7 } }, "additionalProperties": false } } }, "chunks": { "type": "array", "minItems": 1, "items": { "type": "object", "title": "ChunkMetadata", "additionalProperties": false, "required": [ "chunk_id", "source_type", "source_ref", "content_type", "token_count", "embedding_model", "vector_dim", "hash_sha256", "ingested_at" ], "properties": { "chunk_id": { "type": "string", "description": "ID unique du chunk (UUID ou hash)" }, "source_type": { "type": "string", "enum": ["odoo_website","odoo_slides","pdf","html","md","faq_web"], "description": "Type de source d’origine" }, "source_ref": { "type": "object", "additionalProperties": false, "required": ["uri"], "properties": { "uri": { "type": "string", "format": "uri", "description": "URL ou chemin de la source" }, "file_id": { "type": "string", "description": "ID fichier (stockage ou Datasets)" }, "page_range": { "type": "string", "description": "Ex: 3-4,7" } } }, "content_type": { "type": "string", "enum": ["text","html","markdown"] }, "langue": { "type": "string", "enum": ["fr","mg","fr-mg"], "description": "Langue du chunk (si différente du shard)" }, "matiere": { "type": "string" }, "niveau": { "type": "string" }, "chapitre": { "type": "string" }, "tier": { "type": "string", "enum": ["Top10","Mid50-80","Bottom10"] }, "token_count": { "type": "integer", "minimum": 1, "maximum": 4000 }, "embedding_model": { "type": "string", "description": "Nom du modèle d’embedding utilisé", "examples": ["text-embedding-3-large"] }, "vector_dim": { "type": "integer", "minimum": 256, "maximum": 8192 }, "hash_sha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$" }, "quality_flags": { "type": "object", "additionalProperties": false, "properties": { "ocr": { "type": "boolean", "default": false }, "tables": { "type": "boolean", "default": false }, "equations": { "type": "boolean", "default": false }, "figures": { "type": "boolean", "default": false } } }, "pedago_tags": { "type": "array", "description": "Etiquettes pédagogiques (pré-requis, erreurs typiques, etc.)", "items": { "type": "string" } }, "allowed_roles": { "type": "array", "description": "Contrôles d’accès (ex. enseignant, élève, admin)", "items": { "type": "string", "enum": ["eleve","enseignant","admin"] } }, "ingested_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" } } } } } }
Exemple minimal (conforme au schéma)
{ "shard_header": { "shard_id": "RAG:FR:Troisieme:Maths:Equations_2ndDeg:Bottom10", "matiere": "Maths", "niveau": "Troisieme", "chapitre": "Equations_2ndDeg", "tier": "Bottom10", "langue": "fr", "age_min": 13, "age_max": 15, "program_ref": { "pays": "FR", "cycle": "College", "millésime": "2024-2025", "liens_officiels": [] }, "version": "2025-10-02", "auteur_org": "Arovy", "licence": "interne", "mots_cles": ["équation","factorisation","parabole"], "competences_cible": ["Résoudre une équation du 2nd degré simple"], "objectifs_pedago": ["Savoir factoriser et résoudre rapidement des cas canoniques"], "style_reponse": { "ton": "pas-a-pas", "granularite": "fine", "longueur_max_tokens": 600 }, "garde_fous": { "age_gate": true, "sensibles_interdits": [] }, "observabilite": { "kpi_hit_rate_cible": 0.8, "kpi_qualite_cible": 0.7 } }, "chunks": [ { "chunk_id": "b5d4a3e2-0c2b-4a56-9e3c-1c0f4d2a8f01", "source_type": "odoo_slides", "source_ref": { "uri": "https://votre-site/odoo/slides/3e-maths-equations", "file_id": "file_01HXY...", "page_range": "2-3" }, "content_type": "text", "langue": "fr", "matiere": "Maths", "niveau": "Troisieme", "chapitre": "Equations_2ndDeg", "tier": "Bottom10", "token_count": 520, "embedding_model": "text-embedding-3-large", "vector_dim": 3072, "hash_sha256": "0f5e7c3ac1b0d1f9b3d7e21e7d5e9d0f1c2a3b4c5d6e7f8091a2b3c4d5e6f708", "quality_flags": { "ocr": false, "tables": false, "equations": true, "figures": false }, "pedago_tags": ["prerequis:priorites_operatoires","erreur_typique:signe"], "allowed_roles": ["eleve","enseignant"], "ingested_at": "2025-10-02T09:30:00Z", "updated_at": "2025-10-02T09:30:00Z" } ] }
Conseils d’implémentation
- Filtres plats (File Search/Datasets) : dupliquez les champs clés au niveau chunk (matiere, niveau, chapitre, tier, langue) pour pouvoir filtrer directement à la requête, même si vous avez un shard header.
- Nommage : gardez snake_case côté contenu et des valeurs fermées (enum) pour matiere, tier, niveau → routage fiable.
- Traçabilité : le hash_sha256 garantit l’intégrité du chunk affiché lors d’un audit pédagogique.
- Observabilité : stockez retrieval_score, answer_score (côté logs) pour vos KPI, sans les mêler aux métadonnées d’index.
Souhaitez-vous que je vous génère aussi un fichier JSON vierge (template) pour vos 18 shards 3e afin de le pré-remplir et l’ingérer tout de suite (OpenAI hébergé ou VPS/pgvector) ?
2- les prompts d’assistant adaptés aux 3 tiers,
👌 — je vous propose des prompts “système” (assistant instructions) pour la classe de 3e, adaptés aux 3 tiers d’élèves.
Ces prompts sont conçus pour être utilisés soit :
- en assistant system prompt (si vous passez par responses.create ou assistants API chez OpenAI),
- soit en préfixe contextuel dans un RAG custom (VPS + pgvector).
Chaque tier module le ton, la pédagogie, et le niveau de détail.
🎯 Prompts par tier
🟢 Tier « Top 10 % » (élèves très doués)
Tu es un assistant pédagogique pour des élèves de troisième qui font partie des 10 % les plus performants. Objectif : les pousser plus loin, leur proposer des défis, aller vite sur les bases. Instructions : - Utilise un ton clair et concis, mais soutenu, académique. - Donne directement la solution mais enchaîne avec des prolongements (ex. lien avec le lycée, démonstrations avancées). - Pose souvent des questions ouvertes de type concours ou olympiades. - Ne répète pas les bases : pars du principe que l’élève les connaît déjà. - Fournis des références culturelles ou historiques quand c’est pertinent (ex. mathématicien, expérience scientifique). - Encourage la rédaction soignée et l’argumentation critique.
Exemple attendu :
👉 « Tu sais déjà factoriser ce polynôme. Peux-tu maintenant comparer sa courbe avec une équation paramétrée au lycée ? »
🟡 Tier « Mid 50–80 % » (élèves moyens)
Tu es un assistant pédagogique pour des élèves de troisième d’un niveau moyen (la majorité de la classe). Objectif : consolider les connaissances, rendre la matière claire et progressive. Instructions : - Utilise un ton bienveillant, rassurant, avec des phrases courtes. - Propose des explications pas-à-pas, avec 3 à 5 étapes claires. - Donne un exemple concret issu de la vie quotidienne (ex. statistiques de sport, génétique familiale simple). - Termine par une mini-question de vérification (QCM ou vrai/faux). - Utilise des schémas ou analogies simples, sans entrer dans les détails complexes. - Si l’élève fait une erreur, reformule calmement et montre l’étape correcte.
Exemple attendu :
👉 « Résolvons ensemble. Étape 1 : écris ton équation. Étape 2 : isole x. Étape 3 : vérifie en remplaçant. Peux-tu refaire avec x=5 ? »
🔴 Tier « Bottom 10 % » (élèves en grande difficulté)
Tu es un assistant pédagogique pour des élèves de troisième en grande difficulté scolaire. Objectif : les réconcilier avec l’apprentissage, les guider pas à pas, éviter de les décourager. Instructions : - Utilise un ton chaleureux, simple, presque conversationnel. - Phrase maximum : 10–12 mots, vocabulaire très accessible. - Décompose chaque raisonnement en mini-étapes (1 étape = 1 phrase). - Donne beaucoup d’analogies concrètes (recettes, sport, jeux). - Vérifie la compréhension à chaque étape par une question très simple. - Mets en valeur chaque bonne réponse (« Bravo ! », « C’est juste »). - Évite les longues démonstrations, concentre-toi sur l’essentiel. - Encourage souvent : félicitations fréquentes, reformulation positive.
Exemple attendu :
👉 « On veut trouver x. Imagine une balance. Tu retires 3 des deux côtés. Bravo ! Maintenant combien reste-t-il ? »
⚖️ Résumé des différences
| Dimension | Top 10 % | Mid 50–80 % | Bottom 10 % |
|---|---|---|---|
| Ton | Académique, exigeant | Clair, rassurant | Chaleureux, très simple |
| Niveau d’explication | Avancé, enrichi | Pas-à-pas standard | Micro-étapes, très guidées |
| Analogies | Références culturelles | Vie quotidienne | Jeux, recettes, concret immédiat |
| Vérifications | Questions ouvertes | QCM / vrai-faux | Questions ultra-simples |
| Feedback | Critique constructive | Réassurance | Félicitations fréquentes |
| Objectif | Défi, approfondissement | Consolidation | Réconciliation avec la matière |
👉 Si vous validez ces prompts, je peux vous préparer :
- Une bibliothèque JSON de prompts (1 objet par tier) que vous pouvez injecter dans un routeur.
- Un exemple de script Node/Python qui choisit le bon prompt système selon tier et matiere.
Voulez-vous que je vous fasse directement la version JSON “plug-and-play” (Top10 / Mid50-80 / Bottom10) pour que vous puissiez les intégrer à vos shards ?
Si oui Voir https://chatgpt.com/share/68de754d-58e8-8005-95ae-b4bcca83aa57
3.1 - Un script d’ingestion OpenAI hébergé ou VPS/pgvector prêt à coller.
Voici deux scripts d’ingestion prêts à coller : Hébergé OpenAI (File Search / Vector Store)
J’ai gardé les I/O simples : vous placez des fichiers .txt|.md|.html (déjà nettoyés) dans un dossier et un petit JSON de métadonnées “shard”. Le script fabrique les enregistrements et les pousse.
1) Ingestion « Full OpenAI » (File Search / Vector Store)
Ce que fait le script
- Crée (ou réutilise) un Vector Store.
- Concatène les fichiers d’un dossier en JSONL (1 ligne = {text, metadata}).
- Upload le JSONL en File puis l’attache au Vector Store.
- Optionnel : crée/actualise un Assistant pointé sur ce Vector Store.
Pré-requis
- Python 3.10+
- pip install openai python-dotenv
- Variables d’env. : OPENAI_API_KEY, VECTOR_STORE_NAME, ASSISTANT_NAME (optionnel)
Arborescence minimale
ingest_openai/ .env ingest_openai.py data/ 3e_maths_equations_bottom10/ shard_header.json # vos métadonnées shard (schéma fourni précédemment) chunks/ 001.txt 002.txt 003.md
.env (exemple)
OPENAI_API_KEY=sk-... VECTOR_STORE_NAME=RAG_FR_3e_Maths_Equations_Bottom10 ASSISTANT_NAME=Assistant_3e_Maths_Bottom10
ingest_openai.py
import os, json, glob, tempfile from pathlib import Path from datetime import datetime from dotenv import load_dotenv from openai import OpenAI load_dotenv() client = OpenAI() DATA_DIR = Path("data/3e_maths_equations_bottom10") SHARD_HEADER = DATA_DIR / "shard_header.json" CHUNKS_DIR = DATA_DIR / "chunks" VECTOR_STORE_NAME = os.getenv("VECTOR_STORE_NAME", f"VS_{DATA_DIR.name}") ASSISTANT_NAME = os.getenv("ASSISTANT_NAME", "") def load_shard_header(): with open(SHARD_HEADER, "r", encoding="utf-8") as f: return json.load(f)["shard_header"] def build_jsonl_records(shard_header): """ Convertit /chunks/*.txt|.md|.html en lignes JSONL {text, metadata} On duplique les filtres clés en metadata (matiere, niveau, chapitre, tier, langue). """ records = [] for path in sorted(glob.glob(str(CHUNKS_DIR / "*"))): if not Path(path).is_file(): continue with open(path, "r", encoding="utf-8", errors="ignore") as f: text = f.read() meta = { "shard_id": shard_header["shard_id"], "matiere": shard_header["matiere"], "niveau": shard_header["niveau"], "chapitre": shard_header["chapitre"], "tier": shard_header["tier"], "langue": shard_header.get("langue", "fr"), "source_type": Path(path).suffix.lstrip("."), "filename": Path(path).name, "ingested_at": datetime.utcnow().isoformat() + "Z", "program_ref": shard_header.get("program_ref", {}), "version": shard_header.get("version", "") } records.append({"text": text, "metadata": meta}) return records def write_jsonl(records): tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".jsonl") with open(tmp.name, "w", encoding="utf-8") as f: for r in records: f.write(json.dumps(r, ensure_ascii=False) + "\n") return tmp.name def get_or_create_vector_store(name): # Tente de retrouver un VS existant par nom, sinon crée # NB: l’API officielle recommande: créer un Vector Store puis y ajouter les fichiers. vs = client.beta.vector_stores.create(name=name) return vs def attach_file_to_vector_store(vs_id, file_id): client.beta.vector_stores.files.create( vector_store_id=vs_id, file_id=file_id ) def create_or_update_assistant(vs_id): if not ASSISTANT_NAME: return # Crée un assistant minimal qui utilise file_search avec ce Vector Store client.assistants.create( name=ASSISTANT_NAME, model="gpt-5", instructions=( "Tu es un assistant pédagogique 3e. " "Utilise le tool file_search avec les filtres fournis par le client (matiere/niveau/chapitre/tier/langue)." ), tools=[{"type": "file_search"}], tool_resources={"file_search": {"vector_store_ids": [vs_id]}} ) def main(): shard_header = load_shard_header() records = build_jsonl_records(shard_header) jsonl_path = write_jsonl(records) print(f"→ {len(records)} records, JSONL: {jsonl_path}") vs = get_or_create_vector_store(VECTOR_STORE_NAME) print(f"→ Vector Store: {vs.id} ({VECTOR_STORE_NAME})") # Upload le JSONL comme File avec purpose assistants uploaded = client.files.create( file=open(jsonl_path, "rb"), purpose="assistants" ) print(f"→ File uploaded: {uploaded.id}") # Attache le fichier au Vector Store attach_file_to_vector_store(vs.id, uploaded.id) print("→ Fichier attaché au Vector Store.") # (Optionnel) Crée un assistant pointé sur ce VS create_or_update_assistant(vs.id) print("✓ Ingestion terminée.") if __name__ == "__main__": main()
Remarques / docs
- La démarche recommandée par OpenAI : Files → Vector Store → File Search (assistants/tools) OpenAI Platform+1
- Pour la partie Realtime Audio/WebRTC, voir les guides Realtime si vous enchaînez tout de suite avec la voix. OpenAI Platform+1
3.2 - Un script d’ingestion VPS/pgvecto prêt à coller.
2) Ingestion « VPS + Postgres/pgvector »
Ce que fait le script
- Crée la table docs (si besoin) avec une colonne embedding vector(3072).
- Lit vos fichiers de data/.../chunks/.
- Calcule les embeddings avec text-embedding-3-large.
- Insère {content, metadata, embedding} dans Postgres.
- (Optionnel) crée un index IVFFlat (cosine) pour accélérer la recherche.
Pré-requis
- Postgres 15+ avec CREATE EXTENSION vector; (pgvector)
- pip install psycopg[binary] openai python-dotenv
- Variables d’env. : OPENAI_API_KEY, DATABASE_URL (ex: postgresql://user:pass@host:5432/dbname)
SQL d’initialisation (init_pgvector.sql)
CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE IF NOT EXISTS docs ( id BIGSERIAL PRIMARY KEY, content TEXT NOT NULL, matiere TEXT, niveau TEXT, chapitre TEXT, tier TEXT, langue TEXT, shard_id TEXT, source_type TEXT, filename TEXT, version TEXT, program_ref JSONB, ingested_at TIMESTAMPTZ DEFAULT now(), embedding vector(3072) -- text-embedding-3-large ); -- Index approx. (accélère les kNN, à créer APRES quelques milliers d’items) -- Ajustez lists selon la taille (ex. 100–2000) -- Cosine: vector_cosine_ops CREATE INDEX IF NOT EXISTS idx_docs_embed_cosine ON docs USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
(Notes : pgvector supporte IVFFlat et HNSW selon version; créez l’extension et les index comme recommandé dans vos environnements.) GitHub+2Severalnines+2
.env
OPENAI_API_KEY=sk-... DATABASE_URL=postgresql://user:pass@localhost:5432/ragdb DATA_DIR=data/3e_maths_equations_bottom10
ingest_pgvector.py
import os, json, glob from pathlib import Path from datetime import datetime from dotenv import load_dotenv import psycopg from openai import OpenAI load_dotenv() client = OpenAI() DATA_DIR = Path(os.getenv("DATA_DIR", "data/3e_maths_equations_bottom10")) SHARD_HEADER = DATA_DIR / "shard_header.json" CHUNKS_DIR = DATA_DIR / "chunks" DATABASE_URL = os.getenv("DATABASE_URL") EMBED_MODEL = "text-embedding-3-large" # 3072 dims def load_shard_header(): with open(SHARD_HEADER, "r", encoding="utf-8") as f: return json.load(f)["shard_header"] def get_embedding(text: str): # Tronquez/normalisez si besoin (long textes) – ici on envoie le texte brut emb = client.embeddings.create( model=EMBED_MODEL, input=text ) return emb.data[0].embedding # list[float] de taille 3072 def iter_chunk_files(): for path in sorted(glob.glob(str(CHUNKS_DIR / "*"))): if Path(path).is_file(): yield Path(path) def main(): shard = load_shard_header() with psycopg.connect(DATABASE_URL) as conn: conn.execute("SET application_name = 'rag_ingest';") for fpath in iter_chunk_files(): with open(fpath, "r", encoding="utf-8", errors="ignore") as f: content = f.read() emb = get_embedding(content) conn.execute( """ INSERT INTO docs (content, matiere, niveau, chapitre, tier, langue, shard_id, source_type, filename, version, program_ref, ingested_at, embedding) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """, ( content, shard["matiere"], shard["niveau"], shard["chapitre"], shard["tier"], shard.get("langue", "fr"), shard["shard_id"], fpath.suffix.lstrip("."), fpath.name, shard.get("version", ""), json.dumps(shard.get("program_ref", {})), datetime.utcnow(), emb, ), ) conn.commit() print("✓ Ingestion terminée dans Postgres/pgvector.") if __name__ == "__main__": main()
Requête kNN minimale (ex. recherche cosine)
-- $1 = embedding(query), $2..$5 = filtres SELECT id, filename, content, 1 - (embedding <=> $1) AS similarity FROM docs WHERE matiere = $2 AND niveau = $3 AND chapitre = $4 AND tier = $5 ORDER BY embedding <-> $1 LIMIT 8;
Références utiles
- Guide File Search & Vector Store (OpenAI) : upload → vector store → file search. OpenAI Platform+1
- Dimensions du modèle text-embedding-3-large (≈3072). OpenAI Platform+1
- pgvector / indexages (IVFFlat/HNSW) et ops cosine. GitHub+1
Astuces pratiques (communes)
- Dupliquer les filtres clés au niveau record (matiere, niveau, chapitre, tier, langue) pour un routage/filtrage fiable côté requête.
- Nettoyage HTML → texte : si vous partez d’Odoo Website/Slides export HTML/PDF, pré-nettoyez (supprimer menus/footers).
- Batches : si vous avez des milliers de fichiers, passez par des lots (et éventuellement Batch API côté OpenAI). OpenAI Platform
- Index pgvector : créez/REINDEX après insertion d’un volume significatif (sinon latence à la construction).
- Souhaitez-vous que je vous fournisse un dossier ZIP d’exemple (avec shard_header.json, 3 fichiers de chunks, et les deux scripts déjà configurés pour 3e → Maths → Équations → Bottom10) ?
Si oui aller en bas de la page suivant
https://chatgpt.com/share/68de754d-58e8-8005-95ae-b4bcca83aa57