Mapping RAG (structures JSON à indexer dans Supabase) pour que l’agent LLM puisse générer ces create/write à partir de la transcription vocale.
Parfait, on va faire comme si vous étiez en train de préparer vos “documents RAG” à insérer dans une table rag_documents (ou équivalent) dans Supabase.
L’objectif :
👉 À partir d’une transcription vocale, l’agent LLM peut :
- retrouver les bons produits Odoo,
- comprendre qui est le payeur (assurance / mutuelle / patient),
- générer les bons create / write sur sale.order, sale.order.line, etc.
Je vous propose :
- Un schéma générique de doc RAG pour Supabase.
-
Des structures JSON pour :
- produits (product.template),
- patients (res.partner),
- règles de remboursement / mapping (doc “métier”),
- gabarits de commandes vocales (pour guider l’agent).
1. Schéma générique d’un document RAG dans Supabase
Vous pouvez avoir une table rag_items avec au minimum :
- id (UUID)
- embedding (vector)
- doc_type (text) – ex : "product", "patient", "rule", "template"
- content (text) – texte riche pour l’embed
- metadata (jsonb) – pour les infos structurées et les liens Odoo
Côté JSON (avant insertion), chaque doc que vous allez embedder pourrait ressembler à :
{ "doc_type": "product", "content": "Pansement stérile 10x10. Code LPP LPP000111. Remboursable par l'assurance maladie. Prescription obligatoire. Taux conventionnel 12,75 €.", "metadata": { "odoo_model": "product.template", "odoo_id": 42, "name": "Pansement stérile 10x10", "x_lpp_code": "LPP000111", "x_reimbursable_by": "assurance_maladie", "x_requires_prescription": true, "x_convention_rate": 12.75, "synonyms": [ "pansement", "compresse stérile", "pansement 10x10", "pansement stérile" ] } }
L’embed se fait sur content, et vous gardez tout le reste dans metadata pour reconstruire les appels JSON-RPC.
2. Documents RAG pour les PRODUITS (product.template)
2.1. Structure JSON recommandée
Pour chaque produit important de l’eShop médical, un doc du type :
{ "doc_type": "product", "content": "Seringue 5 ml stérile, à usage unique. Code LPP LPP789456. Remboursable par la mutuelle. Pas de prescription obligatoire. Taux conventionnel 0 €.", "metadata": { "odoo_model": "product.template", "odoo_id": 101, "name": "Seringue 5 ml", "x_lpp_code": "LPP789456", "x_reimbursable_by": "mutuelle", "x_requires_prescription": false, "x_convention_rate": 0.0, "uom_name": "unité", "default_qty": 1, "keywords": [ "seringue", "seringue 5 ml", "injection", "piqûre" ], "llm_hints": { "category": "injectable", "usage_context": "infirmier", "voice_examples": [ "ajoute deux seringues de cinq millilitres", "rajoute une seringue 5 ml", "il me faut trois seringues pour les injections" ] } } }
Idée :
- content = phrase(s) en français, très lisibles, que le LLM peut utiliser pour matcher la demande vocale.
- metadata = tout ce qu’il faut pour construire l’appel create sale.order.line (id produit, rembourseur, prescription, etc.).
2.2. Exemple pour un pansement remboursé assurance maladie
{ "doc_type": "product", "content": "Pansement stérile 10x10 cm, pour pansements post-opératoires. Code LPP LPP000111. Remboursable par l'assurance maladie. Prescription obligatoire. Taux conventionnel 12,75 €.", "metadata": { "odoo_model": "product.template", "odoo_id": 42, "name": "Pansement stérile 10x10", "x_lpp_code": "LPP000111", "x_reimbursable_by": "assurance_maladie", "x_requires_prescription": true, "x_convention_rate": 12.75, "uom_name": "boîte", "default_qty": 1, "keywords": [ "pansement", "pansement stérile", "compresse stérile", "pansement 10x10" ], "llm_hints": { "category": "pansement", "typical_dosage_range": "1 à 4 boîtes", "voice_examples": [ "prends-moi deux boîtes de pansements stériles", "ajoute un pansement dix par dix", "rajoute trois compresses stériles" ] } } }
3. Documents RAG pour les PATIENTS (res.partner)
Objectif : que l’agent, à partir du contexte appel (patient courant), sache :
- s’il s’agit d’un senior,
- son niveau de droits,
- qui est le payeur par défaut (x_default_payor),
- quelles sont la caisse et la mutuelle (x_insurer_id, x_mutuelle_id).
3.1. Structure JSON recommandée
Pour chaque patient éligible à la commande vocale :
{ "doc_type": "patient", "content": "Fiche patient : Jeanne Martin, 82 ans, senior avec ALD 100 %. Payeur par défaut : assurance maladie. Assureur : CPAM Yvelines. Mutuelle : Mutuelle Senior Plus.", "metadata": { "odoo_model": "res.partner", "odoo_id": 10, "name": "Jeanne Martin", "x_is_senior": true, "x_rights_level": "ALD100", "x_default_payor": "assurance_maladie", "x_insurer_id": 30, "x_insurer_name": "CPAM Yvelines", "x_mutuelle_id": 31, "x_mutuelle_name": "Mutuelle Senior Plus", "contact": { "phone": "+33 6 00 00 00 00", "email": "jeanne.martin@example.com" }, "llm_hints": { "preferred_payor_logic": "par défaut, facturer à l'assurance maladie sauf mention explicite de la mutuelle ou du patient dans le texte vocal.", "voice_examples": [ "pour madame Martin, ça sera pris en charge par la sécu", "pour Jeanne, facture sur la mutuelle cette fois", "là, on fait payer le patient" ] } } }
Dans la logique d’agent :
- Récupérer le patient courant via le contexte (pas forcément via RAG à chaque fois), mais le RAG peut servir pour des cas plus flous (“madame Martin de Versailles”).
- x_default_payor sert de fallback si le texte vocal est ambigu.
4. Documents RAG pour les RÈGLES MÉTIER / MAPPING
Ce sont des docs “statiques” qui expliquent au LLM comment faire le lien entre le langage naturel et vos champs Odoo.
Par exemple : un document doc_type: "rule" qui décrit le mapping du payeur (x_payor / x_default_payor) :
{ "doc_type": "rule", "content": "Règles de choix du payeur pour les commandes vocales. Si l'infirmière mentionne : 'pris en charge par la sécu', 'la sécurité sociale', 'l'assurance maladie' : utiliser x_payor = 'assurance_maladie'. Si elle dit 'mutuelle', 'complémentaire', 'complémentaire santé' : utiliser x_payor = 'mutuelle'. Si elle indique 'à la charge du patient', 'le patient paie', 'non remboursé' : utiliser x_payor = 'patient'. En l'absence de précision, utiliser le champ x_default_payor du patient concerné.", "metadata": { "rule_name": "payor_mapping", "targets": [ "sale.order.line.x_payor", "res.partner.x_default_payor" ], "keywords": [ "assurance maladie", "mutuelle", "patient", "sécurité sociale", "complémentaire" ] } }
Autre exemple : doc de règles pour la prescription obligatoire :
{ "doc_type": "rule", "content": "Lors de la génération d'une commande vocale, si un produit a x_requires_prescription = true, l'agent doit s'assurer que l'infirmière dispose d'une ordonnance valide. Si la transcription mentionne 'sans ordonnance' ou 'pas d'ordonnance' pour un produit nécessitant une prescription, l'agent doit soit refuser l'ajout, soit marquer la commande avec un avertissement.", "metadata": { "rule_name": "prescription_requirement", "targets": [ "product.template.x_requires_prescription", "sale.order" ] } }
Ces docs RAG servent de mémoire “règles métier” que le LLM peut récupérer pour guider sa génération de JSON-RPC.
5. Documents RAG pour GABARITS DE COMMANDE VOCALE
Vous pouvez aussi indexer des templates de mapping langage → JSON pour aider l’agent à “voir des exemples”.
Exemple :
{ "doc_type": "voice_order_template", "content": "Exemple de commande vocale : 'Pour madame Martin, ajoute deux boîtes de pansements stériles dix par dix remboursés par la sécu et une boîte de compresses non remboursées à la charge du patient.'\n\nJSON attendu :\n- sale.order: { partner_id: <id de madame Martin>, x_is_voice_order: true, x_nurse_id: <id infirmière courante> }\n- sale.order.line 1: { product_id: <id pansement stérile 10x10>, product_uom_qty: 2, x_payor: 'assurance_maladie' }\n- sale.order.line 2: { product_id: <id compresses non remboursables>, product_uom_qty: 1, x_payor: 'patient' }", "metadata": { "template_name": "voice_order_example_1", "targets": [ "sale.order", "sale.order.line" ], "llm_hints": { "strategy": "identifier les produits dans le texte, récupérer leurs fiches depuis Odoo/RAG, déterminer le payeur selon les mots-clés, puis générer les appels create." } } }
6. Vue d’ensemble : comment l’agent s’en sert
Pipeline typique :
- Transcription vocale (OpenAI Realtime) → texte.
-
L’agent :
- interroge le RAG doc_type = "product" pour trouver les produits mentionnés,
- récupère le patient courant (via contexte + éventuellement RAG doc_type = "patient"),
- appelle aussi le RAG sur doc_type = "rule" + doc_type = "voice_order_template" pour connaître les règles de mapping et des exemples.
- À partir de ça, le LLM produit un objet JSON “intermédiaire” de ce style :
{ "order": { "model": "sale.order", "values": { "partner_id": 10, "x_is_voice_order": true, "x_nurse_id": 15, "x_visit_id": 7 } }, "lines": [ { "model": "sale.order.line", "values": { "product_id": 42, "product_uom_qty": 2, "x_payor": "assurance_maladie" } }, { "model": "sale.order.line", "values": { "product_id": 101, "product_uom_qty": 1, "x_payor": "patient" } } ] }
- Un petit tool / agent “Odoo” prend ce JSON intermédiaire et le transforme en vraies requêtes JSON-RPC create / write.
Si vous voulez, au prochain round je peux vous proposer :
- un schéma SQL Supabase concret (CREATE TABLE rag_items…) adapté à ce format doc_type / content / metadata,
- plus un exemple de prompt système pour l’agent LLM, lui expliquant exactement : “Tu as accès à un outil de recherche RAG Supabase qui renvoie ces JSON, et tu dois produire un JSON intermédiaire ‘order + lines’ conforme à Odoo.”