Funk-lab/admin/ia/rag.md
ALI YESILKAYA 9fef555cc3
feat(stt): embeddings dédiés nomic-embed-text sur gpu-01 (:1238) + migration auto (#12)
* feat(stt): mémoire long-terme sémantique via Qdrant (5b)

Serveur : longterm.py — collection Qdrant stt-memory (embeddings Qwen3 gpu-01, dim auto,
Cosine), recall top-k injecté au prompt, remember des tours user. Tout dégrade proprement
si Qdrant/embeddings injoignables (la mémoire court-terme tient). Env STT_MEMORY_LONGTERM,
STT_QDRANT_URL, STT_EMBED_URL, STT_MEMORY_TOPK.

Testé en process : dégradation OK (Qdrant down → mem=0, pas de crash, court-terme tient).
Qdrant réparé le 17/06 (5c). Recherche sémantique réelle à valider sur cluster.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT

* feat(stt): endpoint /v1/memory/health + upsert Qdrant synchrone

- /v1/memory/health sonde activement embeddings + Qdrant + collection et
  expose les erreurs (recall/remember dégradent en silence → indébogables).
  Permet de diagnostiquer la mémoire long-terme sans kubectl exec.
- remember() : upsert avec ?wait=true → le souvenir est immédiatement
  cherchable (sans wait, Qdrant met l'écriture en file → un recall
  cross-session immédiat pouvait le rater).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT

* docs(stt): 5b mémoire long-terme validée en prod + backlog nomic-embed-text

- Rappel cross-session confirmé (« Felix » retrouvé dans une nouvelle session),
  points_count vérifié via /v1/memory/health.
- Note du fix upsert ?wait=true et de l'endpoint de diagnostic.
- Roadmap : 5d (nomic-embed-text dim 768) en backlog qualité ; états haut/bas
  du doc mis à jour (déployé + validé sur cible).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT

* feat(stt): embeddings dédiés nomic-embed-text sur gpu-01 (:1238) + migration auto

Remplace Qwen3 (chat réutilisé, dim 4096, peu discriminant) par un modèle
d'embedding spécialisé nomic-embed-text (dim 768) pour la mémoire long-terme.

Ansible (rôle llama_server) :
- nouvelle instance optionnelle `llama-embed` (llama_embed_enabled) servant un
  modèle d'embedding dédié sur :1238, GPU ; télécharge le GGUF si absent.
- activée sur gpu-01 (host_vars) : nomic-embed-text-v1.5 f16.

STT-server :
- STT_EMBED_URL → :1238, STT_EMBED_MODEL → nomic-embed-text (deployment + config).
- _ensure_collection détecte le changement de dimension (4096→768) et recrée
  automatiquement la collection stt-memory (anciens vecteurs incomparables) —
  pas de drop manuel.

Docs : llama_server README, rag.md, stt.md (5d ), CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-17 21:54:41 +02:00

203 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RAG — Documentation Funk
> ⚠️ **HS depuis 2026-06-05** — Qdrant en crash-loop (segment `funk-docs` corrompu).
> Procédure de réparation dans `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.
---
## Architecture
```
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
│ 2. Recherche vectorielle → POST http://127.0.0.1:6333/collections/funk-docs/points/search
Qdrant (storage-01:6333)
│ Retourne les N chunks les plus proches sémantiquement
rag-query affiche les extraits + fichier source + score
Hermes formule sa réponse en citant les sources
```
---
## Composants déployés
| Composant | Emplacement | Rôle |
|---|---|---|
| Qdrant | storage-01:6333/6334 | Base vectorielle — stocke les embeddings |
| `rag-ingest` | `/usr/local/bin/rag-ingest` | Indexe les docs dans Qdrant |
| `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` | 339 chunks — dimension 4096 (Cosine) |
---
## Modèle d'embedding utilisé
**Qwen3-8B** via llama-server GPU (port 1234), avec les flags `--embeddings --pooling mean`.
C'est le modèle de chat principal, réutilisé pour les embeddings.
### Limitation connue
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`).
```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/
```
---
## Utilisation
### Via Hermes (funk-ai)
Le skill `rag-docs` est chargé dans le profil `funk-ai`. Hermes l'utilise
automatiquement pour les questions sur le cluster :
```
# Dans le TUI Hermes :
"Comment je relance dnsmasq ?"
→ Hermes appelle rag-query en interne et cite les sources
```
### En ligne de commande
```bash
# Depuis storage-01
rag-query "ma question"
rag-query "ma question" --top 3 # limiter les résultats (défaut: 5)
# Exemples
rag-query "comment relancer llama-server gpu"
rag-query "nftables firewall port cluster"
rag-query "hermes profils configuration"
rag-query "alertmanager webhook pipeline"
rag-query "monitoring grafana dashboard"
```
---
## Mise à jour de l'index
L'index doit être re-généré après chaque modification de la doc `admin/`.
### Via Ansible (recommandé)
```bash
cd ansible/
../.venv/bin/ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags rag
```
Ansible synchronise `admin/` vers `/srv/data/rag/docs/` puis relance automatiquement
`rag-ingest` (handler Ansible).
### Manuellement
```bash
# Synchroniser les docs (depuis le poste perso)
rsync -av --delete admin/ storage-01:/srv/data/rag/docs/
# Ré-indexer (depuis storage-01)
ssh storage-01 "rag-ingest /srv/data/rag/docs/"
```
L'ingestion prend ~5-10 minutes pour 339 chunks (embeddings via GPU).
Elle est idempotente — re-lancer ne crée pas de doublons (IDs stables basés sur MD5 du fichier+section).
---
## Administration Qdrant
```bash
# Collections existantes
curl -s http://storage-01:6333/collections | python3 -m json.tool
# Stats de la collection funk-docs
curl -s http://storage-01:6333/collections/funk-docs | python3 -m json.tool
# Nombre de points indexés
curl -s http://storage-01:6333/collections/funk-docs \
| python3 -c "import sys,json; print(json.load(sys.stdin)['result']['points_count'])"
# Vider et re-créer la collection (si re-indexation propre nécessaire)
curl -s -X DELETE http://storage-01:6333/collections/funk-docs
rag-ingest /srv/data/rag/docs/
# Statut du service
ssh storage-01 "systemctl status qdrant --no-pager"
```
---
## Structure des fichiers
```
ansible/roles/rag/
├── defaults/main.yml # URLs Qdrant/embed, collection, répertoires
├── tasks/main.yml # Déploiement scripts + docs + skill Hermes
├── handlers/main.yml # Handler : Run rag-ingest + Restart hermes-agent
└── files/
├── rag-ingest # Script Python d'ingestion
├── rag-query # Script Python de requête
└── rag-docs/SKILL.md # Skill Hermes
hermes-skills/funk/rag-docs/
└── SKILL.md # Source versionnée du skill (copié dans files/)
/srv/data/rag/ (NVMe storage-01 — bind-mount /home/data)
├── docs/ # Copie de admin/ — source des chunks
└── (Qdrant stocke dans /srv/data/qdrant/)
```
---
## Points d'attention
| 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 |
| 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 |