# Funk Lab

> Homelab IA — Kubernetes (Talos) + GPU AMD + Agent autonome (Hermes)

![Funk Lab](docs/funk-lab.jpg)

## Vue d'ensemble

**Funk** est un homelab orienté IA composé de 5 machines physiques :

- un nœud de stockage/passerelle qui héberge tous les services de données et l'agent IA
- un nœud GPU dédié à l'inférence LLM local (AMD RX 6700XT)
- trois nœuds qui forment un cluster Kubernetes (Talos Linux)

L'ensemble est géré en Infrastructure-as-Code : **Ansible** pour les hôtes AlmaLinux, **talhelper** pour Talos, **ArgoCD** pour les workloads Kubernetes.

L'interface du homelab est **Asa**, un assistant **vocal agentique** (STT client‑serveur) qui sait interroger l'état live, chercher sur le web et agir sur l'infra — voir [Asa (STT)](#asa--assistant-vocal-agentique-stt).

---

## Architecture

```
                    Internet
                        │
                   [ Freebox ]  192.168.1.254
                        │  port 1 · VLAN 1
        ┌───────────────┴────────────────────────────┐
        │           Switch Netgear GS308EV4           │  8 ports Gigabit · managé Plus
        │         8 ports Access (sans trunk)         │  VLAN 1 (Default) + VLAN 10 (cluster-funk)
        └───┬───┬─────────────────────────────┬───────┘
     VLAN 1 │   │ VLAN 10                     │ ports 3-6 · VLAN 10 (cluster 192.168.10.0/24)
    enp4s0  │   │ enp6s0f3u2c2     ┌──────────┴───────────────────────┐
   (port 2) │   │ (port 7)         │                                  │
         ┌──┴───┴──┐         [ compute-01 ] [ compute-02 ] [ compute-03 ]   [ gpu-01 ]
         │storage-01│        192.168.10.11  192.168.10.12  192.168.10.13   192.168.10.20
         │AlmaLinux │        control-plane     worker         worker       llama-server
         │Gateway·  │           Talos Linux · Kubernetes                   AMD RX 6700XT
         │NAT·DNS·  │
         │NFS·PG·   │
         │Qdrant·   │
         │LiteLLM·  │
         │Hermes    │
         └──────────┘
```

**Câblage du switch (Netgear GS308EV4)** — tous les ports en mode **Access** (pas de trunk). storage-01 utilise **deux NIC physiques**, une par VLAN (pas de sous-interface taguée) :

| Port | Connecté à | IP | VLAN |
|---|---|---|---|
| 1 | Freebox | — | 1 · Default |
| 2 | storage-01 — NIC1 `enp4s0` (WAN) | `192.168.1.200` | 1 · Default |
| 3 | gpu-01 | `192.168.10.20` | 10 · cluster-funk |
| 4 | compute-01 (control-plane) | `192.168.10.11` | 10 · cluster-funk |
| 5 | compute-02 (worker) | `192.168.10.12` | 10 · cluster-funk |
| 6 | compute-03 (worker) | `192.168.10.13` | 10 · cluster-funk |
| 7 | storage-01 — NIC2 `enp6s0f3u2c2` (LAN cluster) | `192.168.10.1` | 10 · cluster-funk |
| 8 | PC perso (poste admin) | `192.168.1.x` | 1 · Default |

> storage-01 fait **passerelle / NAT** entre la VLAN 1 (LAN domestique `192.168.1.0/24`, côté Freebox) et la VLAN 10 (cluster `192.168.10.0/24`).

---

## Stack IA

```
                    ┌─────────────────────────────────────┐
                    │  Hermes Agent (storage-01)           │
                    │                                      │
                    │  profil funk-ai (Qwen3-8B GPU)       │
                    │       │  ask-agent system/monitor    │
                    │       ▼                              │
                    │  profil system  (Qwen3-1.7B CPU)     │
                    │  profil monitor (Qwen3-1.7B CPU)     │
                    │  profil brain   (Claude Sonnet API)  │
                    └──────────────┬──────────────────────┘
                                   │
                         LiteLLM Proxy :4000
                                   │
              ┌────────────────────┼────────────────────┐
              │                    │                    │
       qwen3-8b GPU          qwen3-1.7b CPU       Claude API
       gpu-01 :1234     :1236 system / :1237 monitor  Anthropic
```

| Composant | Rôle | Hôte |
|---|---|---|
| **llama-server** | Inférence GPU (:1234, qwen3-8b — ROCm 7.x) + embeddings dédiés nomic-embed-text (:1238, CPU) + watchdog/heartbeat. Instances CPU :1236/:1237 (profils `system`/`monitor`) **manuelles, souvent éteintes** | gpu-01 |
| **LiteLLM** | Proxy API — routing local/cloud transparent | storage-01 |
| **Hermes Agent** | Agent IA multi-profils avec mémoire vectorielle | storage-01 |
| **ask-agent** | Délégation inter-agents (system / monitor / brain) | storage-01 |
| **STT** | Assistant vocal « Jarvis » — client `stt` (poste) + STT-server in-cluster | k8s (`ai`) |
| **Qdrant** | Base vectorielle — RAG (`funk-docs`) + mémoire STT (`stt-memory`) | storage-01 |
| **PostgreSQL 16** | Persistance Hermes, LiteLLM, n8n, Open WebUI, Ghostfolio | storage-01 |

### Profils Hermes

| Profil | Modèle | Usage |
|---|---|---|
| `funk-ai` | Qwen3-8B GPU (~35 tok/s) | Agent principal — par défaut |
| `system` | Qwen3-1.7B CPU (~21 tok/s) | Calculs rapides, formatage, résumés |
| `monitor` | Qwen3-1.7B CPU (~21 tok/s) | Analyse logs, métriques, santé système |
| `brain` | Claude Sonnet 4.6 (API) | Raisonnement complexe, architecture |

---

## Asa — assistant vocal agentique (STT)

**Asa** est l'interface **vocale et graphique** de Funk : un assistant type *Jarvis* qui écoute,
parle et affiche un **HUD** animé. Architecture **client‑serveur** — un client `stt` (pipx) sur le
poste (voix + HUD), un **STT‑server in‑cluster** (le « cerveau »). 100 % local (Qwen3‑8B).

![Asa — HUD](docs/img/stt-hud-conversation.png)

Au‑delà du chat, Asa est une véritable **agente** : le modèle **choisit ses outils** (function
calling) pour répondre à partir de données réelles.

| Capacité | Détail |
|---|---|
| 🔎 **Savoir** | doc Funk (RAG) + **état live** (santé machines/GPU, cluster, PromQL) |
| 🌐 **Chercher** | recherche Internet **privée** (SearXNG self‑host) |
| 🤖 **Agir** | actions d'admin via Hermes, **toujours derrière une confirmation vocale** |
| 🧠 **Mémoire** | court terme (session) + long terme (Qdrant `stt-memory`) |
| 🗣️ **Veille/réveil** | « Asa » réveille · « Asa stop » endort |

📖 **Présentation complète (schémas de la chaîne + captures)** : [`docs/stt-presentation.md`](docs/stt-presentation.md)
· 🗣️ **Ce que tu peux lui demander** : [`docs/asa-commandes.md`](docs/asa-commandes.md)
· Détails techniques : [`admin/ia/stt.md`](admin/ia/stt.md)

---

## Infrastructure

### Machines

| Machine | IP | OS | RAM | Rôle |
|---|---|---|---|---|
| storage-01 | 192.168.10.1 | AlmaLinux 9.8 | 32 GB | Gateway · NFS · Services IA |
| gpu-01 | 192.168.10.20 | AlmaLinux 9.8 | 32 GB | Inférence LLM (RX 6700XT) |
| compute-01 | 192.168.10.11 | Talos v1.13 (k8s v1.33.1) | 16 GB | k8s control-plane |
| compute-02 | 192.168.10.12 | Talos v1.13 (k8s v1.33.1) | 8 GB | k8s worker |
| compute-03 | 192.168.10.13 | Talos v1.13 (k8s v1.33.1) | 8 GB | k8s worker |

### Stockage

- NVMe système (`/home/data`, ~845 GB XFS) — bind-monté sur `/srv/data` depuis 2026-05-29
- RAID5 logiciel (`md0`) — 4 × 1 TB SATA, actif mais plus monté (sdb ATA errors + sdd retiré)
- Point de montage `/srv/data` — héberge modèles LLM, bases de données, données Hermes

---

## Organisation du repo

```
├── ansible/              # Gestion storage-01 + gpu-01 (AlmaLinux)
│   ├── inventory.yml
│   ├── playbooks/
│   │   ├── storage-01.yml
│   │   └── gpu-01.yml
│   ├── group_vars/       # Variables globales + vault chiffré
│   ├── host_vars/        # Variables par machine
│   └── roles/
│       ├── common        # Paquets, sécurité, NTP
│       ├── gateway       # nftables + NAT
│       ├── dnsmasq       # DNS local (lab.local)
│       ├── nfs_server    # Exports NFS (modèles, k8s)
│       ├── nfs_client    # Montages NFS sur gpu-01
│       ├── rocm          # Stack GPU AMD (ROCm 7.x)
│       ├── llama_server  # Inférence LLM + embeddings
│       ├── litellm       # Proxy LLM + script ask-agent
│       ├── hermes_agent  # Agent IA autonome
│       ├── hermes_user   # User hermes + SSH partagé entre hosts
│       ├── rag           # RAG : scripts rag-ingest/rag-query + skill Hermes
│       ├── alertmanager_webhook  # Pont AlertManager → ask-agent
│       ├── postfix_relay # SMTP sortant via Gmail
│       ├── node_exporter # Métriques Prometheus
│       ├── minio         # ⚠️ placeholder vide — service non déployé
│       ├── postgresql    # Base de données
│       ├── qdrant        # Base vectorielle
│       └── hermes_auto_improve  # Worker analyse doc + trigger server :9095
├── hermes-skills/        # Skills Hermes versionnés
│   ├── funk/
│   │   ├── agent-delegation/  # Délégation ask-agent (system/monitor/brain)
│   │   └── rag-docs/          # Usage de rag-query
│   └── souls/            # Prompts système par profil (funk-ai, system, monitor, brain)
├── tools/                # Sources des outils déployés
│   ├── hermes-auto-improve/   # auto-improve.py + trigger-server.py
│   └── hermes-voice/          # Client vocal Hermes (CLI faster-whisper + Piper)
├── stt/                  # Asa — assistant vocal agentique (voir docs/stt-presentation.md)
│   ├── client/           # commande `stt` (pipx) : voix (VAD/wake/ASR/TTS) + HUD web
│   └── server/           # STT-server (conteneur, ns ai) : brain + tools + RAG + mémoire
├── admin/                # Base de connaissance (indexée dans le RAG)
├── docs/                 # Présentations & schémas (stt-presentation.md, images)
├── progress/             # Journaux de session (référencés par PROGRESS.md)
├── archive/              # Documents historiques obsolètes (conception d'origine) — ne fait pas foi
├── talos/                # Cluster Kubernetes (talhelper)
└── k8s/                  # Workloads ArgoCD
    ├── infra/            # monitoring, nfs-provisioner (Traefik + MetalLB via helm au bootstrap)
    └── apps/             # Open WebUI, n8n, STT, SearXNG, Ghostfolio, external-services
```

---

## Démarrage rapide

### Pré-requis

```bash
pip install ansible
ansible-galaxy collection install ansible.posix community.general community.postgresql
```

### Déployer storage-01

```bash
cd ansible/
ansible-playbook -i inventory.yml playbooks/storage-01.yml
```

### Déployer gpu-01

```bash
ansible-playbook -i inventory.yml playbooks/gpu-01.yml
```

### Déployer un rôle spécifique

```bash
ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags litellm
ansible-playbook -i inventory.yml playbooks/gpu-01.yml --tags llama_server
```

### Gestion du cluster

```bash
funk-start          # Allume gpu-01 (WOL) + démarre tous les services
funk-start --k8s    # Idem + démarre les nœuds k8s
funk-stop           # Arrêt propre
funk-stop --k8s     # Arrêt propre + éteint k8s avant
funk-status         # État de tous les services (13 checks)
```

> Détail complet des procédures : [admin/ops/cluster.md](admin/ops/cluster.md)

---

## Secrets

Les secrets sont gérés via **Ansible Vault** (chiffrement AES256).

```bash
# Chiffrer le vault
ansible-vault encrypt ansible/group_vars/all/vault.yml

# Editer les secrets
ansible-vault edit ansible/group_vars/all/vault.yml
```

Variables stockées dans le vault : `vault_anthropic_api_key`, `vault_pg_hermes_password`, `vault_pg_litellm_password`, `vault_pg_n8n_password`, `vault_pg_openwebui_password`, `vault_pg_ghostfolio_password`, `vault_postfix_relay_password`, `vault_n8n_api_key`.

---

## Contrainte GPU

La RX 6700XT (gfx1031) n'est pas officiellement supportée par ROCm. La variable suivante est obligatoire dans tous les services GPU :

```
HSA_OVERRIDE_GFX_VERSION=10.3.0
```

Elle est gérée automatiquement par le rôle Ansible `llama_server`.

---

## Roadmap

- [x] Ansible — storage-01 (gateway, NFS, PostgreSQL, Qdrant, LiteLLM, Hermes, postfix, AlertManager webhook)
- [x] Ansible — gpu-01 (ROCm, llama-server : GPU :1234 + embeddings :1238 via le rôle ; 2× CPU manuels)
- [x] Stack IA opérationnelle (Hermes multi-profils + Qwen3 + Claude API)
- [x] Délégation inter-agents (`ask-agent` + skill `agent-delegation`)
- [x] RAG opérationnel — Qdrant ~436 chunks (embeddings nomic-embed-text `:1238`, dim 768), `rag-query` / `rag-ingest`
- [x] User `hermes` avec SSH partagé entre storage-01 et gpu-01
- [x] Cluster Kubernetes opérationnel (Talos v1.13 + talhelper)
- [x] MetalLB + Traefik + wildcard DNS `lab.local`
- [x] Monitoring complet (kube-prometheus-stack + AlertManager → Hermes + n8n)
- [x] Open WebUI (`openwebui.lab.local`, namespace `ai`)
- [x] n8n (`n8n.lab.local`, namespace `ai`, 3 workflows actifs)
- [x] Email sortant — Postfix relay → Gmail SMTP
- [x] hermes-auto-improve — analyse quotidienne de la doc par Hermes (n8n 22h → PR `hermes/daily-work`)
- [x] Ghostfolio — suivi de portefeuille (`ghostfolio.lab.local`, PostgreSQL s01 + Redis)
- [x] STT — assistant vocal « Jarvis » (client `stt` + STT-server in-cluster, mémoire Qdrant `stt-memory`)
- [x] Modèle d'embedding dédié — nomic-embed-text sur gpu-01 `:1238` (utilisé par la mémoire STT **et le RAG**)
- [x] Réparer Qdrant — crash-loop résolu 2026-06-17 ; collection `funk-docs` **reconstruite** depuis (436 chunks)
- [x] Re-ingérer le RAG (`rag-ingest`) **basculé sur l'embedding dédié `:1238`** (dim 768)
- [ ] WOL compute nodes (configuration BIOS)
- [ ] Route statique Freebox
