mise a jour fonctionelle 2
This commit is contained in:
72
README.md
Normal file
72
README.md
Normal file
@ -0,0 +1,72 @@
|
||||
# 🛠️ Ops-GPT : Orchestrateur SRE (Modèle GPT-OSS:20B)
|
||||
|
||||
**Ops-GPT** est un assistant SRE spécialisé dans l'écosystème **RHEL / CentOS / Rocky Linux**. Il utilise les données temps réel pour diagnostiquer les pannes de service et les conflits de ressources.
|
||||
|
||||
## 📌 Architecture de Diagnostic
|
||||
|
||||
L'intelligence du système repose sur la corrélation de cinq flux de données via le protocole **MCP (Model Context Protocol)** :
|
||||
|
||||
1. **Inventaire (RPM)** : Identification des versions OS et paquets.
|
||||
2. **Performance (VictoriaMetrics)** : Monitoring (CPU, RAM, I/O, Network).
|
||||
3. **Réseau & Sockets (`ss -ntulop`)** : État réel des ports et processus à l'écoute.
|
||||
4. **Configuration Live par Service** : Lecture ciblée des fichiers de configuration (`/etc/`).
|
||||
5. **Mémoire Collective (ChromaDB)** : Base vectorielle des incidents passés.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Spécifications des Outils MCP
|
||||
|
||||
### 📂 `get_live_config(service_name)`
|
||||
* **Fonction** : Récupère intelligemment l'ensemble des fichiers de configuration liés à un service spécifique.
|
||||
* **Comportement** : Si `service_name="nginx"`, l'outil renvoie `/etc/nginx/nginx.conf` et les fichiers dans `/etc/nginx/conf.d/`.
|
||||
* **Format de sortie** :
|
||||
```text
|
||||
SERVICE: [service_name]
|
||||
FILES_COLLECTED: /etc/[service]/...
|
||||
--- FILE: [path] ---
|
||||
[Content]
|
||||
```
|
||||
|
||||
### 🔌 `get_network_sockets`
|
||||
* **Fonction** : Fournit la sortie brute de la commande `ss -ntulop`.
|
||||
* **Utilité** : Permet au LLM de corréler les ports configurés avec les processus réellement en cours d'exécution (PID, Users, Inodes).
|
||||
* **Format de sortie** :
|
||||
```text
|
||||
NETWORK_STATE (ss -ntulop):
|
||||
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
||||
tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
|
||||
```
|
||||
|
||||
|
||||
### 📊 `get_system_metrics` (VictoriaMetrics)
|
||||
* **Fonction** : Extraction des statistiques descriptives (Load, CPU, I/O Wait).
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Règles de Gouvernance SRE
|
||||
|
||||
### 1. Sécurité et Anonymisation
|
||||
Le modèle applique un filtre systématique :
|
||||
* `IPs` ➔ `[IP_SOURCE]` / `[IP_DEST]`
|
||||
* `Hostnames` ➔ `[HOSTNAME]`
|
||||
* `Secrets` ➔ `[REDACTED]` (Clés, Passwords dans les confs).
|
||||
|
||||
### 2. Stratégie Linguistique
|
||||
* **Interface Client** : Français.
|
||||
* **Backend & Archivage** : Anglais Technique.
|
||||
|
||||
### 3. Philosophie d'Intervention
|
||||
* **Périmètre** : OS & Middleware (RHEL-based).
|
||||
* **Priorité** : Restauration immédiate (MTTR) via Workarounds.
|
||||
* **Données** : Zéro perte de données tolérée.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Exemple de Corrélation Avancée
|
||||
|
||||
1. **Input** : "Impossible de démarrer Apache sur [HOSTNAME]."
|
||||
2. **Action 1 (`get_network_sockets`)** : Le modèle voit que le port 80 est déjà "LISTEN" par le PID 567 (nginx).
|
||||
3. **Action 2 (`get_live_config("apache")`)** : Le modèle confirme que le `Listen 80` est configuré.
|
||||
4. **Action 3 (`get_rpm_inventory`)** : Vérifie si une mise à jour récente a installé `nginx` par erreur (dépendance).
|
||||
5. **Synthèse** : "Conflit détecté : Nginx occupe déjà le port 80. Souhaitez-vous arrêter Nginx ou changer le port d'Apache ?"
|
||||
6. **Archivage** : Enregistre le conflit de port dans ChromaDB.
|
||||
@ -1,42 +1,102 @@
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
import chromadb
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
# On définit l'hôte sur 0.0.0.0 et le port sur 8080 pour le container Docker
|
||||
mcp = FastMCP("Chroma-Server", host="0.0.0.0", port=8080)
|
||||
# 1. Initialisation de FastMCP
|
||||
# On force l'hôte sur 0.0.0.0 pour Docker et le port sur 8080
|
||||
mcp = FastMCP("Chroma-Admin-Assistant", host="0.0.0.0", port=8080)
|
||||
|
||||
# Initialisation du client ChromaDB
|
||||
CHROMA_URL = os.getenv("CHROMA_URL", "http://chroma-db:8000")
|
||||
client = chromadb.HttpClient(host="chroma-db", port=8000)
|
||||
# 2. Connexion au client ChromaDB
|
||||
# On utilise l'URL interne au réseau Docker définie dans votre docker-compose
|
||||
CHROMA_HOST = os.getenv("CHROMA_HOST", "chroma-db")
|
||||
client = chromadb.HttpClient(host=CHROMA_HOST, port=8000)
|
||||
|
||||
# Nom de la collection dédiée aux incidents
|
||||
COLLECTION_NAME = "incident_history"
|
||||
|
||||
@mcp.tool()
|
||||
async def find_incident_solution(query_error: str, service: str = None):
|
||||
"""
|
||||
Recherche des incidents passés similaires et retourne les solutions appliquées.
|
||||
L'utilisateur peut décrire l'erreur (ex: 'serveur lent') ou donner un code d'erreur.
|
||||
L'utilisateur peut décrire l'erreur ou donner un extrait de log technique.
|
||||
"""
|
||||
collection = client.get_collection(name="incident_history")
|
||||
|
||||
# On cherche les 2 incidents les plus proches pour ne pas saturer le LLM
|
||||
results = collection.query(
|
||||
query_texts=[query_error],
|
||||
n_results=2,
|
||||
where={"service": service} if service else None
|
||||
)
|
||||
|
||||
if not results["documents"][0]:
|
||||
return "Aucun incident similaire répertorié dans la base de connaissances."
|
||||
try:
|
||||
collection = client.get_or_create_collection(name=COLLECTION_NAME)
|
||||
|
||||
# Recherche vectorielle des 2 incidents les plus proches
|
||||
results = collection.query(
|
||||
query_texts=[query_error],
|
||||
n_results=2,
|
||||
where={"service": service} if service else None
|
||||
)
|
||||
|
||||
if not results["documents"] or not results["documents"][0]:
|
||||
return "Aucun incident similaire répertorié pour le moment."
|
||||
|
||||
response = "Voici les incidents similaires trouvés :\n"
|
||||
for i, doc in enumerate(results["documents"][0]):
|
||||
meta = results["metadatas"][0][i]
|
||||
response += f"\n--- INCIDENT {meta.get('incident_id', 'Inconnu')} ---"
|
||||
response += f"\nLOGS : {doc}"
|
||||
response += f"\nSOLUTION : {meta.get('solution_applied', 'Non renseignée')}\n"
|
||||
|
||||
return response
|
||||
response = "🔍 Voici les précédents incidents similaires trouvés :\n"
|
||||
for i in range(len(results["documents"][0])):
|
||||
doc = results["documents"][0][i]
|
||||
meta = results["metadatas"][0][i]
|
||||
response += f"\n--- INCIDENT {meta.get('incident_id')} ---"
|
||||
response += f"\nLOGS : {doc}"
|
||||
response += f"\nSOLUTION : {meta.get('solution_applied')}\n"
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
return f"Erreur lors de la recherche : {str(e)}"
|
||||
|
||||
@mcp.tool()
|
||||
async def add_incident_report(service: str, incident_description: str, solution_applied: str):
|
||||
"""
|
||||
Ajoute un nouvel incident et sa résolution dans la base de connaissances.
|
||||
À utiliser quand une solution technique a été validée par un administrateur.
|
||||
"""
|
||||
try:
|
||||
collection = client.get_or_create_collection(name=COLLECTION_NAME)
|
||||
|
||||
uid = f"INC-{uuid.uuid4().hex[:8].upper()}"
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
collection.add(
|
||||
documents=[incident_description],
|
||||
metadatas=[{
|
||||
"service": service,
|
||||
"solution_applied": solution_applied,
|
||||
"incident_id": uid,
|
||||
"timestamp": timestamp
|
||||
}],
|
||||
ids=[uid]
|
||||
)
|
||||
|
||||
return f"✅ Succès : L'incident {uid} a été archivé pour le service {service}."
|
||||
except Exception as e:
|
||||
return f"Erreur lors de l'ajout : {str(e)}"
|
||||
|
||||
@mcp.tool()
|
||||
async def list_all_incidents(limit: int = 15):
|
||||
"""
|
||||
Liste les incidents enregistrés dans la base.
|
||||
Permet d'avoir une vue d'ensemble des connaissances actuelles.
|
||||
"""
|
||||
try:
|
||||
collection = client.get_or_create_collection(name=COLLECTION_NAME)
|
||||
results = collection.get(limit=limit)
|
||||
|
||||
if not results["ids"]:
|
||||
return "La base de connaissances est vide."
|
||||
|
||||
summary = f"📋 Liste des {len(results['ids'])} dernières entrées :\n"
|
||||
for i in range(len(results["ids"])):
|
||||
meta = results["metadatas"][i]
|
||||
summary += f"\n- [{meta.get('timestamp')}] {meta.get('incident_id')} | Service: {meta.get('service')}"
|
||||
summary += f"\n Extrait: {results['documents'][i][:75]}..."
|
||||
|
||||
return summary
|
||||
except Exception as e:
|
||||
return f"Erreur lors de la lecture : {str(e)}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
# On lance le serveur en mode SSE (Server-Sent Events)
|
||||
# C'est ce que LM Studio et la plupart des clients attendent
|
||||
# Lancement du serveur en mode SSE
|
||||
mcp.run(transport="sse")
|
||||
|
||||
Reference in New Issue
Block a user