From 1b85d4ee653a01c94bdb67beb1fb0cc326df4444 Mon Sep 17 00:00:00 2001 From: ALI YESILKAYA Date: Fri, 19 Jun 2026 22:17:23 +0200 Subject: [PATCH] =?UTF-8?q?feat(llama=5Fserver):=20heartbeat=20embed=20?= =?UTF-8?q?=E2=80=94=20garde=20chaud=20le=20slot=20nomic=20:1238=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le grounding RAG du STT ratait par intermittence (docs=0 → réponses génériques) parce que l'instance d'embedding :1238 partait à froid après une pause : la 1ʳᵉ requête mettait 5-8 s et dépassait le timeout 4 s du STT-server (recall + RAG doc). llama-embed-heartbeat : service systemd local sur gpu-01 qui envoie un petit embed à :1238 toutes les 20 s → le slot reste résident/chaud → la 1ʳᵉ vraie requête est rapide → docs>0 fiable. Pendant du llm-heartbeat (slot chat :1234), pour le slot embed. Co-authored-by: Claude Opus 4.8 --- admin/incidents-llm-gpu.md | 39 +++++++++++++++++++ ansible/roles/llama_server/README.md | 14 +++++++ ansible/roles/llama_server/defaults/main.yml | 8 ++++ ansible/roles/llama_server/handlers/main.yml | 5 +++ ansible/roles/llama_server/tasks/main.yml | 28 +++++++++++++ .../llama-embed-heartbeat.service.j2 | 15 +++++++ .../templates/llama-embed-heartbeat.sh.j2 | 31 +++++++++++++++ 7 files changed, 140 insertions(+) create mode 100644 ansible/roles/llama_server/templates/llama-embed-heartbeat.service.j2 create mode 100644 ansible/roles/llama_server/templates/llama-embed-heartbeat.sh.j2 diff --git a/admin/incidents-llm-gpu.md b/admin/incidents-llm-gpu.md index c1b2260..3eed33a 100644 --- a/admin/incidents-llm-gpu.md +++ b/admin/incidents-llm-gpu.md @@ -168,3 +168,42 @@ vrac, et sonde au plus près du serveur figé. 1. `llm-heartbeat` (storage-01, rôle `litellm`) — garde la connexion chaude (anti-péremption). 2. `request_timeout: 20` + `num_retries: 2` (LiteLLM) + retry STT-server — bornent le blip. 3. `llama-watchdog` (gpu-01, rôle `llama_server`) — répare le wedge ROCm à la source. + +--- + +## 2026-06-19 (suite) — slot d'embedding `:1238` froid → RAG du STT raté (`docs=0`) + +### Symptôme + +Après déploiement du grounding RAG du STT-server (le cerveau interroge `funk-docs`), les +réponses sur le homelab étaient **intermittentes** : parfois ancrées dans la doc, parfois +génériques (« serveur XMPP », « worker nodes »…). Log du pod `stt-server` : + +``` +ask … recall=5471ms … docs=6 ← embed OK → 6 extraits doc injectés → réponse ancrée +ask … recall=8330ms … docs=0 ← embed à 8 s → timeout → 0 extrait → réponse générique +``` + +### Cause + +L'instance d'embedding dédiée (`nomic-embed-text`, gpu-01 `:1238`) part **à froid** après une +pause : le slot GPU devient inactif, la 1ʳᵉ requête met 5-8 s (réchauffe + VRAM). Or le +STT-server borne le recall mémoire **et** la recherche doc à `4 s` (chemin de réponse). Au-delà : +`recall` renvoie `([], None)` → `knowledge.search` ré-embed (2ᵉ timeout) → `docs=0` → le cerveau +répond sans contexte. Même famille que le wedge chat, sur l'**autre slot** GPU — mais ici pas de +freeze, juste une latence à froid. Le slot chat `:1234` est gardé chaud par `llm-heartbeat`, +l'embed `:1238` ne l'était **pas**. + +### Correctif — heartbeat embed (rôle `llama_server`) + +`llama-embed-heartbeat` : service systemd local sur gpu-01 qui envoie un petit embed à `:1238` +toutes les `llama_embed_heartbeat_interval` s (défaut 20). Le slot reste **résident/chaud** → la +1ʳᵉ vraie requête est rapide → recall + RAG doc tiennent dans les 4 s → `docs>0` fiable. + +```bash +ansible-playbook -i inventory.yml playbooks/gpu-01.yml --tags llama_server # déploie le heartbeat embed +systemctl status llama-embed-heartbeat ; journalctl -u llama-embed-heartbeat -f +``` + +> Variante moins bonne envisagée : remonter `STT_DOCS_TIMEOUT`/`recall` à ~8 s → couvre le froid +> mais **ajoute** la latence à chaque requête froide. Le warming l'**élimine** à la source. diff --git a/ansible/roles/llama_server/README.md b/ansible/roles/llama_server/README.md index c4f1066..69a16f7 100644 --- a/ansible/roles/llama_server/README.md +++ b/ansible/roles/llama_server/README.md @@ -12,6 +12,20 @@ Compile llama.cpp (backend HIP/ROCm) et déploie le service **llama-server GPU** `llama-embed` servant un modèle d'embedding spécialisé (ex. `nomic-embed-text`) sur `:1238`, GPU. Télécharge le GGUF depuis `llama_embed_model_url` si absent. Partage le binaire et le GPU avec `:1234` (modèle ~300 Mo VRAM). +- **Heartbeat embed** (optionnel, `llama_embed_heartbeat_enabled`, défaut `true`) : service + systemd `llama-embed-heartbeat` qui envoie un petit embed à `:1238` toutes les + `llama_embed_heartbeat_interval` s → garde le **slot d'embedding chaud**. Sans ça, la 1ʳᵉ + requête après une pause part à froid (5-8 s) et dépasse le timeout du STT-server (4 s) → + recall mémoire vide **et** RAG documentaire raté (`docs=0`, cf. `admin/ia/stt.md`). Pendant + du `llm-heartbeat` (rôle `litellm`) côté chat. Logs : `journalctl -u llama-embed-heartbeat`. + +## Heartbeat embed — variables + +| Variable | Défaut | +|---|---| +| `llama_embed_heartbeat_enabled` | `true` (si `llama_embed_enabled`) | +| `llama_embed_heartbeat_interval` | `20` (s entre deux pings) | +| `llama_embed_heartbeat_timeout` | `15` (s max par ping) | - **Watchdog** (optionnel, `llama_watchdog_enabled`, défaut `true`) : service systemd `llama-watchdog` qui sonde une **vraie génération** sur `:1234` toutes les `llama_watchdog_interval` s. Sur `llama_watchdog_failures` échecs consécutifs → diff --git a/ansible/roles/llama_server/defaults/main.yml b/ansible/roles/llama_server/defaults/main.yml index 91b8464..a7b6a26 100644 --- a/ansible/roles/llama_server/defaults/main.yml +++ b/ansible/roles/llama_server/defaults/main.yml @@ -26,6 +26,14 @@ llama_embed_ctx_size: 2048 llama_embed_n_gpu_layers: 99 llama_embed_pooling: "mean" +# Heartbeat embed — garde CHAUD le slot d'embedding (:1238). Sans ça, la 1ʳᵉ requête +# après une pause part à froid (slot GPU inactif) → latence qui dépassait le timeout 4 s +# du STT-server → recall mémoire vide ET RAG doc raté (docs=0). Petit embed périodique +# (modèle minuscule, ~10-50 ms GPU) → le slot reste résident. Cf. admin/ia/stt.md. +llama_embed_heartbeat_enabled: true +llama_embed_heartbeat_interval: 20 # s entre deux pings (< idle avant refroidissement) +llama_embed_heartbeat_timeout: 15 # s max par ping + # Watchdog — auto-réparation du llama-server figé (wedge ROCm gfx1031). # Sonde une VRAIE génération sur :1234 (pas seulement /health, qui ment quand le # slot d'inférence est bloqué — cf. admin/incidents-llm-gpu.md). Sur N échecs diff --git a/ansible/roles/llama_server/handlers/main.yml b/ansible/roles/llama_server/handlers/main.yml index a7f7609..562f3e7 100644 --- a/ansible/roles/llama_server/handlers/main.yml +++ b/ansible/roles/llama_server/handlers/main.yml @@ -17,3 +17,8 @@ ansible.builtin.systemd: name: llama-watchdog state: restarted + +- name: Restart llama-embed-heartbeat + ansible.builtin.systemd: + name: llama-embed-heartbeat + state: restarted diff --git a/ansible/roles/llama_server/tasks/main.yml b/ansible/roles/llama_server/tasks/main.yml index 120e825..a1c549b 100644 --- a/ansible/roles/llama_server/tasks/main.yml +++ b/ansible/roles/llama_server/tasks/main.yml @@ -124,6 +124,34 @@ immediate: true when: llama_embed_enabled +# --- Heartbeat embed (garde chaud le slot :1238) — optionnel ------------------ + +- name: Deploy llama-embed-heartbeat script + ansible.builtin.template: + src: llama-embed-heartbeat.sh.j2 + dest: /usr/local/bin/llama-embed-heartbeat + mode: '0755' + notify: Restart llama-embed-heartbeat + when: llama_embed_enabled and llama_embed_heartbeat_enabled + +- name: Deploy llama-embed-heartbeat service + ansible.builtin.template: + src: llama-embed-heartbeat.service.j2 + dest: /etc/systemd/system/llama-embed-heartbeat.service + mode: '0644' + notify: + - Reload systemd + - Restart llama-embed-heartbeat + when: llama_embed_enabled and llama_embed_heartbeat_enabled + +- name: Enable llama-embed-heartbeat service + ansible.builtin.systemd: + name: llama-embed-heartbeat + enabled: true + daemon_reload: true + state: started + when: llama_embed_enabled and llama_embed_heartbeat_enabled + # --- Watchdog auto-réparation (wedge ROCm) — optionnel ------------------------ - name: Deploy llama-watchdog script diff --git a/ansible/roles/llama_server/templates/llama-embed-heartbeat.service.j2 b/ansible/roles/llama_server/templates/llama-embed-heartbeat.service.j2 new file mode 100644 index 0000000..fb67740 --- /dev/null +++ b/ansible/roles/llama_server/templates/llama-embed-heartbeat.service.j2 @@ -0,0 +1,15 @@ +[Unit] +Description=llama-embed-heartbeat (garde chaud le slot d'embedding nomic :1238) +After=llama-embed.service +Wants=llama-embed.service + +[Service] +Type=simple +User=root +ExecStart=/usr/local/bin/llama-embed-heartbeat +Restart=always +RestartSec=10 +SyslogIdentifier=llama-embed-heartbeat + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/llama_server/templates/llama-embed-heartbeat.sh.j2 b/ansible/roles/llama_server/templates/llama-embed-heartbeat.sh.j2 new file mode 100644 index 0000000..c59ea56 --- /dev/null +++ b/ansible/roles/llama_server/templates/llama-embed-heartbeat.sh.j2 @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# llama-embed-heartbeat — garde chaud le slot d'embedding nomic (:1238). +# +# Le piège (cf. admin/ia/stt.md) : l'instance d'embedding part à froid après une pause +# (slot GPU inactif) → la 1ʳᵉ requête est lente (5-8 s) et dépasse le timeout du STT-server +# (4 s) → recall mémoire vide ET RAG documentaire raté (`docs=0`) → réponses non ancrées. +# +# Un petit embed périodique (modèle minuscule, ~10-50 ms GPU) garde le slot résident → la +# 1ʳᵉ vraie requête tombe sur un slot chaud. Pendant logique au `llm-heartbeat` (rôle litellm) +# qui garde chaud le slot de CHAT. Logge les échecs → sert aussi de sonde de l'instance embed. +set -u + +URL="http://127.0.0.1:{{ llama_embed_port }}/v1/embeddings" +MODEL="{{ llama_embed_model_alias }}" +INTERVAL="{{ llama_embed_heartbeat_interval }}" +TIMEOUT="{{ llama_embed_heartbeat_timeout }}" + +payload="{\"model\":\"${MODEL}\",\"input\":\"ping\"}" + +fails=0 +while true; do + code=$(curl -s -m "$TIMEOUT" -o /dev/null -w '%{http_code}' \ + "$URL" -H 'Content-Type: application/json' -d "$payload" 2>/dev/null || echo "000") + if [ "$code" = "200" ]; then + fails=0 + else + fails=$((fails + 1)) + echo "embed-heartbeat: KO (HTTP ${code}) — ${fails} échec(s) consécutif(s)" + fi + sleep "$INTERVAL" +done