Votre équipe de management passe des heures chaque mois à extraire manuellement les éléments d'action des rapports d'activité. "Correction de bug", "version 1.0 déployée", "refonte du module paiement". Ces tâches répétitives, sujettes aux erreurs et chronophages sont exactement le type de travail que l'IA peut automatiser.
Mais utiliser ChatGPT ou Claude pour traiter ces rapports internes pose un problème majeur : vous exposez vos données d'activité à des serveurs externes. Pour une entreprise soucieuse de confidentialité, c'est inacceptable.
La solution : un agent LLM local qui tourne entièrement sur vos serveurs. Aucune donnée ne quitte votre infrastructure. Cet article vous guide pas à pas dans la construction d'un tel système, avec intégration optionnelle à Jira pour une gestion de tâches automatisée de bout en bout.
Pourquoi un LLM local plutôt que le cloud
Sécurité des données
Les rapports mensuels contiennent des informations sensibles : noms de clients, montants de contrats, bugs de sécurité, décisions stratégiques. Envoyer ces données à OpenAI ou Anthropic, même via leurs API, implique un transfert de données hors de votre contrôle.
Avec un LLM local comme Llama 3, Mistral ou Phi-3, vos données restent sur votre infrastructure. Vous pouvez même tourner le modèle sur une machine air-gapped sans connexion internet.
Coûts prévisibles
Les API cloud facturent au token. Pour une entreprise traitant 50 rapports mensuels de 2000 mots chacun, cela représente environ 500 000 tokens par mois en input seul. À 3$ par million de tokens, le coût reste modeste. Mais si vous passez à l'échelle avec 500 rapports ou des analyses plus complexes, la facture explose.
Un LLM local a un coût fixe : le matériel et l'électricité. Une fois l'infrastructure en place, le coût marginal par rapport est quasi nul.
Latence et disponibilité
Pas de dépendance à la disponibilité des API externes. Pas de rate limiting. Pas de dégradation de performance aux heures de pointe. Votre système fonctionne même si OpenAI a une panne.
Architecture du système
L'architecture se décompose en quatre composants :
1. Serveur d'inférence LLM
Le coeur du système est un serveur qui héberge et exécute le modèle de langage. Les options populaires :
- Ollama : le plus simple à déployer, interface CLI et API REST
- vLLM : optimisé pour la production, meilleur débit
- llama.cpp : le plus léger, tourne sur CPU seul si nécessaire
Pour un déploiement PME, Ollama est recommandé. Installation sur Ubuntu :
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.1:8b
Le modèle Llama 3.1 8B offre un excellent compromis entre performance et ressources. Il tourne confortablement sur une machine avec 16 Go de RAM et une GPU avec 8 Go de VRAM.
2. Module d'extraction de texte
Les rapports arrivent souvent en PDF ou Word. Vous avez besoin d'un extracteur qui convertit ces formats en texte brut :
- PyPDF2 ou pdfplumber pour les PDF
- python-docx pour les fichiers Word
- Unstructured pour un traitement unifié multi-format
3. Agent d'extraction de tâches
C'est le composant intelligent qui utilise le LLM pour identifier et structurer les éléments d'action. L'agent reçoit le texte du rapport et produit une liste structurée de tâches avec :
- Description de la tâche
- Type (bug fix, feature, documentation, etc.)
- Priorité estimée
- Personne assignée (si mentionnée)
4. Intégration Jira (optionnel)
Pour automatiser complètement le workflow, l'agent peut créer des tickets Jira directement via l'API. Chaque tâche extraite devient un ticket avec les champs appropriés.
Implémentation pas à pas
Étape 1 : environnement Python
Créez un environnement virtuel et installez les dépendances :
python -m venv llm-agent
source llm-agent/bin/activate
pip install ollama pdfplumber python-docx jira pydantic
Étape 2 : extraction de texte
import pdfplumber
from docx import Document
from pathlib import Path
def extract_text(file_path: str) -> str:
path = Path(file_path)
if path.suffix.lower() == '.pdf':
with pdfplumber.open(path) as pdf:
return '\n'.join(page.extract_text() or '' for page in pdf.pages)
elif path.suffix.lower() in ['.docx', '.doc']:
doc = Document(path)
return '\n'.join(para.text for para in doc.paragraphs)
elif path.suffix.lower() in ['.txt', '.md']:
return path.read_text(encoding='utf-8')
raise ValueError(f"Format non supporté: {path.suffix}")
Étape 3 : prompt d'extraction
Le prompt est crucial pour la qualité des résultats. Voici un template testé et optimisé :
EXTRACTION_PROMPT = """Tu es un assistant spécialisé dans l'analyse de rapports d'activité.
Analyse le rapport suivant et extrait toutes les tâches accomplies ou mentionnées.
Pour chaque tâche, fournis:
- description: une description concise de la tâche
- type: bug_fix, feature, documentation, refactoring, deployment, meeting, other
- status: done, in_progress, planned
- assignee: le nom de la personne si mentionné, sinon null
Réponds UNIQUEMENT avec un JSON valide, sans texte avant ou après.
Format attendu:
{
"tasks": [
{"description": "...", "type": "...", "status": "...", "assignee": "..."}
]
}
RAPPORT:
{report_text}
"""
Étape 4 : appel au LLM local
import ollama
import json
from pydantic import BaseModel
from typing import Optional
class Task(BaseModel):
description: str
type: str
status: str
assignee: Optional[str] = None
class ExtractionResult(BaseModel):
tasks: list[Task]
def extract_tasks(report_text: str) -> ExtractionResult:
prompt = EXTRACTION_PROMPT.format(report_text=report_text)
response = ollama.chat(
model='llama3.1:8b',
messages=[{'role': 'user', 'content': prompt}],
options={'temperature': 0.1} # Basse température pour plus de cohérence
)
content = response['message']['content']
# Nettoyer la réponse si nécessaire
if '```json' in content:
content = content.split('```json')[1].split('```')[0]
data = json.loads(content)
return ExtractionResult(**data)
Étape 5 : intégration Jira
from jira import JIRA
def create_jira_tickets(tasks: list[Task], project_key: str, jira_client: JIRA):
created_tickets = []
type_mapping = {
'bug_fix': 'Bug',
'feature': 'Story',
'documentation': 'Task',
'refactoring': 'Task',
'deployment': 'Task',
'other': 'Task'
}
for task in tasks:
if task.status == 'done':
continue # Ne pas créer de tickets pour les tâches terminées
issue_dict = {
'project': {'key': project_key},
'summary': task.description[:255], # Limite Jira
'issuetype': {'name': type_mapping.get(task.type, 'Task')},
'description': f"Tâche extraite automatiquement du rapport mensuel.\n\nType: {task.type}\nStatut original: {task.status}"
}
if task.assignee:
# Rechercher l'utilisateur Jira correspondant
users = jira_client.search_users(query=task.assignee)
if users:
issue_dict['assignee'] = {'accountId': users[0].accountId}
new_issue = jira_client.create_issue(fields=issue_dict)
created_tickets.append(new_issue.key)
return created_tickets
Étape 6 : script principal
import os
from pathlib import Path
def process_monthly_reports(reports_folder: str, jira_project: str = None):
# Configuration Jira optionnelle
jira_client = None
if jira_project and os.getenv('JIRA_URL'):
jira_client = JIRA(
server=os.getenv('JIRA_URL'),
basic_auth=(os.getenv('JIRA_EMAIL'), os.getenv('JIRA_TOKEN'))
)
results = []
for file_path in Path(reports_folder).glob('*'):
if file_path.suffix.lower() not in ['.pdf', '.docx', '.doc', '.txt', '.md']:
continue
print(f"Traitement de {file_path.name}...")
# Extraction du texte
text = extract_text(str(file_path))
# Extraction des tâches via LLM
extraction = extract_tasks(text)
# Création des tickets Jira si configuré
tickets = []
if jira_client and jira_project:
tickets = create_jira_tickets(extraction.tasks, jira_project, jira_client)
results.append({
'file': file_path.name,
'tasks_found': len(extraction.tasks),
'tickets_created': tickets
})
return results
if __name__ == '__main__':
results = process_monthly_reports('./rapports/', jira_project='PROJ')
for r in results:
print(f"{r['file']}: {r['tasks_found']} tâches, {len(r['tickets_created'])} tickets créés")
Optimisations et bonnes pratiques
Gestion des hallucinations
Les LLM peuvent inventer des tâches qui n'existent pas dans le rapport. Pour mitiger ce risque :
- Température basse : utilisez
temperature=0.1pour des réponses plus déterministes - Validation croisée : faites analyser le même rapport 3 fois et ne gardez que les tâches présentes dans au moins 2 résultats
- Revue humaine : affichez les tâches extraites pour validation avant création des tickets
Montée en charge
Pour traiter des volumes importants :
- Utilisez vLLM au lieu d'Ollama pour un meilleur débit
- Parallélisez le traitement des rapports avec
asynciooumultiprocessing - Considérez un cluster de GPU si vous traitez plus de 1000 rapports par jour
Amélioration continue
Gardez un log des corrections manuelles faites par les utilisateurs. Ces données vous permettront de :
- Affiner le prompt pour de meilleurs résultats
- Identifier les types de rapports problématiques
- Éventuellement fine-tuner le modèle sur vos données spécifiques
Configuration matérielle recommandée
Pour une PME traitant 50 à 200 rapports par mois :
| Composant | Minimum | Recommandé | |-----------|---------|------------| | CPU | Intel i5 / AMD Ryzen 5 | Intel i7 / AMD Ryzen 7 | | RAM | 16 Go | 32 Go | | GPU | RTX 3060 (8 Go) | RTX 4070 (12 Go) | | Stockage | 500 Go SSD | 1 To NVMe |
Coût total : entre 8 000 et 15 000 MAD pour une configuration neuve. Un investissement amorti en quelques mois d'économies sur les API cloud et le temps de traitement manuel.
Ce que cela signifie pour votre entreprise
L'automatisation par LLM local transforme un processus manuel de plusieurs heures en une opération de quelques minutes. Sans compromis sur la sécurité des données.
Ce pattern s'applique bien au-delà des rapports mensuels :
- Extraction d'informations de contrats
- Analyse de tickets support pour identifier des tendances
- Résumé automatique de réunions (à partir de transcriptions)
- Classification de documents entrants
Chez ClaroDigi, nous concevons et déployons ce type de solutions pour les entreprises marocaines. Notre service d'automatisation IA inclut l'analyse de vos processus, la sélection du modèle adapté, et l'intégration avec vos outils existants.
Si vous débutez dans l'automatisation et souhaitez comprendre les opportunités pour votre entreprise, notre diagnostic digital gratuit est un bon point de départ.
FAQ
Quelle est la précision de l'extraction par rapport à une extraction manuelle ?
Dans nos tests sur des rapports structurés, Llama 3.1 8B atteint 85 à 92% de précision par rapport à une extraction humaine. Les erreurs sont principalement des omissions (tâches non détectées) plutôt que des faux positifs (tâches inventées). La validation croisée multi-passes améliore ce taux à plus de 95%.
Puis-je utiliser un modèle plus petit pour réduire les coûts matériels ?
Oui. Phi-3 Mini (3.8B paramètres) tourne sur 8 Go de RAM sans GPU et offre des résultats corrects pour l'extraction de tâches simples. La qualité baisse sur les rapports complexes ou mal structurés. Commencez par Phi-3 et passez à Llama 3.1 si les résultats sont insuffisants.
Comment gérer les rapports dans plusieurs langues ?
Les modèles récents comme Llama 3.1 et Mistral sont multilingues. Ils gèrent nativement le français, l'anglais, et de nombreuses autres langues. Adaptez simplement le prompt à la langue du rapport ou gardez un prompt en anglais (les modèles comprennent les instructions en anglais même pour analyser du texte français).
L'intégration Jira fonctionne-t-elle avec Jira Server ou uniquement Cloud ?
La bibliothèque jira-python supporte les deux. Pour Jira Server, utilisez un token API personnel. Pour Jira Cloud, utilisez un token API créé depuis les paramètres de compte Atlassian. La configuration est légèrement différente mais le code reste identique.
Combien de temps faut-il pour traiter un rapport de 10 pages ?
Avec Llama 3.1 8B sur une RTX 4070, comptez 15 à 30 secondes par rapport de 2000 mots. L'extraction de texte (PDF vers texte) ajoute 1 à 2 secondes. Pour 50 rapports, le traitement complet prend moins de 30 minutes, contre plusieurs heures en manuel.
