docs(cluster): aligne la doc sur l'état réel + re-ingest RAG + fix ubatch embed (#44)

* fix(rag): ubatch=2048 pour llama-embed + retry rag-ingest + nettoie defaults rag

L'instance llama-embed (:1238) tournait avec l'ubatch llama.cpp par défaut (512).
En mode --embeddings + pooling, toute l'entrée doit tenir dans un seul ubatch →
tout chunk > 512 tokens échouait en HTTP 500 « input too large to process.
increase the physical batch size ». ~46 chunks du RAG (CHUNK_MAX=2000 chars ≈
700 tokens) n'étaient pas indexés.

- llama_server : ajoute llama_embed_ubatch_size (2048 = ctx) + flags
  --batch-size/--ubatch-size dans le template llama-embed.service.
- rag-ingest : retry (5×, backoff) sur erreurs transitoires 5xx/réseau de l'embed.
- rag/defaults : aligne embed_url/embed_model sur nomic :1238 (étaient des vars
  mortes pointant encore qwen3-8b :1234 — trompeuses).

Résultat : ré-ingestion 436/436 chunks, 0 erreur.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(cluster): aligne la doc sur l'état réel (relevé 2026-06-21) + snapshot

Passe de vérification terrain (SSH s01/g01 + kubectl) pour corriger les écarts
doc↔réalité, et ajout d'un document de référence vérifié.

- admin/ops/etat-cluster.md (nouveau) : snapshot daté — machines, services+ports
  par hôte, workloads k8s, namespaces, IngressRoute, bases PG, modèles LiteLLM,
  RAG/Qdrant, + procédure pour régénérer le relevé.
- OS AlmaLinux 9.7 → 9.8 (s01/g01) ; précise k8s v1.33.1 (CLAUDE/README/install/talos).
- RAG funk-docs : retire le warning périmé « supprimée 2026-06-17 / à re-ingérer » →
  reconstruite (436 chunks, nomic-embed-text :1238 dim 768, CPU). Maj rag.md,
  incidents.md, stt.md.
- Namespace sacrifice (hors-repo, hors ArgoCD) + bases PG grafana/sacrifice +
  services ai via IngressRoute + registry in-cluster (CLAUDE.md, k9s.md).
- Services de résilience (llm-heartbeat, llama-watchdog/embed) et instances CPU
  manuelles :1236/:1237 (souvent éteintes) documentés.
- Journal progress/2026-06-21.md : section relevé + RAG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ALI YESILKAYA 2026-06-21 23:29:05 +02:00 committed by GitHub
parent f0e33c1121
commit af98b47018
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 292 additions and 82 deletions

View file

@ -1,9 +1,8 @@
# RAG — Documentation Funk
> ⚠️ **À re-ingérer** — Qdrant a été réparé le **2026-06-17** (fin du crash-loop), mais la
> réparation a **supprimé la collection `funk-docs`** : `rag-query` ne retourne rien tant que
> `rag-ingest` n'a pas reconstruit l'index (reconstructible depuis `admin/`).
> Détails de l'incident : `admin/incidents.md` (entrée 2026-06-05).
> ✅ **Opérationnel** (relevé 2026-06-21) — `funk-docs` reconstruite après l'incident Qdrant du
> 2026-06-17 : **436 chunks**, statut 🟢 green, embeddings **nomic-embed-text** (`:1238`, dim 768).
> `rag-query` fonctionne. Détails de l'incident : `admin/incidents.md` (entrée 2026-06-05).
Le RAG (Retrieval-Augmented Generation) permet à Hermes de répondre en s'appuyant
sur la documentation du repo (`admin/`) plutôt que sur sa seule mémoire de modèle.
@ -17,7 +16,7 @@ Hermes (funk-ai)
│ terminal: rag-query "comment relancer dnsmasq ?"
/usr/local/bin/rag-query (storage-01)
│ 1. Embed la question → POST http://192.168.10.20:1234/v1/embeddings
│ 1. Embed la question → POST http://192.168.10.20:1238/v1/embeddings (nomic-embed-text)
│ 2. Recherche vectorielle → POST http://127.0.0.1:6333/collections/funk-docs/points/search
Qdrant (storage-01:6333)
@ -40,52 +39,37 @@ Hermes formule sa réponse en citant les sources
| `rag-query` | `/usr/local/bin/rag-query` | Interroge Qdrant depuis la ligne de commande |
| Skill `rag-docs` | Profil `funk-ai` Hermes | Enseigne à Hermes comment utiliser `rag-query` |
| Docs indexées | `/srv/data/rag/docs/` (NVMe) | Copie locale du dossier `admin/` |
| Collection Qdrant | `funk-docs` | supprimée le 2026-06-17 → **à re-ingérer** (≈339 chunks, dim 4096 Cosine) |
| Collection Qdrant | `funk-docs` | **436 chunks**, 🟢 green, **dim 768 Cosine** (nomic-embed-text) |
---
## Modèle d'embedding utilisé
**Qwen3-8B** via llama-server GPU (port 1234), avec les flags `--embeddings --pooling mean`.
**nomic-embed-text** via l'instance dédiée llama-server `llama-embed` sur gpu-01 (`:1238`, **CPU**,
dim 768). C'est un **vrai modèle d'embedding** — les scripts déployés `rag-ingest` et `rag-query`
le hardcodent (`EMBED_URL=http://192.168.10.20:1238/v1/embeddings`, `EMBED_MODEL=nomic-embed-text`).
C'est le modèle de chat principal, réutilisé pour les embeddings.
> Historique : le RAG utilisait au départ **Qwen3-8B** (modèle de chat, `:1234`, dim 4096) réutilisé
> pour les embeddings → scores cosinus saturés (0.900.95), peu discriminants. La migration vers
> nomic (dim 768) étale les scores → tri par pertinence réel. La collection a été recréée à dim 768.
### Limitation connue
> ⚠️ **Drift IaC** : `ansible/roles/rag/defaults/main.yml` a longtemps gardé `embed_url: :1234` /
> `embed_model: qwen3-8b` — variables **mortes** (le handler lance `rag-ingest` sans passer d'env →
> le défaut hardcodé nomic du script gagne). Aligné sur nomic dans le même lot que ce relevé.
Qwen3-8B est un modèle génératif, **pas un modèle d'embedding dédié**.
Ses représentations vectorielles ont peu de discrimination sémantique :
les scores de similarité cosinus sont uniformément hauts (0.900.95)
quelle que soit la pertinence du résultat.
Conséquence : les résultats retournés ne sont pas triés par pertinence réelle —
le fichier avec le plus de tokens (ex : `incidents.md`) remonte souvent en premier.
### Quand améliorer
Pour une recherche sémantique précise, utiliser un modèle d'embedding dédié :
| Modèle | Taille | Dimension | Notes |
|---|---|---|---|
| `nomic-embed-text-v1.5` | ~274 MB | 768 | Rapide, bon équilibre qualité/taille |
| `bge-m3` | ~1.2 GB | 1024 | Multilingue (français natif) — meilleur choix pour ce repo |
Déploiement — **déjà fait pour STT** (instance `nomic-embed-text` sur `:1238`, dim 768) :
le rôle `llama_server` gère désormais une instance dédiée embeddings sur gpu-01 (GPU),
activée par `llama_embed_enabled` (voir `ansible/roles/llama_server/README.md`). STT pointe
dessus (`STT_EMBED_URL=http://192.168.10.20:1238/v1/embeddings`).
L'instance `llama-embed` est gérée par le rôle `llama_server` (`llama_embed_enabled`, voir son README).
```bash
# (re)déployer l'instance embeddings sur gpu-01
ansible-playbook -i inventory.yml playbooks/gpu-01.yml --tags llama_server
```
Pour basculer **le RAG** sur cette même instance (quand il sera réparé) :
```bash
# Modifier ansible/roles/rag/defaults/main.yml :
# embed_url: "http://192.168.10.20:1238/v1/embeddings"
# embed_model: "nomic-embed-text"
# Puis re-indexer (la dimension passe à 768 → collection recréée) : rag-ingest /srv/data/rag/docs/
```
### Alternative envisagée
| Modèle | Taille | Dimension | Notes |
|---|---|---|---|
| `nomic-embed-text` | ~274 MB | 768 | **En service** — rapide, bon équilibre qualité/taille |
| `bge-m3` | ~1.2 GB | 1024 | Multilingue (français natif) — option si la qualité FR doit monter |
---
@ -143,7 +127,7 @@ rsync -av --delete admin/ storage-01:/srv/data/rag/docs/
ssh storage-01 "rag-ingest /srv/data/rag/docs/"
```
L'ingestion prend ~5-10 minutes pour 339 chunks (embeddings via GPU).
L'ingestion prend quelques minutes pour ~436 chunks (embeddings via l'instance nomic `:1238`, CPU).
Elle est idempotente — re-lancer ne crée pas de doublons (IDs stables basés sur MD5 du fichier+section).
---
@ -197,9 +181,8 @@ hermes-skills/funk/rag-docs/
| Sujet | Détail |
|---|---|
| Qualité embeddings | Qwen3-8B = modèle chat réutilisé — scores peu discriminants. Fonctionnel mais pas optimal. |
| Modèle dédié | `nomic-embed-text` ou `bge-m3` diviseraient les faux positifs par ~3 |
| Contention GPU | `rag-ingest` sollicite le GPU (port 1234) pendant ~5-10 min — éviter pendant une session d'inférence active |
| Qualité embeddings | `nomic-embed-text` (modèle dédié) — scores cosinus discriminants, tri par pertinence réel |
| Embeddings CPU | `llama-embed` (:1238) tourne en **CPU** (découplé du slot chat GPU) — `rag-ingest` ne contend pas le GPU. ubatch=2048 requis (sinon 500 sur chunks > 512 tokens) |
| Re-indexation | Obligatoire après modification de `admin/` — pas de sync automatique |
| Qdrant persistance | Données dans `/srv/data/qdrant/` sur NVMe — survivent aux redémarrages |
| Score minimum | `MIN_SCORE = 0.60` dans `rag-query` — tous les résultats sont au-dessus avec ce modèle |
| Score minimum | `MIN_SCORE` dans `rag-query` ajusté pour nomic (scores plus étalés que qwen3-8b) |

View file

@ -31,7 +31,7 @@ Objectif ultérieur : « agir sur le homelab » via les outils de Hermes (phase
| Agir sur le homelab | Hermes Agent (`:8080`, profils funk-ai/system/monitor/brain) | ✅ opérationnel |
| Voix (STT + TTS + wake word) | `tools/hermes-voice/` — faster-whisper + Piper + webrtcvad | ✅ existe (CLI only) |
| Démarrage auto au boot | `stt --install-service` (systemd --user) — repris du pattern `tools/hermes-voice/` | ✅ |
| Mémoire sémantique | Qdrant `:6333` + RAG (`rag-query`/`rag-ingest`) sur s01 | ✅ Qdrant réparé 17/06 (RAG `funk-docs` à re-ingérer) |
| Mémoire sémantique | Qdrant `:6333` + RAG (`rag-query`/`rag-ingest`) sur s01 | ✅ Qdrant réparé 17/06, RAG `funk-docs` re-ingéré (436 chunks, nomic dim 768) |
Maillon réellement manquant : **l'interface graphique HUD** + le packaging + la mémoire perso.
@ -125,7 +125,7 @@ au prompt système (`STT_DISABLE_THINKING`, défaut `true` ; inoffensif hors Qwe
borné `max_turns` + TTL) et l'injecte dans l'appel LLM. `/v1/ask {session_id}` + `/v1/reset`.
Le client génère un `session_id` par run. Deployment en **1 worker** (cohérence mémoire process).
- **Long-terme (5b, ✅ validé 17/06)** : collection Qdrant dédiée `stt-memory` (indépendante du
`funk-docs` du RAG), embeddings via Qwen3 `:1234` (dim 4096, Cosine), souvenirs pertinents
`funk-docs` du RAG), embeddings via nomic-embed-text `:1238` (dim 768, Cosine), souvenirs pertinents
(top-k) injectés au prompt. Qdrant en IP directe `192.168.10.1:6333`. **Dégrade proprement**
si Qdrant/embeddings injoignables (la mémoire court-terme tient).
- **Validé bout-en-bout** : « mon chat s'appelle Felix » (session A) rappelé dans une