Mistral & modèles open 2026
TL;DR En 2026 la souveraineté n'est plus un argument marketing — c'est une obligation contractuelle sur 30-40% des appels d'offres FR (santé HDS, défense, secteur public, banque). Maîtriser Mistral (Small 3, Medium 3, Large 2, Codestral 2, Pixtral, Mistral Embed) + alternatives open (Llama 4, Qwen 3, DeepSeek V3.2) hébergées sur Scaleway / OVHcloud AI Endpoints / Le Chat Enterprise te débloque des marchés où OpenAI/Anthropic sont disqualifiés d'office. Tu factures alors 1 300-1 500€/j parce que tu es l'un des rares à savoir le faire sérieusement.
Mental model
Souveraineté = 3 couches qui doivent toutes être vertes : modèle (poids open ou contractuellement EU), inference (datacenter EU/FR, opérateur EU), data (jamais sortir UE). Si une seule des 3 fuit, tu n'es pas conforme.
┌──────────────────────────────────────────────────────────┐
│ SOUVERAINETÉ EU/FR — 3 couches │
│ │
│ 1. MODÈLE │
│ ✔ Mistral (entreprise FR, modèles 100% EU) │
│ ✔ Llama 4 / Qwen 3 (poids open, self-host) │
│ ✗ OpenAI / Anthropic (US, sauf clauses EU spéciales)│
│ │
│ 2. INFERENCE (où tourne le compute) │
│ ✔ Scaleway (FR, ISO 27001, HDS) │
│ ✔ OVHcloud AI Endpoints (FR, HDS) │
│ ✔ Mistral Le Chat Enterprise (FR) │
│ ✔ Self-host on-prem / VPC EU │
│ ~ Azure EU / AWS EU (legalement EU mais Cloud Act) │
│ ✗ OpenAI / Anthropic US endpoints │
│ │
│ 3. DATA (PII, données sensibles) │
│ Storage EU, encryption at rest + in transit │
│ DPA signé, RGPD article 28 │
│ Pour santé : HDS (Hébergeur Données de Santé) │
│ Pour défense : SecNumCloud │
└──────────────────────────────────────────────────────────┘
PILE SOUVERAINE TYPE 2026 :
┌─────────────────────────┐
│ App (FastAPI on Scw) │
├─────────────────────────┤
│ Mistral Large 2 (Scw) │
│ + Mistral Embed (Scw) │
├─────────────────────────┤
│ pgvector + S3 EU │
└─────────────────────────┘Analogie : si OpenAI/Anthropic sont des avocats américains très talentueux, Mistral + Scaleway, c'est ton avocat lyonnais qui connaît le code civil français, plaide à Paris, et facture en euros. Pour un client banque privée FR avec données clients sensibles, le second est non-négociable.
Comment un·e architecte raisonne (l'arbre de décision)
La souveraineté n'est pas binaire « FR vs US » : c'est une fonction du niveau de risque légal × coût d'opportunité. Voici l'ordre de questionnement réel :
1. La donnée est-elle régulée (santé/défense/PII massive) ?
NON → tu n'as PAS de contrainte souveraine → prends le meilleur modèle (souvent Claude/GPT). Stop.
OUI ↓
2. Le client accepte-t-il un DPA + clauses EU d'un provider US ?
OUI → API US avec data residency EU + DPA possible (Azure OpenAI EU, Bedrock EU). Moins cher que self-host.
NON ↓
3. Volume + besoin temps réel + budget GPU ?
Faible volume → Mistral API (Scaleway/OVH) : zéro CAPEX, conforme, facturé au token.
Fort volume / on-prem imposé → self-host vLLM (CAPEX GPU + équipe MCO).
4. Classification > Diffusion Restreinte / SecNumCloud requis ?
OUI → self-host on-prem air-gapped OBLIGATOIRE. Pas d'API, même EU.Le piège du junior : il choisit le modèle (« Mistral Large »). Le piège du senior évité : il choisit d'abord le plan de contrôle (où tourne le compute, qui signe le DPA, qui détient les clés de chiffrement), puis le modèle est un détail interchangeable derrière une interface OpenAI-compatible.
Le tokenizer : la variable cachée qui casse ton ROI
Le coût réel d'un appel n'est pas prix_$/M, c'est prix_$/M × tokens_réels. Or le tokenizer Mistral (Tekken/v3) découpe le français en ~20-30 % de tokens de plus que cl100k/o200k d'OpenAI sur le même texte. Conséquence : un modèle « moins cher au token » peut coûter plus cher au mot français. Toujours benchmarker en coût / 1000 mots FR de ton corpus, jamais en $/M tokens affiché. C'est exactement le genre de chiffre qu'un client te demande de défendre.
API managée vs self-host : le vrai arbitrage TCO
« Self-host parce que souverain » est un réflexe de junior. Une API EU (Scaleway/OVH) est souveraine et coûte zéro CAPEX. Le self-host ne se justifie que sous trois pressions : air-gap imposé (Diffusion Restreinte/SecNumCloud), volume si élevé que l'amortissement GPU bat le prix au token, ou besoin de fine-tuning/quantization custom. La table que tu poses devant le client :
| Critère | API managée EU (Scw/OVH) | Self-host vLLM (on-prem/VPC) |
|---|---|---|
| CAPEX | 0 | 80-200k€ (cluster H100/H200) |
| OPEX | facturé au token, élastique | 6-25k€/mois GPU + équipe MCO |
| Time-to-prod | jours | semaines (capacity, MCO, monitoring) |
| Scale élastique | oui (provider absorbe le pic) | non — tu provisionnes le pic toi-même |
| Air-gap / DR-SecNumCloud | non (sort vers l'endpoint) | oui — seule option conforme |
| Fine-tune / quantization | limité à l'offre provider | total (LoRA, AWQ/GPTQ/FP8) |
| Point de bascule typique | < ~50-100M tok/mois | > ~quelques centaines de M tok/mois ou contrainte légale d'air-gap |
Le raisonnement senior : le break-even n'est presque jamais le coût au volume d'un PME/ETI — c'est une contrainte légale (air-gap) ou un besoin produit (fine-tune) qui force le self-host. Si le seul argument est « moins cher au token », recalcule en incluant le salaire de l'équipe MCO 24/7 et l'amortissement GPU sur 3 ans : l'API gagne presque toujours sous le million de tokens/jour.
Code minimal
# pip install mistralai
from mistralai import Mistral
import os
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) # endpoint api.mistral.ai
resp = client.chat.complete(
model="mistral-large-2-2026",
messages=[
{"role": "system", "content": "Tu es un assistant juridique français."},
{"role": "user", "content": "Explique le RGPD article 6 en 3 phrases."},
],
temperature=0.0,
)
print(resp.choices[0].message.content)
# Tool calling
tools = [{
"type": "function",
"function": {
"name": "search_jurisprudence",
"description": "Recherche jurisprudence",
"parameters": {
"type": "object",
"properties": {"keyword": {"type": "string"}},
"required": ["keyword"],
},
},
}]
resp = client.chat.complete(
model="mistral-large-2-2026",
messages=[{"role": "user", "content": "Cherche jurisprudence licenciement"}],
tools=tools,
tool_choice="auto",
)Hébergement souverain via Scaleway Generative APIs :
from openai import OpenAI # OpenAI-compatible interface
client = OpenAI(
base_url="https://api.scaleway.ai/v1",
api_key=os.environ["SCALEWAY_API_KEY"],
)
resp = client.chat.completions.create(
model="mistral-large-2-2026", # même modèle, mais inference 100% FR
messages=[{"role": "user", "content": "Bonjour"}],
)OVHcloud AI Endpoints :
client = OpenAI(
base_url="https://oai.endpoints.kepler.ai.cloud.ovh.net/v1",
api_key=os.environ["OVH_AI_KEY"],
)
resp = client.chat.completions.create(
model="Mistral-Small-3-Instruct",
messages=[{"role": "user", "content": "Bonjour"}],
)Cas d'usage concrets
Santé — RAG sur dossiers patients HDS-compliant (clinique privée 6 sites)
Problème : groupe de 6 cliniques privées, 240 médecins, 80 000 patients actifs. Les médecins perdent 30-45 min/jour à chercher dans le DPI (dossier patient informatisé) : antécédents, examens, traitements, allergies. Aucune solution US/cloud non-HDS n'est utilisable (CNIL + HDS strict).
Solution : RAG souverain 100% FR, hébergé chez Scaleway (Datacenter Paris DC5, certifié HDS), Mistral Large 2 + Mistral Embed, pgvector PostgreSQL HDS, S3 FR pour stockage docs. DPA Mistral + Scaleway signés. Logs d'accès médecin pour audit CNIL.
# infra: Scaleway DC5 Paris, certifié HDS
# DB: Postgres + pgvector, HDS-compliant
# Models: mistral-large-2-2026 + mistral-embed-2025
class PatientRAG:
def __init__(self):
self.mistral = Mistral(api_key=os.environ["MISTRAL_HDS_API_KEY"])
self.db = psycopg.connect(os.environ["DPI_DB_URL"]) # VPC privé
def search(self, patient_id: str, query: str, medecin_id: str) -> str:
# Audit log obligatoire HDS
self._audit_log(medecin_id, patient_id, query)
# Embed query
q_vec = self.mistral.embeddings.create(
model="mistral-embed-2025",
inputs=[query],
).data[0].embedding
# Vector search scoped to patient
with self.db.cursor() as cur:
cur.execute(
"SELECT chunk_text, doc_type, doc_date FROM patient_chunks "
"WHERE patient_id = %s "
"ORDER BY embedding <=> %s::vector LIMIT 8",
(patient_id, q_vec),
)
chunks = cur.fetchall()
context = "\n\n".join(f"[{dt.date()}/{typ}] {txt}" for txt, typ, dt in chunks)
resp = self.mistral.chat.complete(
model="mistral-large-2-2026",
messages=[
{"role": "system", "content": MEDICAL_RAG_PROMPT},
{"role": "user", "content": f"<dossier_patient>{context}</dossier_patient>\n\n<question>{query}</question>"},
],
temperature=0.0,
)
return resp.choices[0].message.content
def _audit_log(self, medecin_id: str, patient_id: str, query: str):
with self.db.cursor() as cur:
cur.execute(
"INSERT INTO hds_audit_logs (timestamp, medecin_id, patient_id, action, query) "
"VALUES (now(), %s, %s, 'rag_query', %s)",
(medecin_id, patient_id, query),
)Gains chiffrés :
- Temps recherche dossier : 35 min → 90 sec
- 240 médecins × 35 min × 250 j = 35 000h/an libérées
- À valeur médicale (200€/h) = capacité ~7M€ (en pratique, soulage la pression staffing)
- Coût IA : ~0.02€/requête, ~1500 requêtes/j → 15€/j = 5 500€/an
- Coût Scaleway HDS infra : ~2 200€/mois
- TJM mission : 1 500€/j × 60 j (HDS, RGPD, audit DPO, intégration HL7/FHIR) = 90 000€ + 4 500€/mois MCO
Souveraineté — Assistant interne ministère / collectivité (DSI ministère ~2000 agents)
Problème : DSI d'un ministère, 2000 agents, classification documentaire confidentielle Défense, RGPD strict. Besoin d'un copilote interne (résumés notes, rédaction circulaires, recherche dans réglementation interne). Toute solution US bannie par instruction SGDSN.
Solution : Mistral Medium 3 hébergé on-prem (cluster GPU H200), pas de sortie internet, RAG sur corpus interne (BO, circulaires, jurisprudence administrative). Pas d'API externe. Tout en VPN interne.
# Self-hosted Mistral Medium 3 via vLLM
# pip install vllm
# Lancement serveur (sur machine GPU on-prem) :
# vllm serve mistralai/Mistral-Medium-3-Instruct \
# --tensor-parallel-size 4 \
# --max-model-len 32768 \
# --port 8000
from openai import OpenAI
client = OpenAI(
base_url="http://gpu-cluster.intra.gouv.fr:8000/v1",
api_key="EMPTY", # vLLM accepte tout en local
)
def assistant_ministere(prompt: str, classification: str) -> str:
assert classification in ("NP", "DR"), "Pas de niveau supérieur en clair"
resp = client.chat.completions.create(
model="mistralai/Mistral-Medium-3-Instruct",
messages=[
{"role": "system", "content": MINISTERE_PROMPT},
{"role": "user", "content": prompt},
],
temperature=0.0,
)
return resp.choices[0].message.contentGains chiffrés :
- Rédaction note de service : 2h → 30 min
- Recherche réglementaire : 1h → 5 min
- Agents 2000 × 4h/sem économisées = 8 000h/sem capacité libérée
- Coût infra GPU on-prem : ~180 000€ CAPEX + 25 000€/mois OPEX
- TJM mission : 1 500€/j × 90 j (RSSI, audit ANSSI, MCO sécu) = 135 000€ + 6 000€/mois MCO
Banque privée — Alternative quand client refuse OpenAI/Anthropic (banque privée FR, 80 banquiers privés)
Problème : banque privée, gestion de fortune. Compliance refuse catégoriquement toute solution non-EU pour les use cases qui touchent à la donnée client (synthèse RDV, drafting recommandations placement, audit conformité MIFID II). Mais le métier a vu ChatGPT et veut ses gains de productivité.
Solution : pile Mistral via Le Chat Enterprise (offre privée Mistral, infrastructure EU, DPA renforcé, BCR signé). Connecteurs Outlook, SharePoint, CRM interne, gardés en VPC EU.
# Le Chat Enterprise via API privée Mistral
client = Mistral(
api_key=os.environ["MISTRAL_ENTERPRISE_KEY"],
server_url="https://api-enterprise-fr.mistral.ai", # endpoint EU dédié
)
# Use case 1: synthèse RDV banquier
def synthese_rdv(transcript: str, client_id: str) -> dict:
resp = client.chat.complete(
model="mistral-large-2-2026",
messages=[
{"role": "system", "content": RDV_SYNTHESE_PROMPT},
{"role": "user", "content": transcript},
],
response_format={"type": "json_object"},
temperature=0.1,
)
return json.loads(resp.choices[0].message.content)
# Use case 2: contrôle MIFID II sur reco
def audit_mifid(reco_text: str, profile_client: dict) -> dict:
resp = client.chat.complete(
model="mistral-large-2-2026",
messages=[
{"role": "system", "content": MIFID_AUDIT_PROMPT},
{"role": "user", "content": f"Profil: {profile_client}\nReco: {reco_text}"},
],
response_format={"type": "json_object"},
temperature=0.0,
)
return json.loads(resp.choices[0].message.content)Gains chiffrés :
- Synthèse RDV : 25 min manuelle → 2 min validation
- 80 banquiers × 4 RDV/sem × 23 min = 7 360h/an
- Audit MIFID II avant envoi reco = 0 sanction ACPR (un cas évité ~ 50k€-2M€)
- Coût Le Chat Enterprise : ~6 000€/mois (license + infra dédiée)
- TJM mission : 1 500€/j × 50 j (compliance + intégration MIFID + DSI) = 75 000€ + 4 000€/mois MCO
Exemple end-to-end
RAG souverain complet : pgvector hébergé Scaleway, Mistral Large + Mistral Embed, conforme RGPD/HDS.
"""
sovereign_rag.py — RAG 100% souverain FR.
Pile:
- Scaleway DC5 Paris (HDS) — compute + DB
- Postgres + pgvector — vectorstore (chiffré at rest)
- Mistral Embed 2025 — embeddings
- Mistral Large 2 2026 — generation
- Audit logs (CNIL/HDS compliant)
- Pas de sortie hors UE
"""
from __future__ import annotations
import os
import hashlib
import json
from dataclasses import dataclass
from datetime import datetime
import psycopg
from psycopg.types.json import Json
from mistralai import Mistral
from pypdf import PdfReader
import tiktoken
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
DB_URL = os.environ["SCALEWAY_PG_URL"] # postgresql://[email protected]/
# ---------- 1. Schema DB ----------
SCHEMA = """
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS documents (
id BIGSERIAL PRIMARY KEY,
sha256 TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
org_id TEXT NOT NULL,
confidentiality TEXT NOT NULL DEFAULT 'standard',
ingested_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS chunks (
id BIGSERIAL PRIMARY KEY,
document_id BIGINT REFERENCES documents(id) ON DELETE CASCADE,
chunk_idx INT NOT NULL,
text TEXT NOT NULL,
n_tokens INT NOT NULL,
embedding VECTOR(1024) NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX IF NOT EXISTS chunks_embedding_idx
ON chunks USING hnsw (embedding vector_cosine_ops);
CREATE TABLE IF NOT EXISTS audit_logs (
id BIGSERIAL PRIMARY KEY,
ts TIMESTAMPTZ NOT NULL DEFAULT now(),
user_id TEXT NOT NULL,
org_id TEXT NOT NULL,
action TEXT NOT NULL,
payload JSONB NOT NULL DEFAULT '{}'
);
"""
def init_db():
with psycopg.connect(DB_URL) as conn:
with conn.cursor() as cur:
cur.execute(SCHEMA)
# ---------- 2. Embedder ----------
enc = tiktoken.get_encoding("cl100k_base") # approx token count
def chunk_text(text: str, max_tok: int = 500, overlap: int = 80) -> list[str]:
tokens = enc.encode(text)
chunks = []
i = 0
while i < len(tokens):
chunks.append(enc.decode(tokens[i : i + max_tok]))
i += max_tok - overlap
return chunks
def embed_batch(texts: list[str]) -> list[list[float]]:
# mistral-embed: 1024 dim
resp = client.embeddings.create(
model="mistral-embed-2025",
inputs=texts,
)
return [d.embedding for d in resp.data]
# ---------- 3. Ingest ----------
def ingest_pdf(pdf_path: str, org_id: str, confidentiality: str = "standard") -> int:
raw = open(pdf_path, "rb").read()
sha = hashlib.sha256(raw).hexdigest()
reader = PdfReader(pdf_path)
pages_text = [p.extract_text() or "" for p in reader.pages]
full_text = "\n".join(pages_text)
chunks = chunk_text(full_text, max_tok=500, overlap=80)
with psycopg.connect(DB_URL) as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO documents (sha256, title, metadata, org_id, confidentiality) "
"VALUES (%s, %s, %s, %s, %s) "
"ON CONFLICT (sha256) DO UPDATE SET title=EXCLUDED.title RETURNING id",
(sha, os.path.basename(pdf_path), Json({"n_pages": len(pages_text)}), org_id, confidentiality),
)
doc_id = cur.fetchone()[0]
cur.execute("DELETE FROM chunks WHERE document_id=%s", (doc_id,))
# Batch embed (Mistral accepte ~50/req)
for batch_start in range(0, len(chunks), 50):
batch = chunks[batch_start : batch_start + 50]
vecs = embed_batch(batch)
for j, (text, vec) in enumerate(zip(batch, vecs)):
idx = batch_start + j
cur.execute(
"INSERT INTO chunks (document_id, chunk_idx, text, n_tokens, embedding) "
"VALUES (%s, %s, %s, %s, %s)",
(doc_id, idx, text, len(enc.encode(text)), vec),
)
conn.commit()
return doc_id
# ---------- 4. Search ----------
@dataclass
class SearchHit:
doc_id: int
chunk_idx: int
text: str
score: float
doc_title: str
def search(query: str, org_id: str, user_clearance: str = "standard", k: int = 8) -> list[SearchHit]:
q_vec = embed_batch([query])[0]
clearance_order = {"standard": 0, "confidential": 1, "secret": 2}
max_level = clearance_order.get(user_clearance, 0)
allowed = [c for c, lvl in clearance_order.items() if lvl <= max_level]
with psycopg.connect(DB_URL) as conn:
with conn.cursor() as cur:
cur.execute(
"""SELECT c.document_id, c.chunk_idx, c.text,
1 - (c.embedding <=> %s::vector) AS score,
d.title
FROM chunks c
JOIN documents d ON d.id = c.document_id
WHERE d.org_id = %s
AND d.confidentiality = ANY(%s)
ORDER BY c.embedding <=> %s::vector
LIMIT %s""",
(q_vec, org_id, allowed, q_vec, k),
)
return [SearchHit(*r) for r in cur.fetchall()]
# ---------- 5. RAG answer ----------
RAG_PROMPT = """Tu es un assistant souverain pour [Organisation]. Tu réponds uniquement à partir des extraits fournis.
<rules>
- Si la réponse n'est pas dans les extraits, dis "Information non trouvée dans le corpus"
- Cite [doc:ID, chunk:IDX] après chaque affirmation factuelle
- Réponds en français
- N'invente AUCUNE information
</rules>"""
def answer(query: str, org_id: str, user_id: str, user_clearance: str = "standard") -> dict:
hits = search(query, org_id, user_clearance, k=8)
context = "\n\n---\n\n".join(
f"[doc:{h.doc_id}/{h.doc_title}, chunk:{h.chunk_idx}, score:{h.score:.2f}]\n{h.text}"
for h in hits
)
resp = client.chat.complete(
model="mistral-large-2-2026",
messages=[
{"role": "system", "content": RAG_PROMPT},
{"role": "user", "content": f"<contexte>\n{context}\n</contexte>\n\n<question>{query}</question>"},
],
temperature=0.0,
)
answer_text = resp.choices[0].message.content
audit_log(user_id, org_id, "rag_query", {
"query": query,
"hits": [{"doc_id": h.doc_id, "chunk_idx": h.chunk_idx, "score": h.score} for h in hits],
"tokens": {"in": resp.usage.prompt_tokens, "out": resp.usage.completion_tokens},
})
return {
"answer": answer_text,
"sources": [{"doc_id": h.doc_id, "title": h.doc_title, "chunk_idx": h.chunk_idx, "score": h.score} for h in hits],
}
def audit_log(user_id: str, org_id: str, action: str, payload: dict):
with psycopg.connect(DB_URL) as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO audit_logs (user_id, org_id, action, payload) VALUES (%s, %s, %s, %s)",
(user_id, org_id, action, Json(payload)),
)
# ---------- 6. CLI demo ----------
if __name__ == "__main__":
init_db()
ingest_pdf("./corpus/reglement_interieur.pdf", org_id="ACME-FR", confidentiality="standard")
ingest_pdf("./corpus/circulaire_secret.pdf", org_id="ACME-FR", confidentiality="confidential")
out = answer(
query="Quelles sont les règles de télétravail dans le règlement intérieur ?",
org_id="ACME-FR",
user_id="agent-42",
user_clearance="standard",
)
print(json.dumps(out, indent=2, ensure_ascii=False))Pile 100% FR, indexable, RGPD/HDS-ready, ~1500 lignes de code total avec les tests d'intégration. Ce code est ce que tu vends 90k€ à un client santé/banque/secteur public.
Patterns courants
1. Detection de la souveraineté requise (questionnaire client)
Questions à poser au premier RDV :
- Quelle est la donnée traitée ? (PII, santé, financier, défense)
- Où doit résider la donnée ? (UE, France, on-prem)
- DPA / BCR sont-ils acceptés du provider US ?
- Avez-vous un DPO ? Quelle position sur OpenAI/Anthropic ?
- Certifications requises ? (HDS, ISO 27001, SecNumCloud, PCI-DSS)
Si "santé/défense/CAC40 + DPO strict" → 90% du temps Mistral ou self-host.
2. Self-hosting via vLLM ou TGI
# vLLM, ~3 lignes pour servir Mistral Small 3
pip install vllm
vllm serve mistralai/Mistral-Small-3-Instruct \
--tensor-parallel-size 2 \
--max-model-len 32768 \
--port 8000
# OpenAI-compatible API: http://localhost:8000/v1vLLM > TGI en 2026 sur quasi tous les benchmarks de throughput. Pour réduire le GPU footprint : quantization. L'arbitrage que tu dois savoir défendre :
- FP8 (H100/H200 natif) : ~2× la mémoire/débit du BF16 pour une perte de qualité quasi nulle — le défaut raisonnable en 2026.
- AWQ / GPTQ (4-bit) : ~4× moins de VRAM, permet de faire tenir un Large 2 sur moins de GPU, mais perte mesurable sur le raisonnement long et le FR juridique précis. À benchmarker sur ton corpus avant de promettre un SLA qualité.
- Piège : quantizer pour économiser un GPU, puis perdre la vente parce que la qualité FR a chuté de 3 points sur l'eval client. La quantization se décide après le bench qualité, pas avant.
3. Wrapper unifié multi-provider
from litellm import completion
# Switche modèle sans changer le code
resp = completion(
model="mistral/mistral-large-2-2026", # ou "openai/gpt-5", "anthropic/claude-sonnet-4-6"
messages=[{"role": "user", "content": "..."}],
)Indispensable quand tu as un produit qui doit tourner sur Mistral pour clients EU et sur OpenAI pour clients US.
4. Embed avec Mistral Embed 2025
resp = client.embeddings.create(
model="mistral-embed-2025",
inputs=["texte 1", "texte 2"],
)
# 1024 dim, optimisé multilingue (FR très bon)Vs OpenAI text-embedding-3-large (3072 dim) : Mistral Embed ~10% moins bon sur MTEB EN, mais souvent meilleur sur FR/multilingue, et... il est utilisable légalement chez tes clients sensibles.
5. Pixtral pour la vision souveraine
resp = client.chat.complete(
model="pixtral-large-2026",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Extrais les données de cette facture"},
{"type": "image_url", "image_url": "https://..."},
],
}],
)Pour KYC EU, OCR factures FR, extraction documentaire — Pixtral est l'alternative souveraine à GPT-5 vision / Claude vision.
6. Codestral 2 pour assistance dev interne
Si le client veut un copilot dev interne (DSI bancaire qui ne peut pas envoyer son code chez GitHub Copilot) → Codestral 2 self-hosted via Continue / Cursor mode local.
Versions & écosystème 2026
| Modèle Mistral | Params | Context | Prix API ($/M in/out) | Open weights ? |
|---|---|---|---|---|
| Mistral Large 2 (2026) | 123B | 128K | $2 / $6 | Non |
| Mistral Medium 3 | 70B | 128K | $0.40 / $2 | Non |
| Mistral Small 3 | 24B | 32K | $0.10 / $0.30 | Oui (Apache 2.0) |
| Codestral 2 | 22B | 256K | $0.20 / $0.60 | Oui (research) |
| Pixtral Large 2026 | ~120B | 128K | $2 / $6 | Non |
| Pixtral 12B | 12B | 128K | $0.15 / $0.45 | Oui |
| Mistral Embed 2025 | — | — | $0.10/M | Non |
| Autres modèles open | Force | Hébergement |
|---|---|---|
| Llama 4 70B / 400B | Meta, multimodal | Bedrock, Together, self-host |
| Qwen 3 235B / 30B-MoE | Alibaba, strong code | Together, self-host |
| DeepSeek V3.2 / R1 | reasoning, cheap | DeepSeek API, self-host |
Hébergement souverain FR/EU :
- Scaleway Generative APIs (Mistral + open models, FR Paris DC, HDS dispo)
- OVHcloud AI Endpoints (Mistral, Llama, FR Gravelines)
- Mistral Le Chat Enterprise (offre privée Mistral, infra dédiée)
- Hugging Face Inference Endpoints (EU regions, mais provider Cloudflare/AWS attention)
- Together AI (US par défaut, EU sur contrat)
- Self-host VPC (vLLM/TGI sur Scaleway H100/H200)
Pitfalls
Croire que "Azure EU" = souverain — Cloud Act US s'applique. Vraie souveraineté = opérateur EU (Scaleway, OVH), pas filiale EU d'un cloud US.
Mistral Small 3 en prod sans benchmark FR → tu prends Llama 4 pour économiser, qualité FR moins bonne, retour client négatif. Bench sur ton use case avant.
Self-host sans plan capacity → un pic charge tue ton service. vLLM avec 1 H100 = 500-1500 tok/s en throughput batch. Multi-GPU + load balancing.
HDS = juste serveur "hébergé en France" — non. HDS = certification spécifique (~70 contrôles), hébergeur certifié, contrat HDS signé. Scaleway DC5 oui, mais tous les services Scaleway ne sont pas HDS.
Pas de DPA signé → tu utilises l'API Mistral pour des PII et tu n'as pas signé l'addendum RGPD article 28. Demande systématiquement.
Audit logs manquants → CNIL/DPO te demande qui a fait quoi quand, tu n'as rien. Logs dès le jour 1.
Token count Mistral vs OpenAI → Mistral tokenizer est ~20-30% plus verbeux sur FR. Le coût par mot est parfois plus haut que GPT-4.1 nano au final. Bench avant tarification.
Vector search lent à grande échelle → pgvector + index HNSW jusqu'à 1-5M chunks. Au-delà : Qdrant, Weaviate (peuvent être EU-hostés).
"Open weights" ≠ "RGPD safe" — si tu utilises le modèle via une API US, peu importe que les poids soient open. Self-host ou EU API.
Mistral API : retry pas natif — le SDK ne retry pas agressivement sur rate limit/5xx par défaut. Wrappe toi-même (voir section Production), configure le
retriesdu SDK, ou passe par litellm. Sans ça, un seul 429 du provider devient une 500 visible par l'utilisateur final.Fine-tuner par réflexe — le fine-tuning n'apprend pas des faits (c'est le job du RAG), il apprend un format/ton/style ou compresse un long system prompt. Fine-tuner pour « connaître le règlement intérieur » est un anti-pattern coûteux : le RAG fait mieux, moins cher, et reste à jour. Fine-tune seulement quand le format est figé et appelé des millions de fois (extraction structurée, classification métier).
Pricing / ROI client
Pile souveraine vs pile US (1M tokens/mois) :
| Item | Mistral / Scaleway | OpenAI / Anthropic |
|---|---|---|
| Modèle (Large 2 vs Sonnet 4.6) | $2 + $6 = ~$8/M | $3 + $15 = ~$18/M |
| Hébergement DB FR (Scw pg-hds) | ~250€/mois | 0 (mais data ailleurs) |
| DPA / contrats | Standard inclus | Souvent en option entreprise |
| Conformité (CNIL/HDS) | OK natif | Compliqué |
Note comparaison : côté US le flagship 2026 est Claude Opus 4.8 (
claude-opus-4-8, ~$5/M in, $25/M out à 1M de contexte), le milieu de gammeclaude-sonnet-4-6, et le low-costclaude-haiku-4-5(~$1/$5). Le tableau ci-dessus compare Mistral Large 2 à Sonnet 4.6 (le point de comparaison « qualité ÷ prix » le plus honnête) ; face à Opus 4.8 l'écart de prix se creuse encore mais c'est de toute façon hors-sujet sur un appel d'offres souverain où l'US est disqualifié.
Quand le client est "souveraineté-requis", la concurrence US n'existe pas → tu fixes le prix. Marges typiques :
- Setup pile RAG souverain : 80-120k€ (60-90 jours)
- MCO + évolution + audit annuel : 30-50k€/an
- TJM 1 300-1 500€/j
Pitch type : "Je vous livre un assistant IA conforme HDS/CNIL en 12 semaines. Aucune donnée patient/client ne sort de France. Si l'audit DPO ne passe pas, je vous rembourse" → ce niveau d'engagement vaut TJM premium.
Testing / Eval
Bench FR spécifique
Mistral et Claude sont souvent au coude-à-coude en FR, OpenAI un poil derrière, Llama / Qwen plus loin. Mais ça dépend du domaine (juridique, médical, finance). Bench sur ton corpus.
import litellm
models = [
"mistral/mistral-large-2-2026",
"openai/gpt-5",
"anthropic/claude-sonnet-4-6",
"together_ai/meta-llama/Llama-4-70B-Instruct",
]
eval_set = json.load(open("eval_set_fr_legal.json")) # 100 cas annotés
results = {}
for m in models:
scores = []
for case in eval_set:
out = litellm.completion(model=m, messages=case["messages"]).choices[0].message.content
scores.append(judge(out, case["expected"]))
results[m] = sum(scores) / len(scores)
print(results)Validation conformité
Check-list à passer avec le DPO client :
- [ ] Contrat / DPA signé avec provider
- [ ] Datacenter géolocalisé EU (preuve)
- [ ] Chiffrement at rest + in transit
- [ ] Audit logs activés et conservés N années
- [ ] Procédure droit à l'oubli (suppression embeddings + chunks)
- [ ] Sous-traitants déclarés (article 28 RGPD)
- [ ] PIA / analyse d'impact réalisée
Quand utiliser / éviter
Utilise Mistral / open / souverain quand :
- Client EU avec contraintes RGPD strictes (santé, finance, défense, secteur public)
- Client refuse explicitement OpenAI/Anthropic (BPI, ministères, certaines banques)
- Tu as un volume très élevé et besoin de fine-tuning (Mistral Small 3 fine-tunable)
- Tu veux du on-prem (les seuls qui te le permettent vraiment)
- Le FR est ton focus principal
Évite (ou complète) quand :
- Pas de contrainte souveraineté + tu veux le top qualité → Claude Sonnet 4.6 / GPT-5 souvent meilleurs
- Vision avancée requise → Claude / GPT-5 vision > Pixtral sur l'instant
- Très long context (>128K) → Claude / Gemini
- Tu veux Realtime audio bidirectionnel → OpenAI Realtime (pas d'équivalent souverain mûr en mai 2026)
Production : résilience & observabilité
Le SDK Mistral (comme noté en Pitfall #10) ne retry pas par défaut. En prod souveraine, tu DOIS wrapper. Pattern minimal serveur (async, backoff, timeout, log usage) :
import asyncio, logging, time
from mistralai import Mistral
from mistralai.models import SDKError # erreurs typées du SDK
log = logging.getLogger("sov-llm")
async def chat_resilient(client: Mistral, **kwargs) -> str:
"""Retry exponentiel sur 429/5xx, timeout par appel, log coût."""
delays = [0.5, 1, 2, 4] # backoff; le dernier échec remonte
for attempt, delay in enumerate([*delays, None]):
t0 = time.monotonic()
try:
resp = await asyncio.wait_for(
client.chat.complete_async(**kwargs),
timeout=30.0,
)
u = resp.usage
log.info("ok model=%s in=%d out=%d latency_ms=%d",
kwargs["model"], u.prompt_tokens, u.completion_tokens,
int((time.monotonic() - t0) * 1000))
return resp.choices[0].message.content
except (SDKError, asyncio.TimeoutError) as e:
status = getattr(e, "status_code", None)
retryable = status in (429, 500, 502, 503, 504) or isinstance(e, asyncio.TimeoutError)
if delay is None or not retryable:
log.error("giving up attempt=%d err=%s", attempt, e)
raise
log.warning("retry attempt=%d in=%.1fs err=%s", attempt, delay, e)
await asyncio.sleep(delay)Les trois métriques à exporter (Prometheus / OpenTelemetry) dès le jour 1 : tokens in/out par requête (coût), latence p50/p95/p99 (SLA), taux d'erreur par code (429 = capacity, 5xx = provider). En self-host vLLM, ajoute gpu_cache_usage_perc et num_requests_waiting : ce sont eux qui prédisent l'effondrement de débit avant qu'il n'arrive.
Pour les sorties structurées (synthèse RDV, audit MIFID), préfère un schéma fort plutôt que json_object brut : Mistral supporte response_format avec un JSON Schema ({"type": "json_schema", "json_schema": {...}}), ce qui contraint le décodage côté serveur et supprime les json.loads qui plantent. C'est l'équivalent souverain du client.messages.parse() Pydantic côté Claude.
Caching du préfixe stable (le levier coût qu'on oublie)
Sur un RAG souverain, le system prompt (règles, format, persona juridique/médical) et la définition des tools sont identiques à chaque appel — c'est du préfixe stable que tu paies plein tarif des milliers de fois par jour si tu ne fais rien. Deux régimes selon l'hébergement :
- API managée (Mistral / Scaleway / OVH) : active le prompt caching côté provider quand il est exposé, et surtout ordonne tes messages préfixe-stable d'abord, variable ensuite (system + tools + contexte figé, puis la question utilisateur). C'est la même discipline que
cache_controlsur le préfixe system+tools côté Claude : le cache ne marque que ce qui ne bouge pas. - Self-host vLLM : l'automatic prefix caching (
--enable-prefix-caching) réutilise le KV-cache des préfixes partagés entre requêtes. Sur un RAG où 2000 agents partagent le même system prompt, c'est 20-40 % de tokens de prefill en moins, donc autant de débit GPU récupéré gratuitement. À mesurer viagpu_prefix_cache_hit_rate.
Règle senior : un préfixe non caché, c'est un chiffre que ton client peut te demander de défendre en audit de coût. Tu veux pouvoir répondre « 35 % de cache hit, voici le dashboard ».
🏋️ Exercices
Progressifs. Chacun part d'un livrable concret et finit en « rends-le production-grade / casse-le puis répare ».
Exercice 1 — Coût réel au mot FR (défends le chiffre)
Objectif : prouver, chiffres en main, si Mistral Large 2 est plus ou moins cher que Claude Sonnet 4.6 sur ton corpus FR.
Prends 500 documents FR réels (juridiques ou médicaux anonymisés). Tokenise chacun avec le tokenizer Mistral et avec o200k_base. Calcule le coût total input pour chaque pile aux prix réels (Mistral Large $2/M in, Sonnet $3/M in). Rends un tableau € / 1000 mots FR et la conclusion.
Indice/Solution : le ratio tokens Mistral/OpenAI tourne autour de 1,2-1,3 sur le FR ; à $2 vs $3, Mistral reste moins cher en input, mais en output ($6 vs $15) l'écart explose en faveur de Mistral. Le vrai twist : si tu génères peu et lis beaucoup (RAG), le ratio tokenizer peut annuler l'avantage prix. Le livrable senior n'est pas le chiffre, c'est la phrase « voici la zone où basculer ».
Exercice 2 — Fais respecter le clearance (casse-le puis répare)
Objectif : prouver que ton RAG souverain ne fuit pas un chunk confidential vers un user standard.
Reprends le sovereign_rag.py end-to-end. Ingère un doc confidential contenant un secret canari (« code projet ZEPHYR »). Écris un test qui interroge en tant que user standard avec une question conçue pour faire remonter ZEPHYR. Vérifie qu'il ne sort jamais.
Indice/Solution : le filtre confidentiality = ANY(allowed) protège au niveau SQL — bien. Le piège : un chunk standard qui cite le secret (fuite par contamination d'ingestion), ou un cache d'embeddings partagé. Casse-le en mettant ZEPHYR dans un doc standard ; répare par un scan PII/canari à l'ingestion + un RAG_PROMPT qui ne peut citer que les sources passées. Bonus : ajoute un audit log qui aurait détecté la fuite a posteriori.
Exercice 3 — Capacity planning self-host (défends le SLA)
Objectif : dimensionner un cluster vLLM pour 2000 agents et garantir p95 < 3 s.
Sers Mistral-Small-3-Instruct en vLLM sur 1 GPU. Charge-le avec vllm bench (ou locust) en montant la concurrence jusqu'à ce que p95 dépasse 3 s. Note le débit (tok/s) et num_requests_waiting au point de rupture. Extrapole : combien de GPU pour 2000 agents à 4 requêtes/h en heure de pointe ?
Indice/Solution : le débit batché d'un H100 sur Small 3 est de l'ordre de 1000-2500 tok/s mais s'effondre quand gpu_cache_usage_perc → 100 % (préemption). Le bon raisonnement : capacité = débit_soutenable / tokens_moyens_par_requête, avec marge ×2 pour le pic et N+1 pour la panne GPU. La mauvaise réponse classique : dimensionner sur le débit crête mono-requête.
Exercice 4 — Bascule de provider sous panne (production-grade)
Objectif : ton API Mistral EU renvoie des 503 en rafale ; bascule sur OVH AI Endpoints sans perdre de requête ni violer la souveraineté.
Implémente un routeur (litellm ou maison) avec failover Scaleway → OVH → self-host, tous EU. Injecte des 503 artificiels sur le primaire. Mesure le taux de requêtes servies et vérifie qu'aucun fallback ne sort de l'UE.
Indice/Solution : le failover est facile ; le piège souverain est que le fallback « par défaut » de beaucoup de routeurs est une région US. Pin explicitement chaque endpoint, et écris un test qui échoue si un endpoint non-EU apparaît dans la chaîne. Ajoute un circuit breaker (n'inonde pas un provider déjà mort) et propage le request_id pour l'audit.
Exercice 5 — Le PIA qui ferme la vente (défends devant le DPO)
Objectif : rédiger l'analyse d'impact (PIA) qui fait passer ton RAG santé devant un DPO hostile.
À partir de l'architecture HDS du cas santé, produis un PIA d'une page : finalité, base légale RGPD, sous-traitants (art. 28), durée de conservation des embeddings, procédure de droit à l'oubli (suppression chunks + ré-index), localisation prouvée, et le risque résiduel #1.
Indice/Solution : le point que 90 % des candidats ratent — le droit à l'oubli sur les embeddings. Supprimer la ligne documents ne suffit pas si des copies de vecteurs traînent en cache, en backup, ou dans un index HNSW non recompacté. La réponse senior : suppression transactionnelle (CASCADE), purge des backups selon rétention contractuelle, et journal de preuve de suppression. Sans ça, le DPO refuse.
Exercice 6 — Fine-tune ou RAG ? (défends le « non »)
Objectif : décider, chiffres en main, si le client santé doit fine-tuner Mistral Small 3 sur son corpus DPI, ou rester sur RAG — et savoir défendre le refus.
Le client demande « fine-tunez le modèle sur nos 80 000 dossiers patients ». Construis le contre-argument quantifié : coût d'un fine-tune (jeu d'entraînement annoté + GPU + ré-entraînement à chaque mise à jour du corpus), vs le RAG existant. Puis identifie le seul cas où fine-tuner gagne ici, et chiffre-le.
Indice/Solution : fine-tuner pour « connaître les dossiers » est l'anti-pattern — les faits changent quotidiennement, le modèle fine-tuné est périmé à la livraison et ne cite pas ses sources (rédhibitoire en santé/audit). Le RAG gagne sur fraîcheur, coût, traçabilité, droit à l'oubli (tu supprimes un chunk, pas tu ré-entraînes). Le seul cas où le fine-tune gagne : compresser un system prompt énorme et figé (format de synthèse médicale standardisé) appelé des millions de fois — là tu fine-tunes le format, jamais les faits, et tu chiffres l'économie de tokens de prefill vs le coût d'entraînement amorti. Livrable senior : la phrase « on fine-tune le ton, on RAG les faits ».
🎤 En entretien
« Azure OpenAI est hébergé en Europe, n'est-ce pas suffisant pour un client santé FR ? » Non : le Cloud Act US permet à l'autorité US d'exiger les données d'une filiale US même hébergées en UE ; la vraie souveraineté exige un opérateur de droit EU (Scaleway/OVH) ou du self-host, pas une région EU d'un cloud américain.
« Mistral Large est moins cher que GPT-5 au token. Donc moins cher tout court ? » Pas forcément : le tokenizer Mistral est ~20-30 % plus verbeux sur le FR, donc je benchmarke en
€/1000 mots FR de leur corpus, pas en$/Maffiché — et la conclusion dépend du ratio lecture/génération du use case.« Open weights, ça veut dire RGPD-safe ? » Non : les poids ouverts ne disent rien du plan de contrôle ; appeler un modèle open via une API US transfère quand même la donnée hors UE. RGPD-safe = self-host ou API opérée par un acteur EU, indépendamment de la licence des poids.
« Comment garantis-tu le droit à l'oubli dans un RAG vectoriel ? » Suppression transactionnelle en CASCADE (document → chunks → embeddings), purge/expiration des backups selon la rétention contractuelle, recompaction de l'index, et un journal de preuve — supprimer la source sans purger les vecteurs et caches est la faille la plus courante.
« Ton RAG fait 2000 agents avec le même system prompt de 1500 tokens. Où passe ton budget ? » Dans le prefill répété si je ne cache pas le préfixe stable : en self-host j'active l'automatic prefix caching de vLLM (réutilisation du KV-cache, hit rate observable), en API managée j'ordonne system+tools+contexte figé en tête pour maximiser le cache provider — concrètement 20-40 % de tokens de prefill économisés, donc autant de débit GPU ou de facture en moins.
« Le client veut self-host pour économiser. Tu valides ? » Rarement sur le seul argument coût : sous le million de tokens/jour, une API EU bat le self-host une fois amortis le CAPEX GPU (3 ans) et le salaire de l'équipe MCO 24/7. Le self-host se justifie par une contrainte légale (air-gap Diffusion Restreinte/SecNumCloud) ou un besoin de fine-tune/quantization custom — pas par le prix au token.
« Faut-il fine-tuner Mistral sur les données métier du client ? » On fine-tune le format/ton, jamais les faits : pour des connaissances qui changent, le RAG est plus frais, traçable (citations), moins cher et compatible droit à l'oubli. Le fine-tune ne gagne que pour figer un format appelé des millions de fois ou compresser un system prompt énorme.
Liens
- Mistral docs : https://docs.mistral.ai/
- Mistral models : https://docs.mistral.ai/getting-started/models/models_overview/
- Le Chat Enterprise : https://mistral.ai/products/le-chat
- Scaleway Generative APIs : https://www.scaleway.com/en/generative-apis/
- OVHcloud AI Endpoints : https://endpoints.ai.cloud.ovh.net/
- Hugging Face Inference Endpoints : https://huggingface.co/inference-endpoints
- vLLM : https://docs.vllm.ai/
- LiteLLM : https://docs.litellm.ai/
- HDS référentiel : https://esante.gouv.fr/produits-services/hds
- SecNumCloud référentiel ANSSI : https://cyber.gouv.fr/secnumcloud
- CNIL — IA : https://www.cnil.fr/fr/intelligence-artificielle