Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 |
|
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.
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 :
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.