Un assistant financier IA local combine un pipeline Python de normalisation, des modèles ML (ex. Isolation Forest) et un LLM local (Ollama) pour analyser relevés et générer explications tout en gardant vos données sur votre machine. Je détaille l’architecture, le pipeline, le ML et l’intégration LLM.
Pourquoi construire un assistant financier IA local
La confidentialité des données bancaires n’est pas un détail technique : c’est une exigence juridique et une confiance client. Les relevés, les habitudes de dépense et les soldes forment un profil financier extrêmement sensible qui, s’il fuit ou est mal utilisé, entraîne des risques de fraude, d’usurpation d’identité et des conséquences réglementaires.
Les applications cloud centralisées facilitent l’accès et l’échelle, mais elles imposent l’envoi systématique de CSV et d’identifiants vers des serveurs tiers. Cette logique expose aux mauvaises configurations, aux fuites via des API et aux politiques commerciales imprévues concernant l’utilisation des données. Je privilégie un assistant local quand l’objectif principal est de minimiser ces vecteurs de risque et de garder un contrôle total sur les pipelines et les modèles.
Les modèles de langage locaux deviennent pratiques grâce à des solutions comme Ollama, qui simplifient le déploiement d’LLM sur machine locale ou serveur privé. Pour la détection d’anomalies sur transactions, des méthodes classiques et robustes existent, comme Isolation Forest (Liu et al., 2008), efficace pour isoler les transactions atypiques sans supervision exhaustive.
Critères de conception recherchés :
- Traitement 100% local : Aucun envoi de données sensibles vers le cloud afin de préserver la confidentialité et la conformité.
- Upload instantané de CSV : Interface simple pour importer rapidement des exports bancaires au format CSV sans étapes manuelles compliquées.
- Explications en langage naturel : Résumés et justification des décisions en français clair pour rendre l’analyse accessible non technique.
- Visualisations interactives : Graphiques et filtres pour explorer les dépenses, catégories et anomalies en temps réel.
- Modularité du pipeline : Composants séparés pour ingestion, nettoyage, détection d’anomalies et génération de texte afin de faciliter mises à jour et audits.
| Approche | Avantages | Contraintes |
| Cloud tout-en-un | Déploiement rapide, scalabilité, modèles gérés et mises à jour automatiques. | Confidentialité limitée, coûts récurrents, dépendance au fournisseur. |
| Hybride | Équilibre entre performance cloud et contrôle local des données sensibles. | Complexité d’architecture, nécessité de règles de routage des données. |
| 100% local | Confidentialité maximale, coûts prévisibles, contrôle total du modèle et des données. | Requiert ressources machines, gestion des mises à jour et expertise DevOps. |
Quelle architecture pour le projet
Réponse: Une architecture modulaire avec couches: interface (Streamlit), preprocessing, ML, visualisations et intégration LLM (Ollama) est adaptée.
App.py gère l’interface utilisateur via Streamlit, les formulaires d’upload, les contrôles de taille/filtres et orchestre les appels vers le preprocessing, les modèles ML, les visualisations et l’intégration LLM en streaming.
Config.py centralise les paramètres: endpoints Ollama locaux, port, chemins de stockage temporaire, seuils ML et options de logging, afin d’éviter les valeurs codées en dur et faciliter les tests.
Preprocessing.py détecte automatiquement les formats CSV (délimiteurs, encodage, présence d’en-têtes), normalise les types (dates, montants), gère les valeurs manquantes et produit un DataFrame prêt pour le ML.
Ml_models.py encapsule la logique de classification (ex: catégorisation des transactions) et de détection d’anomalies (ex: IsolationForest), expose des fonctions fit/predict et conserve les artefacts avec joblib pour réutilisation.
Visualizations.py fournit des fonctions Plotly réutilisables pour séries temporelles, heatmaps et distributions, retournant des composants embeddables par Streamlit pour une exploration interactive.
Llm_integration.py gère les échanges avec Ollama en streaming, construit le contexte utile (prompts + extraits de données agrégées), envoie les requêtes au modèle local et délivre la réponse token par token pour UX réactive.
Dossier sample_data contient exemples CSV annotés et README explique le démarrage, les endpoints et les formats attendus.
| app.py | Interface Streamlit, upload, contrôles et orchestration. |
| config.py | Paramètres, endpoints Ollama, chemins et seuils. |
| preprocessing.py | Détection auto CSV, parsing, normalisation et validation. |
| ml_models.py | Classification, anomalies, entraînement et persist. |
| visualizations.py | Fonctions Plotly pour graphiques interactifs. |
| llm_integration.py | Client Ollama local, streaming et gestion du contexte. |
| sample_data/ README.md | Exemples et instructions de démarrage. |
Étape 1: L’utilisateur charge un CSV via Streamlit et app.py vérifie le format et la taille.
Étape 2: Preprocessing normalise le DataFrame, détecte dates/monnaies et retourne un dataset propre.
Étape 3: Ml_models exécute classification et détection d’anomalies et renvoie scores et labels.
Étape 4: Visualizations génère graphiques interactifs affichés dans l’UI pour validation humaine.
Étape 5: Llm_integration construit un prompt concis (contexte + extraits agrégés) et envoie en streaming à Ollama.
Étape 6: Le modèle local répond en flux; app.py affiche la réponse et propose actions (export, rapport).
Exigences techniques: Python 3.10+, bibliothèques clés pandas, scikit-learn, plotly, streamlit, joblib et requests ou client Ollama.
Conseils sécurité: Restreindre les permissions fichiers, limiter la taille d’upload, chiffrer le stockage local des données sensibles, éviter les logs de données financières en clair et exécuter Ollama en environnement isolé avec firewall.
Comment concevoir un pipeline de prétraitement robuste
Normaliser les relevés dès l’import est essentiel: détecter colonnes, fusionner débit/crédit, parser montants et dates.
Commencez par détecter automatiquement les trois champs standard {date, description, amount} en combinant détection par nom de colonne (expressions régulières) et vérification des échantillons de données. Voici la logique pas à pas (pseudocode) :
Algorithme detect_columns(df):
1. Préparer motifs regex pour date, description, amount, debit, credit.
2. Pour chaque colonne:
a. Tester le nom de colonne avec chaque motif.
b. Si ambiguïté, échantillonner 10 valeurs:
- Si >60% parseables en date => classer date.
- Si >60% numériques => classer amount candidate.
- Si mix debit/credit (valeurs négatives et positives) => amount.
3. Si colonne debit et colonne credit détectées => prévoir fusion.
4. Retourner mapping vers {date, description, amount} et actions (fusion si nécessaire).
Exemple Python concret pour detect_columns(df) qui teste motifs regex et renvoie le mapping :
import re
import pandas as pd
import numpy as np
from dateutil import parser
DATE_PATTERNS = [r'date', r'date[_\s]transaction', r'dt']
DESC_PATTERNS = [r'description', r'libell', r'label', r'merchant', r'payee']
AMOUNT_PATTERNS = [r'amount', r'montant', r'transaction[_\s]amt', r'total']
DEBIT_PATTERNS = [r'debit', r'dbt', r'déb?it']
CREDIT_PATTERNS = [r'credit', r'cr', r'crédit']
def is_date_like(val):
try:
parser.parse(str(val), dayfirst=True)
return True
except Exception:
return False
def is_numeric_like(val):
try:
float(str(val).replace(' ', '').replace(',', '.').replace('(', '-').replace(')', ''))
return True
except:
return False
def detect_columns(df):
mapping = {}
for col in df.columns:
name = col.lower()
assigned = False
for pat in DATE_PATTERNS:
if re.search(pat, name):
mapping['date'] = col; assigned=True; break
if assigned: continue
for pat in DESC_PATTERNS:
if re.search(pat, name):
mapping['description'] = col; assigned=True; break
if assigned: continue
for pat in AMOUNT_PATTERNS+DEBIT_PATTERNS+CREDIT_PATTERNS:
if re.search(pat, name):
mapping.setdefault('amount_candidates', []).append(col)
assigned=True; break
if not assigned:
# probe values
sample = df[col].dropna().astype(str).head(10)
if sample.apply(is_date_like).sum() >= 6:
mapping['date'] = col
elif sample.apply(is_numeric_like).sum() >= 6:
mapping.setdefault('amount_candidates', []).append(col)
# Resolve amount: if debit & credit present, will merge
if 'amount_candidates' in mapping:
if len(mapping['amount_candidates'])==1:
mapping['amount']=mapping['amount_candidates'][0]
else:
# prefer combined debit/credit => create merged amount
mapping['amount']='__merge__:'+','.join(mapping['amount_candidates'])
return mapping
Exemple de normalisation pour fusionner debit/credit et parser montants :
def normalize_amount(val):
if pd.isna(val): return np.nan
s = str(val).strip()
s = s.replace(' ', '').replace("'",'').replace('€','')
if s.startswith('(') and s.endswith(')'):
s = '-' + s[1:-1]
s = s.replace(',','.') # gestion séparateur décimal
try:
return float(s)
except:
return np.nan
# Fusion si debit/credit
df['amount'] = np.nan
if mapping.get('amount','').startswith('__merge__'):
cols = mapping['amount'].split(':',1)[1].split(',')
# Supposer debit et credit; crédit positif, débit négatif
df['amount'] = df[cols[0]].apply(normalize_amount).fillna(0) - df[cols[1]].apply(normalize_amount).fillna(0)
else:
df['amount'] = df[mapping['amount']].apply(normalize_amount)
Dates: parser.parse(date_string, dayfirst=True) avec fallback vers formats explicites comme %Y-%m-%d, %d/%m/%Y si exception.
Créer des features basiques permet des modèles plus robustes: extraction MCC approximé via texte de description (règles ou embeddings légers), regroupement de libellés (hashing ou clustering), jour/semaine/mois (datetime.dt.day, .week, .month), montant absolu (abs(amount)), et fenêtres glissantes (rolling 7/30/90 jours: somme, moyenne, count).
| Colonne standard | Type attendu | Exemples |
| Date | Date / datetime | 2026-03-15, 15/03/2026 |
| Description | Texte | Boulangerie Dupont, AMAZON MARKETPLACE |
| Amount | Float (dépense négative) | -23.50, 1500.00 |
| Amount_abs | Float | 23.50, 1500.00 |
Quels modèles ML pour catégoriser et détecter anomalies
Pour petits jeux de données, privilégiez des modèles simples et robustes: RandomForest/Logistic pour classification de catégories et IsolationForest pour anomalies.
RandomForest est recommandé pour sa robustesse et sa capacité à gérer des features hétérogènes (numériques, catégoriques encodées) sans beaucoup de pré-traitement; Logistic Regression ou Ridge servent d’baseline rapide, interprétable et stable quand les données sont faibles. IsolationForest est adapté pour la détection d’anomalies car il isole les observations rares via des arbres aléatoires (Liu, Ting, Zhou, 2008).
Je fournis ci‑dessous des exemples concrets à intégrer dans ml_models.py.
# Exemples pour ml_models.py
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier, IsolationForest
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.metrics import f1_score, make_scorer
import numpy as np
# Pipeline pour RandomForest (baseline robuste)
numeric_transform = Pipeline([('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())])
# Adapter categorical si nécessaire (exemple)
preproc = ColumnTransformer([('num', numeric_transform, numeric_cols),
('cat', OneHotEncoder(handle_unknown='ignore'), categorical_cols)])
rf_pipeline = Pipeline([('preproc', preproc),
('clf', RandomForestClassifier(n_estimators=100, max_depth=None, random_state=42))])
# Cross-validation stratifiée avec F1
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
f1 = make_scorer(f1_score, average='weighted')
scores = cross_val_score(rf_pipeline, X, y, cv=cv, scoring=f1)
print('F1 CV mean:', np.mean(scores))
# IsolationForest pour anomalies
iso = IsolationForest(n_estimators=100, contamination='auto', random_state=42)
iso.fit(X_train) # X_train pré-traité numériquement
scores_iso = iso.score_samples(X_val) # score plus élevé = plus normal
threshold = np.percentile(scores_iso, 5) # seuil au 5ème percentile
anomalies = scores_iso < threshold
Validation raisonnée pour petits jeux de données.
Privilégiez Stratified K‑Fold pour conserver la distribution des classes. Regroupez catégories rares (data augmentation par regroupement), ou utilisez bootstrapping pour estimer variance des métriques. Évaluez seuils d’IsolationForest via courbes PR et validation humaine.
Conseils pratiques pour anomalies.
Ajustez le paramètre contamination ou choisissez un seuil percentile sur score_samples pour contrôler les faux positifs. Conservez une boucle de feedback humain pour labelliser faux positifs et réentraîner (active learning). Envisagez un vote d’ensembles (isolation + LOF) si le coût des faux positifs est élevé.
| Modèle | Usage | Avantages | Inconvénients |
| RandomForest | Classification catégories | Robuste, gère features hétérogènes, peu d'optim | Moins interprétable, overfit si très petit jeu |
| Logistic / Ridge | Baseline, cas linéaire | Simple, interprétable, stable | Performances limitées si relations non linéaires |
| IsolationForest | Détection d'anomalies | Conçu pour anomalies, scalable | Sensible au choix du seuil, faux positifs possibles |
Comment intégrer un LLM local et produire des visualisations interactives
Intégrer un LLM local (ex. Ollama) en streaming permet générer explications naturelles sans quitter la machine, et Plotly produit visualisations interactives embarquées dans Streamlit.
Pour l'intégration technique, envoyer au modèle un prompt qui contient un résumé des transactions, des embeddings/metadata (IDs, catégories, montants, timestamps) et récupérer la réponse en streaming permet une interface réactive.
Exemple de prompt envoyé au LLM :
Veuillez résumer les dépenses mensuelles à partir des transactions ci‑dessous.
Fournissez :
1) Total par catégorie pour le mois.
2) Liste des anomalies avec leurs IDs.
3) Recommandations d'optimisation (3 actions).
4) Explication courte (2-3 phrases) pour la transaction suspecte ID=txn_12345.
Transactions (JSON avec embeddings/metadata) : [{ "id":"txn_12345", "date":"2026-03-12", "amount":-1200, "category":"Abonnement", "merchant":"ServiceX", "embedding":[...]} ...]
Snippet Python minimal utilisant l'API Ollama en streaming (requests) :
import requests
url = "http://localhost:11434/api/generate"
payload = {"model":"ollama-model", "prompt":"", "stream":True}
with requests.post(url, json=payload, stream=True) as r:
for line in r.iter_lines(decode_unicode=True):
if line:
# Chaque ligne contient un jeton ou chunk JSON selon le modèle
print(line)
Pour Plotly, créer : camembert par catégories, histogramme des montants, timeline (scatter time vs amount), heatmap horaire (heure x jour).
Exemple Streamlit + Plotly et export PNG :
import streamlit as st
import plotly.express as px
from io import BytesIO
fig = px.pie(df, names='category', values='amount', title='Dépenses par catégorie')
st.plotly_chart(fig, use_container_width=True)
# Export PNG (kaleido requis)
buf = BytesIO()
fig.write_image(buf, format='png', engine="kaleido")
st.download_button("Télécharger PNG", data=buf.getvalue(), file_name="pie.png", mime="image/png")
Feuille de route de déploiement local rapide :
- Installer Ollama et télécharger le modèle local.
- Installer dépendances Python : pip install streamlit plotly requests kaleido.
- Lancer Streamlit : streamlit run app.py.
- Configurer sécurité locale : firewall, droits d'accès, chiffrement des données sensibles.
| Composant UI | Rôle |
| Zone Conversation LLM | Affiche résumé, anomalies, recommandations en streaming |
| Camembert | Vue partagée dépenses par catégorie |
| Timeline | Suivi chronologique des transactions |
| Heatmap Horaire | Révèle pics d'activité selon heure/jour |
Prêt à déployer votre assistant financier IA local pour garder le contrôle de vos données ?
Le parcours complet va du nettoyage automatique des CSV à la détection d'anomalies et à la génération d'explications en langage naturel via un LLM local. En standardisant tôt les formats, en privilégiant des modèles simples adaptés aux petits jeux de données et en intégrant Ollama pour le NL, vous obtenez un assistant privé, interactif et actionnable. Bénéfice direct: analyses exploitables sans exposer vos relevés au cloud.
FAQ
A propos de l'auteur
Franck Scandolera — expert & formateur en Tracking avancé server-side, Analytics Engineering, Automatisation No/Low Code (n8n) et intégration d'IA en entreprise. Responsable de l'agence webAnalyste et de l'organisme de formation Formations Analytics. Références clients: Logis Hôtel, Yelloh Village, BazarChic, Fédération Française de Football, Texdecor. Dispo pour aider les entreprises => contactez-moi.
⭐ Analytics engineer, Data Analyst et Automatisation IA indépendant ⭐
- Ref clients : Logis Hôtel, Yelloh Village, BazarChic, Fédération Football Français, Texdecor…
Mon terrain de jeu :
- Data Analyst & Analytics engineering : tracking avancé (GTM server, e-commerce, CAPI, RGPD), entrepôt de données (BigQuery, Snowflake, PostgreSQL, ClickHouse), modèles (Airflow, dbt, Dataform), dashboards décisionnels (Looker, Power BI, Metabase, SQL, Python).
- Automatisation IA des taches Data, Marketing, RH, compta etc : conception de workflows intelligents robustes (n8n, App Script, scraping) connectés aux API de vos outils et LLM (OpenAI, Mistral, Claude…).
- Engineering IA pour créer des applications et agent IA sur mesure : intégration de LLM (OpenAI, Mistral…), RAG, assistants métier, génération de documents complexes, APIs, backends Node.js/Python.






