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>
This commit is contained in:
ALI YESILKAYA 2026-06-22 21:52:28 +02:00 committed by GitHub
parent de1cd6c407
commit 7cafc06069
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 566 additions and 42 deletions

View file

@ -91,12 +91,26 @@ switch global type `hermes-switch` (pas de restart, chaque client choisit le sie
par requête à `/v1/ask` (`GET /v1/contexts` liste les profils). Chaque profil (`contexts.py`)
a son **system prompt** et ses **sources live** injectées dans le contexte (`sources.py`,
best-effort, env-config) :
- `funk` — grounding strict + RAG `funk-docs` (défaut, comportement historique)
- `asa`**boucle d'outils (function calling)**, **défaut prod** (`STT_DEFAULT_CONTEXT=asa`). Voir ci-dessous.
- `funk` — grounding strict + RAG `funk-docs` injecté d'office (comportement historique, sélectionnable)
- `ghostfolio` — valeur/positions du portefeuille (API Ghostfolio, `STT_GHOSTFOLIO_TOKEN`)
- `grafana` — métriques clés (Prometheus)
- `alerting` — alertes actives (Alertmanager, hors Watchdog)
- `cluster` — état pods/nœuds (Prometheus/kube-state-metrics) + RAG doc
**Contexte « asa » — boucle d'outils (function calling, local)** : au lieu d'un appel LLM unique
sur un contexte figé, Asa **décide elle-même** quels outils appeler (`tools.py`) puis répond à
partir de leurs résultats (`brain.ask_with_tools`, boucle bornée par `STT_TOOL_MAX_ITERS=4` ;
au-delà, réponse forcée sans outils). C'est ce qui débloque les questions à **état live** sans
présélection — ex. « gpu-01 tourne bien ? » → l'outil `host_health` est appelé → réponse réelle
(avant : le RAG seul disait « la doc ne le précise pas »). Outils Phase 1, **LECTURE SEULE** :
`search_docs` (RAG `funk-docs`), `host_health(gpu-01|storage-01)` (up/charge/RAM + llama-server,
Prometheus), `cluster_status` (nœuds/pods), `prometheus_query(expr)` (PromQL arbitraire). Modèle
**local** Qwen3-8B (le tool-calling natif de llama.cpp fonctionne, **même avec `/no_think`**
vérifié). Le `trace` des outils appelés est renvoyé dans `context` → visualiseur HUD (un bloc par
appel). L'écriture (agir) reste au contexte `agent` ci-dessous (Phase 3 : la brancher comme outil).
> Web search (SearXNG in-cluster) = Phase 2 ; actions admin pilotées par le LLM = Phase 3.
La réponse `/v1/ask` renvoie le **contexte assemblé** (`context`: system prompt + blocs live +
extraits RAG + mémoire) → alimente le **visualiseur** du HUD (« voir ce qu'on envoie à Asa »).
URLs in-cluster dans `k8s/apps/stt/deployment.yaml` (Prometheus/Alertmanager `monitoring`,