22 KiB
2026-05-11 — Hermes opérationnel sur Qwen3.5-9B
Résumé
Hermes Agent pleinement fonctionnel sur storage-01 avec le modèle Qwen3.5-9B sur gpu-01. Chemin parcouru : 6 blocages techniques résolus dans la même session.
Blocages rencontrés et résolus
1. auxiliary.compression.context_length manquant dans config.yaml
Symptôme : Failed to initialize agent: Auxiliary compression model google/gemma-4-e4b has a context window of 4,096 tokens, which is below the minimum 64,000 required.
Cause : Hermes a deux vérifications de contexte distinctes — le modèle
principal (model.context_length) et le modèle de compression auxiliaire
(auxiliary.compression.context_length). Seule la première était surchargée.
Fix : Ajout dans roles/hermes_agent/templates/config.yaml.j2 :
auxiliary:
compression:
context_length: {{ hermes_context_length }}
2. Crash GPU Vulkan sur Gemma 4 E4B avec long prompt (>800 tokens)
Symptôme : The model has crashed without additional information. (Exit code: null)
dès le premier appel Hermes (system prompt ~15 000 tokens). Modèle rechargé avec
n_ctx=4096 par défaut, puis erreur de contexte en cascade.
Cause : Bug dans le backend Vulkan de llama.cpp avec l'architecture Gemma 4. Gemma 4 utilise une attention interleaved global/local avec sliding window de 512 tokens. Quand le prompt dépasse 800 tokens (juste au-delà du sliding window), les layers d'attention globale crashent dans l'implémentation Vulkan. Confirmé par test binaire : OK à 500 tokens, crash à ~900 tokens.
Note : Le crash n'est pas lié à la VRAM — Gemma 4 utilise GQA aggressif, le KV cache réel à 65536 tokens est ~2 GB seulement (7 global layers × 65536 × 4096 bytes). La VRAM ne serait pas le facteur limitant.
Fix : Passage sur Qwen3.5-9B qui utilise une attention GQA standard, sans problème Vulkan connu. Fichiers modifiés :
host_vars/gpu-01/vars.yml:lm_studio_default_model: "qwen/qwen3.5-9b"roles/hermes_agent/defaults/main.yml:hermes_model: "qwen/qwen3.5-9b"
3. dnsmasq crash au boot (interface USB ethernet non disponible)
Symptôme : Après reboot de storage-01, gpu-01 ne peut plus télécharger
de paquets (yum makecache bloque). DNS timeout vers 192.168.10.1.
Cause : dnsmasq démarre avec After=network.target mais l'interface USB
ethernet enp6s0f3u2c2 prend plus de temps à apparaître. dnsmasq log :
dnsmasq: unknown interface enp6s0f3u2c2 → crash.
Fix :
- Immédiat :
systemctl reset-failed dnsmasq && systemctl start dnsmasq - Permanent : override systemd dans
roles/dnsmasq/tasks/main.ymlqui crée/etc/systemd/system/dnsmasq.service.d/wait-for-network.conf:[Unit] After=network-online.target Wants=network-online.target
4. LM_API_KEY=qwen/qwen3.5-9b dans le .env de Hermes
Symptôme : Failed to initialize agent: Failed to initialize OpenAI client: bad marshal data (invalid reference) — erreur trompeuse, masque le vrai problème.
Cause : La commande hermes model avait écrit le nom du modèle à la place
de la clé API dans /srv/data/hermes/.env. La clé API pour LM Studio doit être
une chaîne arbitraire (lm-studio), pas le nom du modèle.
Fix : sed -i 's/^LM_API_KEY=.*/LM_API_KEY=lm-studio/' /srv/data/hermes/.env
5. Fichiers .pyc pydantic corrompus sur le RAID5
Symptôme : Failed to initialize OpenAI client: bad marshal data (invalid reference) persistait même après fix du .env.
Cause : Le reboot du RAID5 a laissé des fichiers .pyc Python partiellement
écrits dans le venv de Hermes. Spécifiquement pydantic/fields et ses
dépendances. Python essaie de charger le bytecode compilé → ValueError: bad marshal data (invalid reference).
Traceback exact :
File ".../openai/_models.py", line 39, in <module>
from pydantic.fields import FieldInfo
File "<frozen importlib._bootstrap_external>", line 729, in _compile_bytecode
ValueError: bad marshal data (invalid reference)
Fix :
find /srv/data/hermes/hermes-agent/venv/lib/python3.11/site-packages \
-name '*.pyc' -delete
Python recompile les .pyc au prochain import depuis les .py sources (intacts).
Leçon : Données applicatives sur RAID5 = écriture partielle possible sur
reboot non-propre. Les fichiers .pyc ne sont pas critiques (régénérables) mais
peuvent bloquer l'application entièrement.
Changements ansible
| Fichier | Changement |
|---|---|
roles/common/tasks/main.yml |
Ajout tâche Upgrade all packages (tag update) |
roles/common/handlers/main.yml |
Ajout handler Reboot if kernel updated |
roles/lm_studio/tasks/main.yml |
Ajout socat dans les dépendances dnf |
roles/lm_studio/files/run-llmster.sh |
Ajout lms unload --all avant le chargement du modèle |
roles/hermes_agent/templates/config.yaml.j2 |
Ajout auxiliary.compression.context_length |
roles/hermes_agent/defaults/main.yml |
Modèle passé à qwen/qwen3.5-9b |
roles/dnsmasq/tasks/main.yml |
Ajout override systemd wait-for-network.conf |
roles/dnsmasq/handlers/main.yml |
Ajout handler Reload systemd |
host_vars/gpu-01/vars.yml |
lm_studio_default_model: "qwen/qwen3.5-9b" |
État final
- Hermes Agent opérationnel sur storage-01 (
hermes chatfonctionne) - Modèle Qwen3.5-9B chargé sur gpu-01 avec contexte 65 536 tokens via Vulkan
- Proxy socat actif :
192.168.10.20:1234→127.0.0.1:1234(contourne le rebind llmster post-Vulkan) - DNS résolu après reboot grâce à l'override
network-online.target - Mises à jour système effectuées sur storage-01 et gpu-01
Prochaines étapes (après session matin)
- Configurer Claude (API Anthropic) comme second modèle dans Hermes
- Route statique Freebox :
192.168.10.0/24→192.168.1.200 - Premier commit GitHub (repo privé)
- Stubs rôles :
postgresql,qdrant,minio
2026-05-11 — Session après-midi : llama-server ROCm (remplacement LM Studio)
Résumé
Remplacement complet de LM Studio pour l'inférence par llama-server compilé from
source avec ROCm 7.x. Gain de performance ×5 (15 → 70 tok/s génération). Résolution
du bug de contexte --parallel 4 qui limitait chaque slot à 8192 tokens.
Blocages rencontrés et résolus
6. Hermes — temps de réponse de 2 minutes
Symptôme : Hermes met ~2 minutes pour répondre, même sur des requêtes courtes.
Investigation :
nvtop: GPU à 90% pendant les 2 minutes → le GPU est utilisé, pas d'idlejournalctl -u lm-studio: backend = Vulkan (pas ROCm)- Comparaison : Vulkan ~100 tok/s prefill / ~15 tok/s génération ; Hermes system prompt ≈ 15 000 tokens → ~2 min juste pour le prefill (15 000 ÷ 100 ≈ 150s)
Tentative 1 — activer le backend ROCm de LM Studio :
lms runtime get lmstudio-community/gguf-parser --release latest: backends dans.lmstudio/extensions/backends/(Node.js addons + libs partagées)- Backend ROCm compilé pour ROCm 6.x :
libhipblas.so.2,librocblas.so.4,libamdhip64.so.6— ROCm 7.2.3 installé expose.so.3,.so.5,.so.7 - Tentative patchelf pour corriger les SONAME : fixe les DT_NEEDED entries mais incompatibilité ABI profonde (structs, signatures de fonctions changées entre ROCm 6 et 7 — SONAME seul n'est pas suffisant)
- Résultat : crash GPU au warmup (
Exit code: null= SIGSEGV) pour tous les modèles
Fix final — llama.cpp compilé from source avec ROCm 7.x (option B) :
7. Build llama.cpp from source (ROCm 7.x)
Packages requis (non installés initialement) :
sudo dnf install -y hipblas-devel rocblas-devel hip-devel hipcc cmake gcc-c++ git patchelf
Problème cmake — HIP compiler :
cmake -DCMAKE_HIP_COMPILER=hipcc→ rejeté (pas dans PATH, cmake ne trouve pas)- Fix : utiliser le chemin absolu
{{ rocm_path }}/llvm/bin/clang++
Commande de build finale :
cd /opt/llama.cpp
cmake -B build \
-DGGML_HIP=ON \
-DAMDGPU_TARGETS=gfx1030 \
-DCMAKE_BUILD_TYPE=Release \
-DROCM_PATH=/opt/rocm \
-DCMAKE_PREFIX_PATH=/opt/rocm \
-DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++
cmake --build build --target llama-server -j$(nproc)
Note gfx1031 → gfx1030 : RX 6700XT = gfx1031 (non officiel). Target cmake =
gfx1030 (compatible) + HSA_OVERRIDE_GFX_VERSION=10.3.0 dans le service systemd.
Autre problème : après systemctl stop lm-studio, le processus llmster
survivait (double-fork) et tenait le port 1234.
Fix : sudo kill $(pgrep llmster) && sudo kill $(pgrep socat)
8. Tentative Qwen3.5-9B sur llama-server — crash ROCm
Symptôme : llama-server crash au warmup avec Qwen3.5-9B (ggml_cuda_op_mul_mat
assertion failure).
Cause : Qwen3.5-9B utilise l'architecture Gated Delta Net (hybride SSM + attention) — non supportée par le backend ROCm de llama.cpp. Confirmé : crash identique avec plusieurs versions GGUF de ce modèle.
Fix : Passage sur Qwen2.5-7B-Instruct (transformer standard, GQA classique, aucun problème ROCm). Modèle déjà présent sur le NFS.
9. Bug --parallel 4 — contexte insuffisant pour Hermes
Symptôme : Hermes renvoie HTTP 400 context window exceeded même avec
--ctx-size 32768. System prompt Hermes = 14 579 tokens.
Cause : --parallel N divise le ctx-size total en N slots égaux.
Avec --parallel 4 --ctx-size 32768 → chaque slot = 8192 tokens.
14 579 > 8192 → dépassement dès le premier appel Hermes.
Fix :
sudo sed -i 's/--parallel 4/--parallel 1/' /etc/systemd/system/llama-server.service
sudo systemctl daemon-reload && sudo systemctl restart llama-server
Corrigé aussi dans roles/llama_server/defaults/main.yml : llama_parallel: 1.
Performances finales (RX 6700XT, ROCm 7.x natif)
| Backend | Prefill | Génération | TTFT Hermes (15k tokens) |
|---|---|---|---|
| LM Studio Vulkan | ~100 tok/s | ~15 tok/s | ~150s |
| llama-server ROCm 7.x | ~400 tok/s | ~70 tok/s | ~35s |
Gain ×4 prefill, ×4.5 génération — gain ×4 sur le TTFT Hermes.
Changements ansible
| Fichier | Changement |
|---|---|
roles/llama_server/tasks/main.yml |
Création — build ROCm, service systemd, firewall, désactive lm-studio |
roles/llama_server/defaults/main.yml |
Création — llama_parallel: 1 (critique : 4 → context bug) |
roles/llama_server/templates/llama-server.service.j2 |
Création — template service systemd |
roles/rocm/defaults/main.yml |
Ajout rocm_build_packages (hipblas-devel, rocblas-devel, hip-devel, hipcc) |
roles/hermes_agent/defaults/main.yml |
Modèle → qwen2.5-7b-instruct |
playbooks/gpu-01.yml |
Remplacement rôle lm_studio → llama_server |
host_vars/gpu-01/vars.yml |
Suppression vars lm_studio, ajout llama_model_path + llama_model_alias |
group_vars/gpu_hosts/vars.yml |
Suppression vars lm_studio (port, host, context) |
admin/llama_server.md |
Création — doc ops complète |
admin/lm_studio.md |
Refonte — gestion modèles uniquement, service désactivé |
admin/hermes.md |
Mise à jour — modèle, config.yaml, endpoint llama-server |
admin/systeme.md |
Table services gpu-01 : lm-studio → llama-server |
admin/incidents.md |
Création — incident storage-01 XFS + résolution |
admin/README.md |
Ajout entrées llama_server.md et incidents.md |
État final
- llama-server opérationnel sur gpu-01 (
192.168.10.20:1234) — API OpenAI-compatible - Modèle : Qwen2.5-7B-Instruct Q4_K_S — 32768 tokens ctx, 1 slot (requis pour Hermes)
- Hermes : fonctionne correctement, réponse en ~35s (vs 2 min avant)
- LM Studio : désactivé pour l'inférence, reste installé pour
lms get/lms ls
Prochaines étapes (après session matin, avant session LiteLLM)
Configurer Claude (API Anthropic) comme second modèle dans Hermes✅ fait- Route statique Freebox :
192.168.10.0/24→192.168.1.200 - Premier commit GitHub (repo privé)
- Stubs rôles :
postgresql,qdrant,minio
2026-05-11 — Session soir : LiteLLM proxy + intégration Claude API
Résumé
Déploiement de LiteLLM comme proxy unifié sur storage-01. Hermes passe désormais par LiteLLM pour toute inférence — Qwen local par défaut, Claude API switchable sans toucher à la configuration de Hermes.
Blocages rencontrés et résolus
10. Hermes ne résout pas le provider LiteLLM
Symptôme : hermes -z échoue avec AuthError: No inference provider configured
même avec LM_BASE_URL=http://127.0.0.1:4000/v1 dans le .env.
Investigation :
hermes doctoravecLM_API_KEY=lm-studio→ "✓ API key or custom endpoint configured"hermes doctoravecLM_API_KEY=funk-litellm→ pas reconnu
Cause 1 : LM_API_KEY doit valoir lm-studio exactement — c'est la valeur
magic qui active le provider "LM Studio" (OpenAI-compatible custom endpoint) dans Hermes.
Toute autre valeur n'est pas reconnue.
Cause 2 : config.yaml manquait model.provider: lmstudio et model.base_url.
Sans ces champs, Hermes ne sait pas quel provider utiliser même si .env est correct.
Format config.yaml correct :
model:
provider: lmstudio
base_url: "http://127.0.0.1:4000/v1"
default: "hermes-default"
context_length: 65536
Fix :
LM_API_KEY=lm-studiodans.env(géré parlineinfileAnsible)litellm_master_key: lm-studiopour que LiteLLM accepte les requêtes Hermes- Ajout de
provideretbase_urldans le templateconfig.yaml.j2
11. PermissionError hermes -z depuis le mauvais répertoire
Symptôme : PermissionError: [Errno 13] Permission denied: '/home/ansible/.git'
Cause : hermes -z remonte l'arborescence pour trouver un .git. Lancé depuis
/home/ansible, l'utilisateur hermes n'a pas accès.
Fix : Toujours lancer depuis /srv/data/hermes :
cd /srv/data/hermes && hermes -z "question"
12. 404 sur /api/v1/models à l'init Hermes
Symptôme : Logs LiteLLM montrent des GET /api/v1/models HTTP/1.1" 404.
Cause : Hermes appelle /api/v1/models lors de l'initialisation — LiteLLM
n'expose que /v1/models. Ce 404 n'est pas bloquant, les requêtes
POST /v1/chat/completions fonctionnent normalement.
Fix : Aucun — comportement Hermes attendu, sans impact fonctionnel.
Architecture finale LiteLLM
Hermes (hermes-default)
│ LM_API_KEY=lm-studio
│ LM_BASE_URL=http://127.0.0.1:4000/v1
▼
LiteLLM (:4000) master_key=lm-studio
│
├── hermes-default → qwen2.5-7b-instruct → llama-server gpu-01 (défaut)
├── hermes-default → claude-sonnet-4-6 → api.anthropic.com (switch)
└── [autres modèles accessibles par nom direct]
Switch sans toucher Hermes : modifier uniquement le bloc hermes-default
dans /etc/litellm/config.yaml + sudo systemctl restart litellm.
Proof : Qwen répond "Je suis Claude" (hallucination sur l'identité — comportement normal des 7B). La GPU qui chauffe sur gpu-01 est la vraie source de vérité.
Changements ansible
| Fichier | Changement |
|---|---|
roles/litellm/ |
Création complète du rôle (defaults, tasks, handlers, templates) |
roles/litellm/templates/config.yaml.j2 |
Alias hermes-default + 3 modèles |
roles/litellm/defaults/main.yml |
master_key: lm-studio, host: 127.0.0.1 |
roles/hermes_agent/defaults/main.yml |
hermes_lm_base_url: 127.0.0.1:4000, api_key: lm-studio |
roles/hermes_agent/templates/config.yaml.j2 |
Ajout provider: lmstudio, base_url, model.default |
roles/hermes_agent/templates/hermes-agent.service.j2 |
LM_BASE_URL/LM_API_KEY (pas HERMES_LLM_*), gateway run |
roles/hermes_agent/tasks/main.yml |
Tâches lineinfile pour .env (LM_BASE_URL, LM_API_KEY) |
playbooks/storage-01.yml |
Rôle litellm ajouté avant hermes_agent |
group_vars/all/vault.yml |
vault_anthropic_api_key (clé Anthropic chiffrée) |
admin/hermes.md |
Refonte complète — architecture LiteLLM, config correcte, switch |
admin/litellm.md |
Correction master_key, procédure switch, points d'attention |
État final
- LiteLLM opérationnel sur storage-01 (:4000), 4 modèles enregistrés
- Hermes utilise Qwen local via LiteLLM — ~70 tok/s, gratuit
- Claude Sonnet/Opus disponibles via switch LiteLLM uniquement (clé API Anthropic en vault)
- Vault Ansible :
vault_anthropic_api_keychiffré dansgroup_vars/all/vault.yml
Prochaines étapes
- Route statique Freebox :
192.168.10.0/24→192.168.1.200 - Premier commit GitHub (repo privé)
- Mémoire Hermes sur cluster : Qdrant (vectoriel) + PostgreSQL sur storage-01, embeddings via Qwen, retrieval via Claude API pour les tâches complexes
- Stubs rôles :
postgresql,qdrant,minio
2026-05-11 — Session nuit : Qdrant + PostgreSQL + embeddings + upgrade 14B
Résumé
Déploiement de la stack mémoire vectorielle complète pour Hermes : PostgreSQL 16 et Qdrant v1.13.4 sur storage-01 (RAID5). Activation des embeddings sur llama-server. Installation du skill qdrant-vector-search dans Hermes. Indexation de 17 chunks de connaissance dans Qdrant. Upgrade modèle Qwen2.5-7B → 14B.
Blocages rencontrés et résolus
13. Embeddings llama-server — erreur 400 pooling
Symptôme : curl /v1/embeddings → 400 Pooling type 'none' is not OAI compatible
Cause : Sur les LLM causaux (Qwen2.5), le pooling par défaut de llama-server est
none (pas de agrégation des token embeddings). Le format OAI attend un vecteur unique.
Fix : Ajout du flag --pooling mean dans le template service llama-server.
Variables Ansible : llama_embeddings: true + llama_pooling: "mean".
14. qdrant-client non installé dans le venv Hermes
Symptôme : ModuleNotFoundError: No module named 'qdrant_client' lors des tests Python.
Cause : Le skill qdrant-vector-search fournit la documentation API à Hermes mais n'installe pas automatiquement qdrant-client dans tous les environnements.
Fix : Installation manuelle + ajout dans le rôle Ansible :
/srv/data/hermes/hermes-agent/venv/bin/pip install "qdrant-client>=1.12.0"
Tâche ansible.builtin.pip ajoutée dans roles/hermes_agent/tasks/main.yml.
15. Hermes utilise execute_code (mauvais env Python) au lieu du terminal
Symptôme : Hermes échoue en boucle quand on lui demande d'utiliser qdrant-client.
Il utilise l'outil 🐍 execute_code qui tourne dans son propre env Python sans qdrant-client.
Cause : Qwen 7B ne sait pas distinguer quand utiliser execute_code vs terminal.
Sur les tâches avec imports externes, il faut forcer l'approche "écrire un script + terminal".
Workaround : Écrire le script dans /tmp/script.py puis exécuter via terminal avec
le chemin complet /srv/data/hermes/hermes-agent/venv/bin/python /tmp/script.py.
Fix structurel : PATH du venv ajouté dans le service systemd hermes-agent et
dans ~/.bashrc de l'utilisateur hermes.
16. Segfault llama-server avec Qwen2.5-14B à 32k contexte
Symptôme : code=dumped, signal=SEGV au démarrage du service après upgrade 14B.
Cause : VRAM insuffisante. Qwen2.5-14B Q4_K_M ≈ 8 GB + KV cache 32k ≈ 6 GB = ~14 GB → dépasse les 12 GB de la RX 6700XT.
Fix : Réduction du contexte à 16384 tokens dans host_vars/gpu-01/vars.yml :
llama_ctx_size: 16384
Budget VRAM résultant : ~8 GB (modèle) + ~3 GB (KV cache) = ~11 GB ✅
17. qdrant-client API : client.search() supprimé en v1.14+
Découverte : qdrant-client v1.18 (installé dans le venv) vs serveur Qdrant v1.13.4.
La méthode client.search() a été supprimée en v1.14 — utiliser client.query_points().
Note : Warning de compatibilité de version (majeure OK, mineure diff > 1) affiché au démarrage du client mais sans impact fonctionnel sur les opérations de base.
Réalisations
Qdrant opérationnel
- Collection
hermes_memorycréée : dim=5120 (14B), distance=COSINE - 17 chunks de connaissance indexés (architecture, Hermes, LiteLLM, ROCm, PostgreSQL, Ansible)
- Recherche sémantique validée : score 0.82-0.85 sur requêtes pertinentes
- Script d'indexation :
admin/index_knowledge.py
Skill Hermes
mlops/qdrant(qdrant-vector-search) installé et actif- qdrant-client v1.18 dans le venv Hermes
- PATH venv dans service systemd et bashrc
Script hermes-switch
/usr/local/bin/hermes-switch [qwen|claude|status]- Switch LiteLLM + restart automatique
- Déployé via
roles/litellm/files/hermes-switch
Upgrade modèle
- Qwen2.5-7B → Qwen2.5-14B Q4_K_M
- Contexte : 32768 → 16384 (contrainte VRAM)
- Alias API :
qwen2.5-14b-instruct
Changements ansible
| Fichier | Changement |
|---|---|
roles/llama_server/defaults/main.yml |
Ajout llama_embeddings: true, llama_pooling: mean |
roles/llama_server/templates/llama-server.service.j2 |
Flags --embeddings --pooling mean conditionnels |
roles/hermes_agent/tasks/main.yml |
pip install qdrant-client, PATH venv dans bashrc |
roles/hermes_agent/templates/hermes-agent.service.j2 |
Environment=PATH avec venv en premier |
roles/litellm/files/hermes-switch |
Création script switch modèle |
roles/litellm/tasks/main.yml |
Déploiement hermes-switch |
roles/litellm/templates/config.yaml.j2 |
Alias qwen2.5-14b-instruct (remplace 7b) |
host_vars/gpu-01/vars.yml |
Modèle 14B, llama_ctx_size: 16384 |
admin/index_knowledge.py |
Script indexation Qdrant (17 chunks) |
admin/llama_server.md |
Modèle 14B, embeddings, contrainte VRAM documentés |
admin/litellm.md |
hermes-switch documenté, alias mis à jour |
admin/hermes.md |
Section skill Qdrant ajoutée |
État final
- PostgreSQL 16 : opérationnel sur storage-01 (RAID5
/srv/data/postgres) - Qdrant v1.13.4 : opérationnel sur storage-01 (RAID5
/srv/data/qdrant) - llama-server : Qwen2.5-14B, 16384 ctx, embeddings activés
- Hermes : skill qdrant actif, 17 chunks indexés dans
hermes_memory - hermes-switch : déployé et fonctionnel
Prochaines étapes
- Route statique Freebox :
192.168.10.0/24→192.168.1.200 - Premier commit GitHub (repo privé)
- Tester Hermes 14B sur les tâches agent complexes (Qdrant queries)
- Mettre à jour la dimension des vecteurs dans index_knowledge.py (5120 pour 14B vs 3584 pour 7B)
- MinIO : rôle à déployer