Files
blog/content/posts/alert-agent-claude-api-fr.md
Betty baedc4622e
Some checks failed
Hugo Build & Deploy / build-deploy (push) Failing after 1m2s
post: Remplacer Ollama par Claude API dans l'alert-agent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-21 20:36:07 +02:00

5.9 KiB

title, date, draft, tags, summary, description, author, ShowToc, ShowReadingTime
title date draft tags summary description author ShowToc ShowReadingTime
Remplacer Ollama par Claude API dans un Alert-Agent Docker Swarm 2026-07-21T00:00:00+00:00 false
cybersecurity
infosec
homelab
docker-swarm
docker
devops
selfhosted
opensource
build-in-public
french-tech
apprendre-la-cyber
threat-intelligence
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. 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. Bojemoi true 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.

HighSeverityFindingsFoundwarning. NodeHighSwapUsagewarning. DiskAlmostFullwarning.

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 :

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": "<action>", "severity": "<severity>", "reason": "<brief 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

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.