Funk-lab/admin/ia/llama_server.md
ALI YESILKAYA 870c28b43d
docs(stt): fix /no_think Qwen3 + instance embeddings :1238 (doc complète) (#14)
* 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

* fix(stt): désactiver le mode thinking de Qwen3 (/no_think) — content vide / 502

Qwen3 est un modèle « thinking » : il dépensait tout le budget max_tokens en
reasoning_content → content vide, ou partait en raisonnement long → timeout (502
"upstream LiteLLM : " avec message vide). Diagnostic : appel direct LiteLLM en
0.87s mais content="" et tout dans reasoning_content.

- brain.py : ajoute le token de contrôle `/no_think` au system prompt
  (configurable STT_DISABLE_THINKING, défaut true ; inoffensif pour non-Qwen).
- brain.py : filet de sécurité — si content vide, récupère reasoning_content
  au lieu de renvoyer "".

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

* docs(stt): documenter le fix /no_think Qwen3 + instance embeddings :1238

- admin/ia/stt.md : caveat « thinking » Qwen3 (content vide / timeout 502) +
  fix /no_think + fallback reasoning_content ; roadmap 5e ; bannière.
- stt/server/README.md : env STT_DISABLE_THINKING + section caveat Qwen3.
- admin/ia/llama_server.md : 4e instance llama-embed :1238 (nomic, dim 768),
  double source d'embeddings (1234 Qwen3 / 1238 nomic), caveat thinking,
  correction dim Qwen3 (3584 → 4096).
- admin/ia/litellm.md : cross-ref caveat thinking (concerne tous les
  consommateurs de hermes-default/qwen3).
- ci : exclure **/*.md du trigger build-stt-server (doc-only ne rebuild plus).

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 22:32:43 +02:00

8.5 KiB
Raw Blame History

llama-server — Inférence LLM (gpu-01)

llama-server (llama.cpp compilé with ROCm 7.x) tourne en quatre instances systemd sur gpu-01 (192.168.10.20). API 100% compatible OpenAI — remplace LM Studio pour l'inférence.


Quatre instances

Service systemd Port Modèle Backend Usage
llama-server 1234 Qwen3-8B Q4_K_M GPU (RX 6700XT) Inférence principale — Hermes + LiteLLM
llama-server-system 1236 Qwen3-1.7B Q4_K_M CPU Profil Hermes system (tâches légères)
llama-server-monitor 1237 Qwen3-1.7B Q4_K_M CPU Profil Hermes monitor (supervision)
llama-embed 1238 nomic-embed-text-v1.5 f16 GPU Embeddings dédiés (mémoire STT, dim 768)

Les instances CPU (system, monitor) partagent le même binaire mais des configs distinctes. Les modèles sont sur NFS monté depuis storage-01 (/mnt/models).

llama-embed (:1238) est géré par Ansible (rôle llama_server, llama_embed_enabled) — contrairement aux instances CPU (gap IaC, voir plus bas). Sert un modèle d'embedding dédié (nomic-embed-text, dim 768), bien plus discriminant que Qwen3-8B réutilisé. Partage le GPU avec :1234 (~300 Mo VRAM).


Service systemd

# Instance GPU (principale)
sudo systemctl status llama-server
sudo systemctl restart llama-server
sudo journalctl -u llama-server -f

# Instance CPU — profil system
sudo systemctl status llama-server-system
sudo systemctl restart llama-server-system
sudo journalctl -u llama-server-system -f

# Instance CPU — profil monitor
sudo systemctl status llama-server-monitor
sudo systemctl restart llama-server-monitor
sudo journalctl -u llama-server-monitor -f

# Instance GPU — embeddings dédiés (nomic-embed-text :1238)
sudo systemctl status llama-embed
sudo systemctl restart llama-embed
sudo journalctl -u llama-embed -f

Modèle actuel (GPU)

Paramètre Valeur
Modèle Qwen3-8B Q4_K_M
Alias API qwen3-8b
Chemin /mnt/models/bartowski/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf
Contexte 32768 tokens
GPU offload 99 layers (tout sur GPU)
Parallélisme 1 slot (requis pour Hermes — voir bug parallel)
Embeddings activés (--embeddings --pooling mean)

Contrainte VRAM (12 GB RX 6700XT)

Élément VRAM
Modèle Q4_K_M 8B ~5 GB
KV cache à 32768 ctx ~4 GB
Total ~9 GB

Embeddings

llama-server expose /v1/embeddings avec --embeddings --pooling mean. Le flag --pooling mean est obligatoire pour les LLM causaux (sinon : erreur 400 Pooling type 'none' is not OAI compatible).

Deux sources d'embeddings :

Endpoint Modèle Dim Usage
:1234/v1/embeddings Qwen3-8B (chat réutilisé) 4096 RAG (funk-docs) — peu discriminant
:1238/v1/embeddings nomic-embed-text (dédié) 768 Mémoire STT (stt-memory) — précis

Qwen3-8B n'est pas un modèle d'embedding dédié (cosinus uniformément hauts → recherche peu discriminante). L'instance llama-embed (:1238) sert nomic-embed-text, spécialisé. Le RAG pourra y basculer quand il sera réparé (voir admin/ia/rag.md).

# Test embeddings dédiés nomic (:1238) — depuis le LAN cluster
curl -s -X POST http://192.168.10.20:1238/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model":"nomic-embed-text","input":["test"]}' | jq '.data[0].embedding | length'
# → 768 (dimension nomic-embed-text-v1.5)

# Test embeddings Qwen3 (:1234) — RAG
curl -s -X POST http://192.168.10.20:1234/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b","input":["test"]}' | jq '.data[0].embedding | length'
# → 4096 (dimension Qwen3-8B)

Performances (RX 6700XT, ROCm 7.x)

Métrique Qwen3-1.7B (CPU) Qwen3-8B (GPU)
Prefill ~50 tok/s ~300 tok/s
Génération ~15 tok/s ~60 tok/s
TTFT Hermes (15k tokens) ~300s ~50s
VRAM modèle — (CPU only) ~5 GB

Validation API

# Depuis storage-01 ou n'importe quelle machine du LAN cluster
curl -s http://192.168.10.20:1234/v1/models | jq '.data[].id'

# Test inférence rapide (GPU)
curl http://192.168.10.20:1234/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-8b",
    "messages": [{"role": "user", "content": "Dis bonjour"}],
    "max_tokens": 50
  }'

# Test instances CPU
curl -s http://192.168.10.20:1236/v1/models | jq '.data[].id'
curl -s http://192.168.10.20:1237/v1/models | jq '.data[].id'

Métriques Prometheus

Le flag --metrics est requis dans le service systemd pour activer l'endpoint /metrics. Sans lui, llama-server retourne 501 Not Implemented.

# Vérifier que les métriques sont disponibles
curl -s http://192.168.10.20:1234/metrics | grep llamacpp

# Métriques clés exposées
curl -s http://192.168.10.20:1234/metrics | grep -E \
  "llamacpp:prompt_tokens_total|llamacpp:tokens_predicted_total|llamacpp:requests_deferred|llamacpp:kv_cache_usage_ratio"
Métrique Description
llamacpp:prompt_tokens_total Tokens de prompt traités (cumul)
llamacpp:tokens_predicted_total Tokens générés (cumul)
llamacpp:prompt_tokens_seconds Débit prefill (tok/s)
llamacpp:predicted_tokens_seconds Débit génération (tok/s)
llamacpp:requests_deferred Requêtes en file d'attente (backpressure)
llamacpp:kv_cache_usage_ratio Occupation KV cache (01)
llamacpp:kv_cache_tokens Tokens actuellement en KV cache

Ces métriques sont scrapées par Prometheus via les jobs llama-server-gpu, llama-server-system, llama-server-monitor définis dans k8s/infra/monitoring/values.yaml.


Changer de modèle

  1. Modifier host_vars/gpu-01/vars.yml :

    llama_model_path: "/mnt/models/bartowski/NouveauModele.gguf"
    llama_model_alias: "nouveau-alias"
    llama_ctx_size: 32768   # ajuster selon VRAM disponible
    
  2. Redéployer :

    ansible-playbook -i inventory.yml playbooks/gpu-01.yml --tags llama_server
    
  3. Mettre à jour LiteLLM (alias hermes-default) :

    ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags litellm
    

Build (source)

# Sur gpu-01 — rebuild si besoin
cd /opt/llama.cpp
git pull
cmake -B build \
  -DGGML_HIP=ON \
  -DAMDGPU_TARGETS=gfx1030 \
  -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++ \
  -DCMAKE_PREFIX_PATH=/opt/rocm
cmake --build build --target llama-server -j$(nproc)
sudo systemctl restart llama-server

⚠️ Gap IaC — instances CPU non couvertes par Ansible

Le rôle ansible/roles/llama_server/ ne déploie que l'instance GPU (llama-server.service sur :1234). Les instances CPU (llama-server-system :1236 et llama-server-monitor :1237) ont été créées manuellement et ne sont pas encore dans le code Ansible.

Conséquence : un make apply-gpu complet sur un hôte vierge ne déploierait PAS les instances CPU.

Axe d'amélioration : étendre le rôle llama_server pour déployer les 3 instances via une boucle (with_items ou loop Ansible), avec des variables dédiées dans host_vars/gpu-01/vars.yml.


Points d'attention

Sujet Détail
GPU non officiel RX 6700XT (gfx1031) — HSA_OVERRIDE_GFX_VERSION=10.3.0 dans le service
ROCm version 7.2.3 — libs dans /opt/rocm
--metrics obligatoire Sans ce flag, /metrics retourne 501 Not Implemented — Prometheus ne scrappe rien
parallel=1 obligatoire --parallel 4 divise le ctx en slots → contexte insuffisant pour Hermes
pooling mean obligatoire Sans --pooling mean, l'endpoint /v1/embeddings retourne erreur 400
Qwen3 = modèle « thinking » Par défaut Qwen3 raisonne avant de répondre → consomme le budget max_tokens en reasoning_content (content vide) ou réfléchit longtemps (timeout). Ajouter /no_think au prompt système pour le désactiver. STT le fait (STT_DISABLE_THINKING) ; à considérer pour tout consommateur de Qwen3 à max_tokens faible (ask-agent, profils Hermes).
Gemma 4 interdit Crash ROCm sur prompts >800 tokens
Modèles sur NFS /mnt/models monté depuis storage-01 (NFS) — si NFS down, les 3 services échouent au démarrage
3 services indépendants Chaque instance a son propre service systemd — redémarrer l'un n'affecte pas les autres
Instances CPU hors Ansible llama-server-system et llama-server-monitor créés manuellement — voir section Gap IaC ci-dessus