"""A/B test : effet du prompt engineering sur la détection Accord/Désaccord. Ground truth (vérifié visuellement sur les images) : - OGC 7 p1 → "accord" - OGC 55 p1 → "désaccord" - OGC 27 p1 → "désaccord" """ import time from pathlib import Path from pipeline.ocr_glm import GLMOCR from pipeline.ingest import pdf_to_images # --- Variantes de prompt --- PROMPTS = { # V0 : ce que fait actuellement la V1 — schéma JSON complet "V0_json_schema_complet": """Lis la fiche médicale OGC et renvoie STRICTEMENT le JSON suivant : { "etablissement": "", "finess": "", "n_ogc": "", "ghm_etab": "", "ghs_etab": "", "accord_desaccord": "", "praticien_conseil": "" }""", # V1 : JSON minimal, seulement la checkbox "V1_json_cible": """Regarde la fiche médicale OGC et renvoie UNIQUEMENT ce JSON : {"accord_desaccord": ""} Pour accord_desaccord, écris "accord" ou "désaccord" selon la case cochée en bas à droite (zone "Accord □ Désaccord □").""", # V2 : question directe en langage naturel "V2_question_naturelle": """Sur la fiche médicale OGC, en bas à droite, il y a deux cases à cocher : "Accord" et "Désaccord". Quelle case est cochée ? Réponds UNIQUEMENT par un seul mot : "accord" ou "désaccord".""", # V3 : chain of thought court "V3_CoT_court": """Sur cette fiche médicale OGC : 1. Repère en bas à droite la zone avec "Accord" et "Désaccord", chacun suivi d'une case à cocher. 2. Identifie laquelle des deux cases est cochée (X, V ou remplie). 3. Réponds par un JSON strict : {"case_cochee": "accord"} ou {"case_cochee": "désaccord"}.""", } CASES = [ ("2018 CARC/OGC 7.pdf", 1, "accord"), ("2018 CARC/OGC 55.pdf", 1, "désaccord"), ("2018 CARC/OGC 27.pdf", 1, "désaccord"), ] def main(): ocr = GLMOCR() print(f"Modèle chargé, VRAM={ocr.vram_gb:.2f} Go\n") results = {} for pdf, page, expected in CASES: images = pdf_to_images(pdf) img = images[page - 1] print(f"=== {Path(pdf).stem} page {page} (attendu: {expected}) ===") for name, prompt in PROMPTS.items(): t0 = time.time() res = ocr.run(img, prompt, max_new_tokens=256) out = res["text"].strip().replace("\n", " ")[:180] print(f" [{name}] ({time.time()-t0:.1f}s)") print(f" → {out}") results.setdefault(name, []).append((expected, out)) print() print("=== RÉCAPITULATIF ===") for name, outs in results.items(): hits = 0 for expected, out in outs: low = out.lower() # On compte un hit si la bonne valeur apparaît et pas l'autre is_acc = "accord" in low and "désaccord" not in low and "desaccord" not in low is_des = "désaccord" in low or "desaccord" in low got = "accord" if is_acc else ("désaccord" if is_des else "?") hits += 1 if got == expected else 0 print(f" {name:28s} : {hits}/{len(outs)}") if __name__ == "__main__": main()