From baedc4622e408752c857c6f406170cc942847c63 Mon Sep 17 00:00:00 2001 From: Betty Date: Tue, 21 Jul 2026 20:35:47 +0200 Subject: [PATCH] post: Remplacer Ollama par Claude API dans l'alert-agent Co-Authored-By: Claude Sonnet 4.6 --- content/posts/alert-agent-claude-api-fr.md | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 content/posts/alert-agent-claude-api-fr.md diff --git a/content/posts/alert-agent-claude-api-fr.md b/content/posts/alert-agent-claude-api-fr.md new file mode 100644 index 0000000..b1a54d3 --- /dev/null +++ b/content/posts/alert-agent-claude-api-fr.md @@ -0,0 +1,130 @@ +--- +title: "Remplacer Ollama par Claude API dans un Alert-Agent Docker Swarm" +date: 2026-07-21T00:00:00+00:00 +draft: false +tags: ["cybersecurity", "infosec", "homelab", "docker-swarm", "docker", "devops", "selfhosted", "opensource", "build-in-public", "french-tech", "apprendre-la-cyber", "threat-intelligence"] +summary: "Mon alert-agent appelait Ollama/Mistral pour prendre des décisions de remédiation. Problème : 40+ secondes de latence, timeouts, et une sévérité figée à 'warning'. Voilà comment j'ai branché Claude Haiku à la place — et pourquoi ça change vraiment quelque chose." +description: "Intégration de Claude API (Anthropic) dans un alert-agent Prometheus/Docker Swarm pour remplacer Ollama. Sévérité dynamique, latence réduite de 40s à 2s, architecture multi-backend." +author: "Bojemoi" +ShowToc: true +ShowReadingTime: true +--- + +Mon homelab tourne sur Docker Swarm avec un pipeline de sécurité continu. Quand Prometheus déclenche une alerte — swap élevé, service crashé, disk plein — un composant que j'appelle l'**alert-agent** reçoit le webhook, enrichit le contexte avec des infos Docker, consulte un LLM pour décider quoi faire, et envoie le résultat sur Telegram. + +Jusqu'ici, ce LLM c'était Ollama avec Mistral:latest, tournant en local sur meta-68. + +Le problème : ça ne fonctionnait plus vraiment. + +## Le Problème avec Ollama + +Ollama était là depuis le début, mais au fil des mois, meta-68 est devenu de plus en plus chargé : MSF teamserver (6 GB RAM), ZAP qui scanne en continu, 15 replicas bm12, 3 replicas uzi... Le modèle Mistral avait du mal à répondre dans les temps. + +En mesurant directement : + +``` +status: 200 time: 42.9 s +{"action":"assistant_response","message":"Understood. I have received a test message..."} +``` + +**43 secondes.** Et même là, Mistral ignorait le format JSON demandé et répondait en texte libre. + +Avec un timeout à 30s dans le code, chaque alerte finissait en `notify_only` par défaut, sans décision LLM réelle. Le fallback prenait le relais — ce qui était mieux que rien, mais l'agent ne servait plus à grand chose. + +## L'autre Problème : la Sévérité Figée + +Toutes mes alertes arrivaient comme `⚠️ Severity: warning`. Pourquoi ? Parce que la sévérité affichée dans Telegram venait directement du label Prometheus, qui est défini statiquement dans les règles d'alerting. + +`HighSeverityFindingsFound` → `warning`. `NodeHighSwapUsage` → `warning`. `DiskAlmostFull` → `warning`. + +Tout est `warning`. C'est inutile — un swap à 85% sur le manager n'a pas le même impact qu'un disk à 99% sur le worker qui héberge la base MSF. + +## La Solution : Claude API avec Évaluation Dynamique + +J'ai intégré Claude Haiku (Anthropic) comme backend LLM, avec deux changements clés. + +### 1. Architecture multi-backend + +Le backend LLM est maintenant configurable via une variable d'environnement `LLM_BACKEND` : + +```python +async def _call_claude(messages: list) -> dict: + client = anthropic.AsyncAnthropic(api_key=settings.claude_api_key) + system = next((m["content"] for m in messages if m["role"] == "system"), "") + user_messages = [m for m in messages if m["role"] != "system"] + response = await client.messages.create( + model=settings.claude_model, + max_tokens=256, + system=system, + messages=user_messages, + ) + return _extract_json(response.content[0].text) +``` + +`LLM_BACKEND=claude` active Claude. `LLM_BACKEND=ollama` revient à Mistral local. `LLM_BACKEND=kimi` appellerait Kimi K3 via Moonshot AI. Pas de rebuild nécessaire pour switcher. + +### 2. Sévérité évaluée par le LLM + +J'ai modifié le system prompt pour demander au LLM d'évaluer la sévérité réelle de l'alerte, indépendamment du label Prometheus : + +``` +Severity levels (assess based on actual risk, ignore Prometheus label): +- critical: immediate risk of data loss, service outage, or security breach +- high: significant degradation, likely to escalate without action +- medium: noticeable issue, should be addressed soon +- low: minor issue, informational +- info: no real impact + +Respond with ONLY a JSON object: +{"action": "", "severity": "", "reason": "", "params": {}} +``` + +Le JSON de réponse inclut maintenant un champ `severity` que le LLM choisit lui-même. Ce champ remplace le label Prometheus dans le message Telegram. + +### 3. Emojis adaptatifs + +```python +severity_emoji = { + "critical": "🔴", + "high": "🟠", + "medium": "🟡", + "low": "🔵", + "info": "⚪", +}.get(severity or "", "⚠️") +``` + +## Résultat + +Avant : +``` +🤖 Alert-Agent +🔔 Alert: NodeHighSwapUsage +⚠️ Severity: warning +🧠 LLM decision: notify_only +💬 Reason: LLM unavailable: ReadTimeout +✅ Action: notify_only +``` + +Après : +``` +🤖 Alert-Agent +🔔 Alert: NodeHighSwapUsage +🟠 Severity: high +🧠 LLM decision: notify_only +💬 Reason: Swap at 85% on the Swarm manager — no Docker service to restart, but warrants monitoring. Escalate if it reaches 95%. +✅ Action: notify_only +``` + +Latence : **2.4 secondes** au lieu de 40+. La sévérité est maintenant contextuelle — le même `NodeHighSwapUsage` peut être `medium` à 60% et `high` à 85%. + +## Ce que j'ai Gardé d'Ollama + +Le stack `51-service-ollama.yml` est conservé avec `replicas: 0`. Si un jour Claude API est indisponible ou trop coûteux, un `docker service scale ollama_ollama=1` + `LLM_BACKEND=ollama` suffit à revenir en arrière. + +C'est l'avantage de l'architecture multi-backend : pas de couplage fort avec un provider. + +## Leçon + +Un LLM local gratuit peut sembler idéal pour ce genre d'usage. Mais quand le node est déjà à 90% de sa capacité RAM/CPU, l'inference locale devient le goulot d'étranglement. À 0.25$ / million de tokens d'input pour Haiku, et avec des alertes qui se déclenchent quelques dizaines de fois par jour au max, le coût mensuel sera probablement inférieur à 1$. + +Parfois, l'API cloud est la solution pragmatique.