ITSkillsCenter
Intelligence Artificielle

Assistant juridique OHADA avec Claude et RAG

4 min de lecture
Miniature - Assistant juridique OHADA avec Claude et RAG

Ce que vous saurez faire

  1. Assistant juridique OHADA avec RAG
  2. Parser les Actes uniformes
  3. Obliger citation d’articles
  4. Déployer Streamlit

Étape 1 — Stack

Ingestion: PyMuPDF + Tesseract OCR (textes scannés)
Chunking: par article OHADA
Embeddings: voyage-3
Vector DB: ChromaDB
Reranker: cohere rerank-multilingual-v3.0
LLM: Claude Sonnet 4.6 (citations fiables)

Étape 2 — Parser OHADA

import pymupdf, re
from pathlib import Path

def parser_ohada(pdf_path):
    doc = pymupdf.open(pdf_path)
    texte = "\n".join(p.get_text() for p in doc)
    articles = re.split(r'(Article\s+\d+\s*(?:bis|ter)?\s*\.?-?)', texte)
    chunks = []
    for i in range(1, len(articles)-1, 2):
        num = articles[i].strip()
        contenu = articles[i+1].strip()
        if len(contenu) < 10: continue
        chunks.append({
            "texte": f"{num} {contenu}",
            "metadata": {"source": Path(pdf_path).stem, "article": num,
                        "acte": "OHADA"}})
    return chunks

Étape 3 — Indexation Chroma

import chromadb, voyageai

vo = voyageai.Client()
chroma = chromadb.PersistentClient(path="./chroma_ohada")
coll = chroma.get_or_create_collection("ohada")

for pdf in Path("./textes_ohada").glob("*.pdf"):
    chunks = parser_ohada(str(pdf))
    texts = [c["texte"] for c in chunks]
    embs = vo.embed(texts, model="voyage-3", input_type="document").embeddings
    coll.add(
        documents=texts, embeddings=embs,
        metadatas=[c["metadata"] for c in chunks],
        ids=[f"{c['metadata']['source']}-{c['metadata']['article']}-{i}"
             for i,c in enumerate(chunks)])

Étape 4 — Retrieval + reranking

import cohere
co = cohere.Client()

def recherche(question, k_retrieve=20, k_final=6):
    q_emb = vo.embed([question], model="voyage-3", input_type="query").embeddings[0]
    res = coll.query(query_embeddings=[q_emb], n_results=k_retrieve)
    docs = res["documents"][0]
    metas = res["metadatas"][0]
    rerank = co.rerank(model="rerank-multilingual-v3.0",
                       query=question, documents=docs, top_n=k_final)
    return [{"texte":docs[r.index], "meta":metas[r.index], "score":r.relevance_score}
            for r in rerank.results]

Étape 5 — LLM avec citations obligatoires

from anthropic import Anthropic
claude = Anthropic()

SYSTEM = """Tu es un assistant juridique OHADA.
Règles strictes:
1. Cite chaque affirmation avec [acte, article]
2. Si info absente: "Information non disponible dans les textes fournis"
3. Aucune interprétation: reste factuel
4. Ton formel juridique français
5. Si question hors OHADA: refuser"""

def repondre(question):
    chunks = recherche(question)
    contexte = "\n\n".join(f"[{c['meta']['acte']}, {c['meta']['article']}]\n{c['texte']}"
                              for c in chunks)
    r = claude.messages.create(
        model="claude-sonnet-4-6", max_tokens=1500,
        system=[{"type":"text","text":SYSTEM,"cache_control":{"type":"ephemeral"}}],
        messages=[{"role":"user","content":
            f"Extraits:\n{contexte}\n\nQuestion: {question}"}])
    return r.content[0].text, [c["meta"] for c in chunks]

Étape 6 — Interface Streamlit

import streamlit as st
st.title("Assistant OHADA")

if "hist" not in st.session_state: st.session_state.hist = []

q = st.chat_input("Question juridique...")
if q:
    with st.spinner("Recherche..."):
        rep, sources = repondre(q)
    st.session_state.hist.append({"role":"user","content":q})
    st.session_state.hist.append({"role":"assistant","content":rep,"sources":sources})

for m in st.session_state.hist:
    with st.chat_message(m["role"]):
        st.write(m["content"])
        if m.get("sources"):
            with st.expander("Sources"):
                for s in m["sources"]:
                    st.caption(f"- {s['acte']} {s['article']}")

Étape 7 — Garde-fous

AVERTISSEMENT = """⚠ Assistant pédagogique basé sur textes OHADA publiés.
Ne remplace pas un avocat/notaire pour décisions contractuelles."""

INTERDITS = ["rédaction contrat", "conseil personnalisé", "défense judiciaire"]

def garde_fou(q, r):
    if any(x in q.lower() for x in INTERDITS):
        return r + "\n\n⚠ Pour ces cas, consultez un juriste."
    return r

Étape 8 — Évaluation

from ragas import evaluate
from ragas.metrics import faithfulness, context_precision

result = evaluate(dataset, metrics=[faithfulness, context_precision])
# Seuil: faithfulness >= 0.90 (citations correctes)

Étape 9 — Déploiement

fly launch
fly secrets set ANTHROPIC_API_KEY=... VOYAGE_API_KEY=... COHERE_API_KEY=...
fly deploy

Coûts 100 questions/jour

Embeddings indexation: 0,5 USD one-off
Reranking: 0,50 USD/mois
Claude Sonnet avec cache: 5-7 USD/mois
Chroma + Streamlit: gratuit
Total: ~15 USD/mois (9k FCFA)
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité