Commit graph

276 commits

Author SHA1 Message Date
73e2ad30db
feat(finlab): boutons d'analyse rapide (digest/analyse/plan/fondamental/opport./alertes) (#70)
Panneau « Actions rapides » sur le dashboard : 6 boutons one-click qui renvoient
une analyse finlab instantanée (déterministe, gratuite, sans LLM), dans une modale.

- 📊 Analyse (action sélectionnée) : technique (RSI/MACD/MM/signaux) + fondamental
  (PER/PEG/ROE/marges/dette/reco/cible) + plan R:R, combinés en une fiche
- 📐 Plan R:R, 📈 Fondamentaux (action sélectionnée) ; 📋 Digest, 🔭 Opportunités,
  🔔 Alertes (global)
- Backend : endpoints /api/digest, /api/fundamentals, /api/analyze
- Fix : fundamental.compare_portfolio() utilisait l'ancien schéma mono-compte
  (pf["positions"]) → cassé par le multi-comptes ; passe par data.portfolio_tickers()

Vérifié : endpoints OK (TestClient), rendu modale au navigateur (Playwright) —
fiche Analyse NVDA complète (technique + fondamental + plan). JS node --check OK.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:51:40 +02:00
github-actions[bot]
b09d3db1fa ci(finlab): image → sha-8bbfe88 [skip ci] 2026-06-30 09:03:27 +00:00
8bbfe88a30
feat(finlab): import de relevé par image (upload dashboard → scan Console IA) (#69)
Process dédié pour actualiser le portefeuille à partir d'une capture de relevé :

- Dashboard : bouton « 📥 Importer un relevé » (panneau Mes comptes) → upload de
  l'image vers le workspace (imports/) via POST /api/import ; une modale donne la
  phrase exacte à dire à la console + un lien direct
- Backend : endpoints /api/import (UploadFile, refuse les non-images) et /api/imports
  (listing), dossier imports/ sous FINLAB_HOME (workspace, persistant)
- Persona console (CLAUDE.md) : workflow « Import d'un relevé » — lire l'image,
  extraire compte/positions, mapper vers tickers Yahoo en VÉRIFIANT les cours,
  confirmer, puis mettre à jour portfolio.yaml

Le scan vision se fait dans la Console IA (abonnement, pas de clé API). Le dashboard
n'est que le pont d'upload. Testé : POST image→200 + listing OK, non-image→400 ;
JS node --check OK.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:00:29 +02:00
github-actions[bot]
2228613050 ci(finlab): image → sha-c182bac [skip ci] 2026-06-29 23:43:08 +00:00
451910901e
chore(finlab): + de CPU pour la console (1→2 cœurs, throttle ~11% mesuré) (#68)
Console Claude Code throttlée ~11% en pic à 1 cœur (CPU moy ~0,2, RAM ~384 Mo —
non contrainte). On double le plafond CPU pour fluidifier les bursts ; un peu de
marge RAM. Mesuré via Prometheus. (La latence du modèle reste côté API.)

requests cpu 150m→300m, mem 512Mi→768Mi ; limits cpu 1→2, mem 1536Mi→2Gi.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:40:32 +02:00
c182bac1d2
feat(finlab): portefeuilles multi-comptes (courtier/banque) + onglets dashboard (#67)
Permet de suivre plusieurs comptes (courtiers/banques) côte à côte, avec vue par
compte et agrégée. Ajoute le PEA BNP Paribas à côté de Revolut.

- portfolio.yaml : nouveau format `accounts` (name/type/cash/positions par compte).
  Compat ascendante : ancien format (cash/positions à la racine) lu comme compte
  unique « Principal »
- data.py : load_accounts(), base_currency(), portfolio_tickers() (union dédupliquée)
- tracker.py : build_account / build_all (par compte + agrégat global) ; build()
  reste la vue globale (digest/report) ; report() multi-comptes
- scanner/technical/alerts : helpers « portfolio » → union de tous les comptes
- dashboard /api/portfolio : renvoie accounts[] (positions+agg par compte) + global
- dashboard front : onglets de comptes (Tous / Revolut / BNP Paribas), KPI + positions
  + secteurs qui suivent le périmètre sélectionné, badge de type par ligne en vue Tous
- BNP Paribas (PEA) saisi depuis la capture : AI/PAEEM/DCAM/PCEU/PSP5/BNP/ENGI/TTE .PA
  (tickers Yahoo vérifiés, cours conformes à la capture)

Vérifié : totaux conformes à la capture BNP (6 395,49 € ; P&L -39 €), global 22 444 € ;
bascule d'onglet OK au navigateur (Playwright) ; compile + JS + CLI report OK.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:40:20 +02:00
github-actions[bot]
ba45ca198b ci(finlab): image → sha-ff37d27 [skip ci] 2026-06-29 23:25:40 +00:00
ff37d27b04
feat(finlab): console embarquée sous le graphique + vue chaîne de valeur IA (#66)
- Dashboard : la console Claude Code (/console/) est désormais embarquée en iframe
  sous le graphique (boutons reconnecter / plein écran), en plus du lien topbar
- Nouvelle vue pleine largeur « Chaîne de valeur IA — par couche » : regroupe les
  watchlists par couche de l'électron au logiciel ( énergie → 🔧 puces →
  🏢 datacenter → 🔌 câbles/optique → ☁️ software), tickers cliquables colorés
  par biais (haussier/baissier), tooltip cours/RSI/MACD
- Backend : endpoint /api/layers (scan groupé par thème, ordre des couches)

Vérifié au navigateur (Playwright, instance locale) : layout complet OK —
portefeuille + graphique chandeliers MM50/200 + console embarquée + alertes +
5 cartes de couches colorées. Endpoints et JS validés.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:24:01 +02:00
github-actions[bot]
4c61758899 ci(finlab): image → sha-56cb32e [skip ci] 2026-06-29 22:59:57 +00:00
56cb32eacc
feat(finlab): dashboard web (graphiques/portefeuille/actions) + fix seed NFS (#65)
Ajoute une interface graphique à finlab, en plus de la console Claude Code (les
deux cohabitent sur finance.lab.local) :

- dashboard/ : backend FastAPI (sans LLM/clé) exposant les données finlab en JSON
  (/api/portfolio, /api/scan, /api/ohlc, /api/plan, /api/alerts) + front statique
  (lightweight-charts) : graphiques chandeliers MM50/200 + volume, portefeuille
  (P&L/poids/secteurs), watchlists thématiques scannées, alertes, plan de trade R:R
- Découplage code/data : finlab lit ses configs depuis FINLAB_HOME si défini
  (workspace persistant) ; repli sur le dossier du package en dév local
- Image double usage (un build, deux conteneurs) : code → /opt/app, seed minimal
  (configs + persona + MCP) → workspace
- Fix seed NFS (cause racine du workspace vide) : WORKDIR n'est plus sur le volume
  (le runtime le créait en root sur NFS) ; seed déplacé dans un initContainer
  robuste (recrée le dossier s'il est non-inscriptible)
- Deployment : initContainer seed + 2 conteneurs (console ttyd -b /console, dashboard
  uvicorn) ; Service 2 ports ; IngressRoute 2 routes (/console + dashboard), basicAuth

Validé en local (podman) : seed OK, dashboard /api/portfolio → 16k€/10 positions
via FINLAB_HOME, endpoints ohlc/scan/plan/alerts OK, ttyd/claude/finlab présents.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:57:23 +02:00
github-actions[bot]
607e5e6c0a ci(finlab): image → sha-54f6f7c [skip ci] 2026-06-29 21:29:00 +00:00
54f6f7c634
feat(finlab): console Claude Code finance in-cluster + toolkit d'analyse (#64)
* feat(finlab): console Claude Code finance in-cluster + toolkit d'analyse

Intègre finlab (ex-projet Projets/Finance) au lab comme une console Claude Code
web spécialisée finance — l'esprit OpenAlice, mais c'est le vrai Claude Code sur
l'abonnement (login persisté, pas d'API facturée), agentique, avec la boîte à
outils finlab (Yahoo Finance) branchée en MCP.

- tools/finlab/ : source finlab rapatriée + Dockerfile (Python 3.12 + Node +
  claude-code + ttyd) + persona workspace/CLAUDE.md + branchement MCP + entrypoint
  (seed du workspace no-clobber sur le PVC)
- .github/workflows/build-finlab.yml : build GHCR funk-finlab + bump manifest (main)
- k8s/apps/finlab/ : Deployment/Service/PVC/IngressRoute (finance.lab.local) +
  Middleware basicAuth (shell web protégé) ; PVC = HOME (login) + workspace
- k8s/apps-of-apps/apps/finlab.yaml : Application ArgoCD
- .mcp.json (racine) : outils finlab dans les sessions Claude Code du lab
- admin/ia/finlab.md + READMEs + CLAUDE.md : doc + enregistrement

Analyse/aide à la décision uniquement — aucun ordre réel (paper trading Alpaca
fictif seul exécutable).

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

* fix(finlab): ttyd absent des dépôts bookworm → binaire statique GitHub

Le build amont échouait (`E: Package 'ttyd' has no installation candidate`) :
ttyd n'est pas packagé dans Debian bookworm. On récupère le binaire statique
(musl, pin TTYD_VERSION=1.7.7) depuis les releases GitHub. Build complet validé
en local (podman) : ttyd 1.7.7, claude-code 2.1.195, import finlab + seed OK.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:26:09 +02:00
35ad1deb64
chore(openalice): suspend le déploiement (replicas 0), conserve tout (#63)
Projet mis en pause : Kraken via « CCXT Custom » non supporté (fetchBalance
avorte) + pas d'automatisation actions. Pod éteint, mais Deployment + PVC
openalice-data (config, clés scellées, données) conservés. Relance = replicas 1.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:05:57 +02:00
cc6141f424
fix(openalice): embarque les shims CLI alice* (bug packaging Dockerfile amont) (#62)
Le Dockerfile amont v0.60.0-beta.1 copie src/workspaces/templates dans l'image
runtime mais oublie src/workspaces/cli, alors que cliBinPath()
(/app/src/workspaces/cli/bin) est requis au runtime pour poser les shims
alice/alice-uta/traderhub sur le PATH des agents. Sans eux, l'agent ne peut pas
joindre le moteur de trading ("alice-uta introuvable sur mon PATH").

- build-openalice.yml : patch du Dockerfile cloné (COPY src/workspaces/cli) + guard
- tag image suffixé via IMAGE_PATCH=p1 → nouveau tag immuable, ArgoCD redéploie
- deployment.yaml → :v0.60.0-beta.1-p1
- doc admin/ia/openalice.md (correctifs locaux)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 17:03:45 +02:00
c2f7255da0
feat(openalice): déploiement in-cluster de l'agent IA financier (back-end finance d'Asa) (#61)
Déploie TraderAlice/OpenAlice (AGPL-3.0) dans le namespace ai comme back-end de
connaissances financières pour Asa, consommé via le connecteur MCP Ask.

- CI build-openalice.yml : rebuild depuis le Dockerfile amont (ref épinglée v0.60.0-beta.1)
  → ghcr.io/alkatrazz24/funk-openalice, bump manifest sur main
- k8s/apps/openalice : Deployment (restricted + fsGroup, Recreate), PVC 5Gi NFS /data,
  Service web:47331 + mcpask:3003, IngressRoute openalice.lab.local
- ArgoCD app + doc admin/ia/openalice.md (RAG) + MAJ CLAUDE.md
- Cerveau hybride : LiteLLM/Qwen3-8B local (gratuit) + Claude basculable ; données
  TraderHub (sans clé) + FMP gratuit. Lecture/recherche seule — aucun broker, zéro trading.

Onboarding premier boot (Web UI) imposé : credentials scellés dans /data par design.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:39:35 +02:00
ebb2eec184
feat(stt-client): page détail des services en pleine page + métriques live (#60)
La page détail d'un service (panneau « Services » du HUD) passe en PLEINE
PAGE : le drawer prend tout l'écran, le titre devient le nom du service, et
le contenu s'organise en 2 colonnes sur grand écran (données à gauche, état
+ actions à droite).

Nouvelle section « Métriques » par service, config-driven (comme `components`) :
- `[[services.metrics]]` → {label, prom, unit, hide_if_empty} évalué par le
  StatusPoller (dédup + parallèle avec les composants).
- Défauts via `config._pod_metrics(ns, pod_re)` pour les 7 services : redémarrages
  + uptime (kube-state-metrics, toujours dispo) ; mémoire + CPU (cAdvisor,
  `hide_if_empty` → carte masquée si non scrappé).
- Formatage HUD par unité : duration (8 j 9 h), bytes (497 Mo), cpu (30 mcœur),
  count.

Client 0.19.0. PromQL vérifié live (les 4 métriques renvoient des valeurs réelles
sur les 7 services). Rendu pleine page + formats validés via Playwright.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 00:18:39 +02:00
github-actions[bot]
4bf4655a83 ci(stt): image → sha-f47bc36 [skip ci] 2026-06-23 21:48:55 +00:00
f47bc36f29
feat(stt-client): pages détail enrichies (aperçu portefeuille + alertes) (#59)
Les pages détail du portail HUD affichent désormais un aperçu live des données
du service, pas seulement santé + lien :

- Ghostfolio → portefeuille complet : valeur, performance globale, positions
  (allocation % + P/L par ligne, coloré). Nouvel endpoint serveur
  POST /v1/portfolio/details (jeton client, source unique réutilisée).
- Alertmanager → TOUTES les alertes actives du homelab (total + critiques +
  liste avec sévérité), rendu côté HUD depuis `all_alerts` poussé par le
  StatusPoller (auto-rafraîchi, aucun appel supplémentaire).

Mécanisme extensible (`DETAIL_PROVIDERS` : `server` = requête backend |
`alerts` = liste locale). Cartes de stats + lignes de données génériques.

Client 0.18.0. Vérifié via Playwright (rendu, tons, normalisation des %).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:48:20 +02:00
7bfedb0901
feat(stt-client): volume de la voix réglable + fenêtrage persistant (#58)
Deux demandes côté client :

- VOLUME : slider « Volume de la voix » (0–200 %) dans le drawer Réglages du HUD,
  appliqué À CHAUD (canal settings → engine.cfg.tts_volume) et persisté
  (localStorage). Gain appliqué au WAV avant lecture, commun Piper/Kokoro
  (engine._apply_gain, PCM16 ×gain, clip). Config [voice].tts_volume (0.0 muet …
  2.0 amplifié, défaut 1.0). 0 = muet (le tour se déroule sans son).
- FENÊTRAGE : le mode existait ([ui].window_mode app|kiosk|none) ; `stt --window`
  PERSISTE désormais le choix → `stt --window app` rend le HUD fenêtré (déplaçable/
  redimensionnable, type Discord) durablement, y compris pour le service systemd.

Client 0.17.0. Doc : stt.example.toml + admin/ia/stt.md. Validé : gain ×0.5 exact,
HUD sans erreur JS (slider + collectSettings + label live).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:27:45 +02:00
0c263faac0
docs: réconcilie la doc cluster avec l'état réel (SearXNG + Asa agentique) (#57)
Relevé live 2026-06-23 → mise à jour des écarts depuis le 21/06 :

- admin/ops/etat-cluster.md : SearXNG ajouté (ns ai deploy/svc/IngressRoute,
  Application ArgoCD), date + note « STT devenu agentique ».
- CLAUDE.md : date d'état → 23/06 ; bullet STT réécrit (Asa agentique : contexte
  asa par défaut, function calling, état live enrichi GPU, web_search, admin_action,
  TTS Piper|Kokoro) ; nouveau bullet SearXNG ; apps-of-apps + apps + namespaces
  incluent searxng.
- admin/ia/stt.md : corrige les mentions « agir via Hermes = à venir » (désormais
  FAIT via admin_action/hermes-exec) ; bandeau daté complété (21→23/06).

(Re-ingestion RAG à faire après merge.)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:17:11 +02:00
0b117916be
feat(stt-client): stt --voices / --install-voice (changer de voix Piper) (#56)
Faciliter le changement de voix (le simple --update ne bascule rien : la config
perso prime sur le défaut).

- `stt --voices` : liste les voix Piper FR (homme/femme), URLs vérifiées sur
  HuggingFace rhasspy/piper-voices.
- `stt --install-voice fr_FR-tom-medium` : télécharge .onnx + .onnx.json et
  SÉLECTIONNE la voix (écrit tts_engine=piper + piper_voice dans stt.toml, en
  préservant les autres réglages). Puis `stt --restart`.
- doc : admin/ia/stt.md (note « pourquoi --update ne change pas la voix »).
  Client 0.16.0.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:04:09 +02:00
610d32f1e9
feat(stt-client): voix Kokoro (TTS enfichable) + commande --install-kokoro (#55)
Ajoute un moteur TTS plus naturel (Kokoro, ONNX/CPU) au choix, Piper restant
le défaut et le repli.

- engine.py : synthèse enfichable (_synthesize → _synth_piper | _synth_kokoro).
  Kokoro chargé une fois (préchargé au boot si sélectionné), WAV via wave stdlib
  (numpy → PCM16), lecture/interruption inchangées. Repli SILENCIEUX si le modèle
  ou le paquet kokoro-onnx manque (jamais de crash).
- config : [voice].tts_engine ("piper"|"kokoro") + kokoro_voice (ff_siwis, FR),
  kokoro_speed, kokoro_lang.
- cli : `stt --install-kokoro` télécharge kokoro-v1.0.onnx + voices-v1.0.bin dans
  ~/.local/share/kokoro et imprime les étapes (pipx inject kokoro-onnx, espeak-ng).
- pyproject : extra optionnel [kokoro] ; client 0.15.0.
- doc : stt.example.toml + admin/ia/stt.md + journal.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:57:27 +02:00
666abe765b
docs(stt): catalogue des demandes possibles à Asa (#54)
Ajoute docs/asa-commandes.md : liste complète, classée par capacité, des
demandes que l'on peut faire à Asa (avec exemples de phrases FR), bâtie sur
l'implémentation réelle (intents client + outils serveur + services du portail) :
réveil/veille, santé homelab (host_health/cluster_status), métriques (PromQL),
doc Funk (RAG), recherche web, actions admin (confirmation), ouvrir un service,
portefeuille, mémoire, contextes. Liée depuis README.md et docs/stt-presentation.md.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:34:38 +02:00
5de575bc9b
docs(stt): présentation d'Asa (schémas de la chaîne + captures HUD) (#53)
- docs/stt-presentation.md : présentation de l'assistant vocal Asa (STT
  client-serveur) — diagramme d'architecture (poste ↔ cluster ↔ LLM/outils/
  données) et diagramme de séquence d'une question vocale via la boucle
  d'outils, le tout en mermaid (rendu validé). + 2 captures du HUD réel
  (conversation agentique, veille/réveil) dans docs/img/.
- README.md : Asa promue en section dédiée (capacités sait/cherche/agit +
  capture), mention en vue d'ensemble, arbo stt/ (client/server) + docs/.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:17:39 +02:00
github-actions[bot]
b2a611eba4 ci(stt): image → sha-0d09dec [skip ci] 2026-06-22 21:16:27 +00:00
0d09dec3dc
feat(stt): enrichit les sondes (host_health, cluster_status, metrics) (#52)
- host_health : ajoute CPU%, disque /%, température CPU, uptime ; pour gpu-01
  un bloc GPU complet (température, utilisation, VRAM%, puissance via
  rocm_scraper) en plus de llama-server. Requêtes concurrentes (asyncio.gather).
- cluster_status : ajoute le nb de nœuds k8s Ready et une ligne CrashLoopBackOff
  (pods en boucle de crash) — complète le filtrage des pods terminés.
- metrics_block (contexte grafana) : ajoute le résumé GPU (util/temp/VRAM).
- Toutes les PromQL validées contre le Prometheus in-cluster.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:15:49 +02:00
github-actions[bot]
421ec77ef9 ci(stt): image → sha-3ea4c3c [skip ci] 2026-06-22 20:52:44 +00:00
3ea4c3c706
feat(stt): Phase 3 — actions admin pilotées par le LLM (admin_action) (#51)
Asa peut désormais AGIR sur le homelab quand on le demande explicitement, via
un outil de la boucle agentique — mais jamais sans confirmation.

- Outil admin_action(description) (contexte asa) : le LLM PROPOSE une action,
  n'exécute rien. brain.ask_with_tools gagne `confirm_tools` : un tel outil
  arrête la boucle et surface sa réponse (la question de confirmation).
- _handle_agentic : stocke la proposition en pending par session ; au tour
  suivant « confirme » → agent.run_action → hermes-exec (hermes -z --yolo),
  « annule » → oubli. Réutilise le handshake + jeton du contexte agent.
- admin_action n'est exposé que si _actions_available() (STT_ACTIONS_ENABLED
  + jeton) ; sinon retiré des schémas envoyés au modèle.
- Factorisation du ctx_debug du visualiseur. 1 test unitaire (confirm_tools
  arrête la boucle). Serveur 0.9.0 ; doc stt.md + journal.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:51:11 +02:00
dc7a2093ae
fix(searxng): crash port — enableServiceLinks:false + port/bind explicites (#50)
Le pod SearXNG crashait en boucle : granian recevait --port tcp://<ip>:8080
au lieu d'un entier. Cause classique k8s : le Service « searxng » fait injecter
l'env service-link SEARXNG_PORT=tcp://<clusterIP>:8080, que l'entrypoint SearXNG
lit comme port de bind.

- enableServiceLinks:false (coupe l'injection des env service-link, inutiles ici)
- SEARXNG_PORT=8080 + SEARXNG_BIND_ADDRESS=0.0.0.0 explicites (défense en profondeur)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:50:46 +02:00
github-actions[bot]
55344b55ed ci(stt): image → sha-73b52cd [skip ci] 2026-06-22 20:11:43 +00:00
73b52cde2c
feat(stt): recherche web (SearXNG in-cluster) + fix cluster_status (#49)
Phase 2 d'Asa agentique : nouvel outil web_search adossé à un SearXNG
self-host in-cluster, + correction du faux positif de cluster_status.

- SearXNG (k8s/apps/searxng/) : Deployment + Service + ConfigMap settings.yml
  + IngressRoute searxng.lab.local, Application ArgoCD. Namespace ai, interne
  (l'outil tape http://searxng:8080). use_default_settings + search.formats
  inclut json (sinon API JSON 403) ; limiter/image_proxy off ; image pinnée ;
  conf copiée dans un emptyDir via initContainer (contourne le mount RO) ;
  PodSecurity restricted.
- Outil web_search (tools._web_search) ajouté au contexte asa + STT_SEARXNG_URL.
- fix(cluster_status) : les pods de CronJob TERMINÉS (Succeeded/Failed, ex.
  sacrifice-assign-renfort) comptaient comme « non prêts » → fausse alarme.
  Join kube_pod_status_phase{phase=~"Running |Pending"} (3 faux positifs → 0,
  validé in-cluster).
- Serveur 0.8.0 ; doc stt.md + journal mis à jour.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:10:59 +02:00
github-actions[bot]
c9e89c91b9 ci(stt): image → sha-7cafc06 [skip ci] 2026-06-22 19:53:22 +00:00
7cafc06069
feat(stt): Asa agentique — boucle d'outils (function calling, Phase 1) (#48)
Asa ne répondait qu'à partir d'un contexte figé (RAG doc ou blocs live
pré-câblés, à présélectionner) → les questions à état live (« gpu-01 tourne
bien ? ») tombaient sur « la doc ne le précise pas ». Bascule vers une boucle
de function-calling : le modèle décide quels outils appeler puis répond à
partir de leurs résultats.

- Nouveau contexte `asa` (STT_DEFAULT_CONTEXT=asa, défaut prod) + tools.py
  (registre schémas OpenAI + exécuteurs) + brain.ask_with_tools (boucle bornée
  STT_TOOL_MAX_ITERS=4, réponse forcée au-delà ; _post factorisé + retry).
- Outils Phase 1, LECTURE SEULE : search_docs (RAG funk-docs), host_health
  (gpu-01|storage-01 : up/charge/RAM + llama-server), cluster_status,
  prometheus_query (PromQL arbitraire). Réutilisent sources.py / knowledge.py.
- Trace des outils renvoyée dans `context` → visualiseur HUD (un bloc par appel).
- 100 % local (Qwen3-8B) : tool-calling natif llama.cpp validé, survit au
  /no_think. LiteLLM transmet bien les tools. PromQL validés contre le vrai
  Prometheus in-cluster. 3 tests unitaires de la boucle (hors-ligne).

Web search (SearXNG in-cluster) = Phase 2 ; actions admin pilotées par le LLM
(hermes-exec comme outil) = Phase 3.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:52:28 +02:00
de1cd6c407
fix(gpu): débloque make apply-gpu — repo amdgpu rhel9.7 + pin commit llama.cpp (#47)
Le `dnf upgrade` du rôle common avait bumpé gpu-01 en AlmaLinux 9.8, mais
repo.radeon.com ne publie pas latest/rhel/9.8 (ni 9.5) — seulement 9.4/9.6/9.7.
`make apply-gpu` échouait alors dès `common` sur « Cannot download repomd.xml »
pour le repo amdgpu.

- rocm_rhel_version 9.4 → 9.7 (dernière mineure publiée par radeon) + piège
  documenté dans le README rocm (avec workaround live /etc/dnf/vars/amdgpudistro).
- Pin llama_server_commit au commit construit sur g01 : sans pin le rôle clonait
  HEAD → rebuild (~15 min) + restart GPU non déterministe à chaque apply.
- Réconciliation du drop-in llama-embed (ubatch 2048) dans l'unit rendu par le
  rôle (apply ciblé) ; drop-in temporaire supprimé. RAG validé (rag-query OK).
- Doc : etat-cluster + journal 2026-06-22 mis à jour.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:19:02 +02:00
1e01caf634
fix(stt): détecte « Asa » même découpé par l'ASR (« a so »/« à ça ») + log diagnostic (#46)
Test sur poste après #45 : Parakeet FR ne transcrit pas « Asa » comme un mot mais comme
« a so » / « à ça » / « ah ça » (deux tokens) → la détection mot-entier ne matchait jamais
(« quand je dis Asa, rien ne se passe »).

- _detect_wake : glisse une fenêtre de 1-2 mots, joint (sans espace) + déaccentue, compare
  en égalité exacte à des alias COMPACTS (asa, aso, aca, ahca…). L'exact évite les faux
  positifs (« à cause » → « acause » ≠ alias). Extrait la commande après le wake.
- wake_aliases (engine + config) : formes compactes reflétant les rendus ASR réels.
- Log diagnostic [stt.voice] sur stderr/journalctl (transcription + décision), coupable via
  STT_VOICE_LOG=0 — précieux pour ajuster les alias selon l'ASR.
- .gitignore : build/ + *.egg-info (artefacts d'install locale du client).

Validé en live (poste) : « Asa » réveille, « Asa stop » endort, partiels en direct.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:54:46 +02:00
d83091a350
feat(stt): transcription partielle live + veille/réveil vocal (« Asa » / « Asa stop ») (#45)
* 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>

* feat(stt): transcription partielle live + veille/réveil vocal (« Asa » / « Asa stop »)

Deux fonctionnalités sur l'assistant vocal Asa (client 0.13.0 → 0.14.0).

Transcription partielle live :
- en éveil, le buffer en cours est re-transcrit périodiquement (partial_interval_sec) et
  poussé au HUD (event `partial`) → le texte se forme en direct dans une bulle dédiée,
  remplacée par la bulle « user » finale.
- auto-activée seulement avec l'ASR onnx (Parakeet, rapide CPU) ; bornée min/max ;
  forçable via [voice].partial_transcription.

Veille / réveil vocal (machine à états asleep ↔ éveil) :
- veille : Asa transcrit chaque énoncé UNIQUEMENT pour repérer le wake word, ne répond à
  rien d'autre (HUD : portrait atténué, « SOMMEIL »).
- « Asa » (mot entier, alias ASR tolérés) réveille ; « Asa, <question> » exécute la question.
- « Asa stop » (ou stop/dors/au revoir… = sleep_words) remet en veille ; timeout aussi.
- wake word par défaut `asa` (était `hermes`) ; détection durcie en mot entier (plus de
  sous-chaîne — « asa » matchait « casa », « phrase »…).

Validé : tests unitaires (wake/sleep/alias) + Playwright (rendu asleep, bulle partielle mise
à jour en place puis remplacée, nettoyage au changement d'état). Doc admin/ia/stt.md + journal.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:27:53 +02:00
af98b47018
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>
2026-06-21 23:29:05 +02:00
f0e33c1121
feat(stt): active les actions via Hermes (STT_ACTIONS_ENABLED=true) (#43)
* feat(stt): active les actions via Hermes (STT_ACTIONS_ENABLED=true)

hermes-exec déployé sur storage-01 (:9096) + secret k8s stt-server-secrets/hermes-exec-token
en place → on active le contexte « agent ». Après déploiement ArgoCD, le contexte
🤖 Agent apparaît dans le sélecteur et les actions (avec confirmation) sont opérationnelles.

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

* docs: journal 2026-06-21 + état STT (portail, contextes, intents, actions Hermes)

- progress/2026-06-21.md : récap complet de la journée (portail + santé live, contextes
  visualiseur + presets live, intents vocaux, jeton Ghostfolio unifié, actions via Hermes,
  ASR Parakeet) + l'incident de merge des PR empilées et sa leçon.
- PROGRESS.md : ligne du jour.
- CLAUDE.md : « État actuel » daté 2026-06-21, ligne STT enrichie.

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

* chore(vault): ajoute vault_hermes_exec_token (jeton actions hermes-exec, chiffré)

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:09:35 +02:00
github-actions[bot]
0802d81160 ci(stt): image → sha-9a46f6c [skip ci] 2026-06-21 02:01:51 +00:00
9a46f6cbcb
feat(stt): actions cluster via Hermes — contexte « agent » + exécuteur hermes-exec (#42)
Asa peut AGIR sur le homelab (l'autre moitié de la vision), via le vrai agent Hermes,
profil par défaut, tous ses outils — avec confirmation et jeton.

Découverte : Hermes n'a pas d'API HTTP (appli TUI), mais un mode one-shot `hermes -z
"<prompt>" --yolo`. On s'appuie dessus.

storage-01 — exécuteur :
- tools/hermes-exec/server.py : service HTTP qui lance `hermes -z --yolo` en user hermes,
  derrière un jeton Bearer (compare_digest), timeout + audit, une action à la fois.
- rôle Ansible hermes_exec : systemd (User=hermes, env hermes-agent), jeton via
  EnvironmentFile 0640 (Vault vault_hermes_exec_token) ; ajouté au playbook storage-01.

STT-server (0.5.0 → 0.6.0) :
- agent.py : pont vers hermes-exec (jeton) + détection confirme/annule.
- contexts.py : contexte « agent » (court-circuite le LLM).
- app.py : flux dédié — handshake 2 temps par session (pending action) → sur « confirme »,
  appelle hermes-exec ; « annule » annule. /v1/contexts masque « agent » si désactivé.
- config : STT_ACTIONS_ENABLED (opt-in, défaut false) + URL + jeton (secret k8s).
- deployment : env actions + secret stt-server-secrets/hermes-exec-token (optionnel).

Sécurité : opt-in désactivé par défaut ; jeton obligatoire (sinon contexte caché + exécuteur
refuse tout) ; --yolo atteint seulement jeton+confirmation en main ; audit storage-01.
Client : AUCUN changement (le contexte « agent » apparaît tout seul dans le sélecteur).

Validé en local : exécuteur (401 sans/mauvais jeton, exec avec bon jeton via echo),
handshake serveur (TestClient : confirme→exécute, annule→annule, agent masqué si OFF).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:00:14 +02:00
github-actions[bot]
5af51f8f66 ci(stt): image → sha-e49b4fc [skip ci] 2026-06-21 01:29:29 +00:00
e49b4fc3ba
refactor(stt): unifie le jeton Ghostfolio — source client unique, appel côté serveur (#41)
Avant : jeton Ghostfolio en double (config client pour l'intent vocal + secret k8s pour
le contexte serveur). Désormais une SEULE source — la config client — transmise au
serveur, qui fait tous les appels Ghostfolio (un seul code, lecture seule).

Serveur :
- sources.py : cœur _ghostfolio_fetch(token) partagé ; ghostfolio_block (contexte) et
  ghostfolio_phrase (intent) acceptent un jeton par requête (priorité client > env).
- app.py : /v1/ask accepte `secrets` (jetons client) passés aux sources ; nouvel
  endpoint POST /v1/portfolio {token} → valeur du portefeuille.
- bump 0.4.0 → 0.5.0.

Client :
- api.py : ServerClient.secrets transmis dans /v1/ask ; méthode portfolio() → /v1/portfolio.
- cli.py : _make_client renseigne secrets["ghostfolio_token"] depuis [ghostfolio].
- ui/app.py : l'intent « combien ghostfolio » appelle client.portfolio (serveur).
- supprime portal/ghostfolio.py (client) — l'appel API vit côté serveur.
- config : [ghostfolio] = juste access_token (transmis au serveur).
- bump 0.12.0 → 0.13.0.

Le secret k8s STT_GHOSTFOLIO_TOKEN devient un simple fallback. Validé bout-en-bout
contre une instance serveur locale + Ghostfolio réel : « …vaut environ 16 520 EUR. »

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:28:52 +02:00
ae4d5bd20e
fix(stt-client): restaure les intents vocaux (#37) + l'affichage version perdus de main (#40)
Deux features mergées n'avaient pas atteint main (effet de bord des PR empilées :
#37 a été mergé dans sa branche de base supprimée, jamais propagé). Recollées proprement
sur main à jour, en fusionnant engine._respond / app.py avec le travail contextes (#39).

Intents vocaux (lecture seule, court-circuitent le LLM) :
- portal/intents.py + ghostfolio.py (récupérés) + exports __init__.
- engine.py : intent_router AVANT le LLM ; cohabite avec context_provider
  (pas de contexte émis sur un intent local).
- app.py : construit le routeur (services, ouverture, santé, Ghostfolio).
- config : section [ghostfolio] + doc example.toml.

Affichage version :
- app.py : meta_msg poussé à la connexion.
- hud : case 'meta' + setVersion + ligne « Version installée » (section Service).

- bump 0.11.0 → 0.12.0.

Validé : compile, routage intents, coexistence intent/visualiseur dans _respond
(intent → mode portail sans contexte ; LLM → contexte émis), HUD (meta/version).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:05:03 +02:00
de4b66ed56
feat(stt-client): sélecteur de contexte + visualiseur dans le HUD (#39)
Moitié client de la fonctionnalité contextes (le serveur #38 est déployé). Tu choisis
un contexte pour Asa et tu vois exactement ce qu'on lui envoie.

- Sélecteur (Réglages → « Contexte d'Asa ») : liste peuplée depuis GET /v1/contexts ;
  le choix est envoyé en settings{context}, persisté en localStorage, et réappliqué au
  backend à la connexion. Masqué si le serveur ne supporte pas (dégradation).
- Visualiseur (icône ▤ de la barre) : drawer affichant le contexte assemblé renvoyé
  après chaque tour — consigne (system prompt), données live injectées, doc RAG, mémoire.
- api.py : ServerClient envoie `context`, expose last_context + contexts().
- engine.py : context_provider → émet {type:context} après chaque réponse.
- app.py : récupère /v1/contexts au démarrage, diffuse à la connexion, applique le
  changement de contexte (client.context).
- bump 0.10.0 → 0.11.0.

Validé via Playwright : sélecteur (5 contextes), changement → settings{context},
visualiseur (consigne + bloc live Ghostfolio + mémoire), drawer partagé. Rendu conforme.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 02:57:03 +02:00
github-actions[bot]
89aeb418ff ci(stt): image → sha-1faf50a [skip ci] 2026-06-20 21:49:59 +00:00
1faf50a9be
feat(stt-server): contextes présélectionnables + sources live + contexte assemblé (visualiseur) (#38)
Asa n'est plus bloqué sur le seul contexte « doc cluster grounding-strict ». Le client
choisit un contexte par requête ; le serveur change le system prompt ET injecte les
données live du domaine, puis renvoie le contexte assemblé pour le visualiseur du HUD.

- contexts.py : profils funk / ghostfolio / grafana / alerting / cluster (system prompt
  + sources) + assemble() (prompt final + structure de visualisation).
- sources.py : fetchers live best-effort (Ghostfolio auth+details, Alertmanager alerts
  hors Watchdog, Prometheus cluster/metrics), env-config, dégradation propre.
- brain.py : ask() reçoit le system prompt déjà assemblé (assemblage remonté).
- app.py : /v1/ask accepte `context`, renvoie context_id + le contexte assemblé ;
  nouveau GET /v1/contexts ; RAG doc conditionné au profil.
- config.py : URLs sources + STT_GHOSTFOLIO_TOKEN + STT_DEFAULT_CONTEXT.
- deployment : env in-cluster (Prometheus/Alertmanager monitoring, Ghostfolio ai),
  jeton via secret optionnel stt-server-secrets/ghostfolio-token.
- bump 0.3.1 → 0.4.0.

Validé en local : assemblage (blocs+RAG+mémoire), parsing des sources (mock),
endpoints /v1/contexts et /v1/ask (LLM mocké) — context_id, visualiseur, fallback
contexte inconnu → funk.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:49:29 +02:00
d4b053622c
feat(stt-client): pages détail par service + santé live (probe + Prometheus + Alertmanager) (#36)
Transforme le portail (tuiles ouvrant un lien) en mini-pages par service, avec état
de santé temps réel — comme demandé.

Page détail (clic sur une tuile) : icône+nom, pilule d'état (en ligne/dégradé/hors
ligne + latence), description, composants (pastilles), alertes actives, infos (URL),
bouton « Ouvrir ». Tuiles : pastille de santé live + badge global d'alertes.

- registry.py : Service enrichi (description, health_url, components[{name,prom}],
  alerts_match) — toujours config-driven.
- health.py : StatusPoller en thread. Probe HTTP (up/down+latence) + Prometheus
  /api/v1/query (composants via up{}/kube-state-metrics) + Alertmanager /api/v2/alerts
  (hors Watchdog). Parallélisé (ThreadPoolExecutor), pousse portal-status au HUD.
- config.py : métadonnées homelab vérifiées en live (7 services ; traefik retiré —
  URL injoignable) + section [portal] (intervalle, URLs, timeouts).
- app.py : démarre le poller, diffuse portal-status (+ dernier état au connect).
- HUD : vues liste⟷détail, pastilles, badge alertes, mise à jour live.
- bump 0.9.0 → 0.10.0.

Validé : StatusPoller en direct contre le homelab (cycle 0.12s, 7 services ok,
composants Prometheus résolus, 0 alerte) ; HUD via Playwright (pastilles ok/dégradé/
hors-ligne, page détail complète, alerte rendue, bouton Ouvrir → open-service).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:17:27 +02:00
3b6099ffa0
feat(stt-client): contrôles du service dans le HUD + commandes stt --start/--restart (#35)
Ajoute la gestion du service STT directement depuis l'interface (Réglages →
section « Service ») et deux commandes CLI.

- CLI : `stt --start` (démarre le service) et `stt --restart` (recharge la config :
  modèle ASR, wake word…). Garde-fou si le unit n'est pas installé.
- HUD : boutons Redémarrer / Mettre à jour / Arrêter. Le HUD tournant DANS le
  service, les actions sont lancées en process détaché (start_new_session) → le
  redémarrage va à son terme même quand le process courant est tué (systemd
  possède le job). « Mettre à jour » = `stt --update` puis `stt --restart`.
- bump 0.8.0 → 0.9.0.

Validé : py_compile, --start/--restart dans l'aide, et HUD via Playwright
(3 boutons émettent service-restart/-update/-stop, toast, aucune erreur JS,
rendu conforme).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:37:04 +02:00
9c335d169b
feat(stt-client): portail de services dans le HUD — tuiles cliquables + registre config-driven (#34)
Ajoute un panneau « Services » au HUD du client STT : une tuile par service du
homelab (Ghostfolio, Grafana, ArgoCD, n8n, Open WebUI, Prometheus, Alertmanager,
Traefik), un clic ouvre l'URL dans le navigateur habituel.

- stt/portal/ : registre piloté par [[services]] de stt.toml (id, name, url, icon,
  aliases) + résolution floue (registry.match_service) prête pour la voix.
  Ajouter un service = quelques lignes de config, zéro code.
- config.py : défauts homelab + doc dans stt.example.toml.
- ui/app.py : pousse la liste au HUD à la connexion, action de contrôle
  open-service → _open_url_external (xdg-open, session navigateur normale) + toast.
- hud/index.html : bouton header, drawer Services (voile partagé avec Réglages),
  grille de tuiles, toast de confirmation.
- bump 0.7.0 → 0.8.0.

Validé : registre (fuzzy-match FR/fautes/négatif), wiring backend, et HUD via
Playwright (rendu tuiles + clic émet open-service + toast, aucune erreur JS).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:20:11 +02:00
a7b5c44e82
docs(rag): chunk dense « nœuds + OS » pour fermer le trou de grounding (cas Q3) (#33)
Hermès répondait « la doc ne le précise pas » à « combien de nœuds K8s + OS »
alors que talos.md § Architecture contenait l'info — mais en TABLE : nomic embed
mal une table face à une question en langage naturel (pas de « 3 »/« trois », OS
implicite), donc le chunk ne remontait pas dans le top-k.

Fix = phrase en prose dense en tête de la section (compte explicite + OS + rôles +
les 2 hôtes AlmaLinux hors cluster). Le chunk passe #1 (score 0.743 vs ~0.69 avant).

Validé après re-ingest :
- « combien de nœuds K8s + OS » → « 3 nœuds, tous sous Talos Linux v1.13.0 » (docs=6)
- « combien de machines + OS » → réponse nuancée (3 Talos + 2 AlmaLinux hors cluster)

Leçon RAG : préfixer toute table dense d'une phrase en prose pour l'ancrage des
questions naturelles « combien / quel ».

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:41:13 +02:00