Revue : pistes ecartees et source (local/OpenRouter) gardees, historisation pour rejeu, carte verbeuse du conseiller

This commit is contained in:
Tallagrand Thierry
2026-10-08 05:43:54 +02:00
parent 78f434778b
commit b25770736f
7 changed files with 206 additions and 41 deletions

View File

@@ -198,36 +198,84 @@ views:
- type: vertical-stack
cards:
- type: custom:mushroom-title-card
title: 🤖 Conseiller IA (observateur)
subtitle: Il regarde et signale — il ne décide rien
title: 🤖 Conseiller IA — relecteur de la journée
subtitle: Les constats sont mesurés par le code ; le conseiller propose des pistes, il ne décide rien
- type: markdown
title: Revue de ce matin
content: |
{% set r = states.sensor.evcs_revue_jour %}
{% set a = states.sensor.evcs_advisor_revue %}
{% set auj = now().strftime('%Y-%m-%d') %}
{% if r is none %}
**Revue indisponible** (capteur absent : publiée à 07:30 par le service d'historisation).
{% else %}
**Revue du {{ r.state }}**{% if r.state != auj %} — ⚠️ pas celle d'aujourd'hui (publiée à 07:30){% endif %}
· 24 h jusqu'à la fin des heures creuses de nuit · **{{ r.attributes.a_examiner | default(0) }} point(s) à examiner**
{% for c in r.attributes.constats | default([]) %}
---
{% if c.gravite == 'a_examiner' %}🔶{% else %}ℹ️{% endif %} **{{ c.code }}**
{{ c.texte }}
{% if c.contexte %}<br><small>Raison publiée par le contrôleur à ce moment-là : <i>{{ c.contexte }}</i></small>{% endif %}
{% set ps = (a.attributes.pistes | default([]) | selectattr('code','eq',c.code) | list) if a is not none and a.state == r.state else [] %}
{% for p in ps %}
<br>💡 **Piste du conseiller** (avis, à vérifier) : {{ p.piste }}
{% endfor %}
{% set es = (a.attributes.ecartees | default([]) | selectattr('code','eq',c.code) | list) if a is not none and a.state == r.state else [] %}
{% for e in es %}
<br><small>🚫 Piste écartée ({{ e.motif }}) : <i>{{ e.piste }}</i></small>
{% endfor %}
{% endfor %}
{% if (r.attributes.constats | default([])) | length == 0 %}✅ {{ r.attributes.texte }}{% endif %}
{% endif %}
- type: markdown
title: Qui a répondu, et comment
content: |
{% set a = states.sensor.evcs_advisor_revue %}
{% if a is none %}
Le conseiller n'a encore publié aucune revue.
{% else %}
| | |
|---|---|
| Revue relue | {{ a.state }} ({{ a.attributes.constats_relus | default('?') }} constat(s)) |
| Modèle | {{ a.attributes.modele | default('?') }} |
| **Où** | {% if a.attributes.source == 'openrouter' %}☁️ **OpenRouter (cloud)**{% else %}🏠 **Spark (local)**{% endif %} |
| Pistes retenues | {{ a.attributes.pistes | default([]) | length }} |
| Pistes écartées | {{ a.attributes.rejetees | default(0) }} (chiffre inventé, code inconnu, doublon) |
{{ a.attributes.source_texte | default('') }}
{% endif %}
**Rôle d'OpenRouter** : solution de **secours seulement**. Le conseiller interroge d'abord le modèle local de la Spark ; si la Spark ne répond pas, les constats (textes ci-dessus, sans identifiants) sont envoyés à OpenRouter. Il n'a aucun autre rôle.
Statut du dernier passage : `{{ states('input_text.evcs_advisor_status') }}` · {{ states('input_text.evcs_advisor_last_run') }} · {{ states('input_text.evcs_advisor_model') }}
- type: markdown
title: Les 7 derniers jours
content: |
{% set h = states.sensor.evcs_revue_historique %}
{% if h is none or (h.attributes.jours | default([])) | length == 0 %}
Pas encore d'historique (la première revue est publiée le 08/10 à 07:30).
{% else %}
{% for j in h.attributes.jours %}
**{{ j.jour }}** — {{ j.constats | selectattr('gravite','eq','a_examiner') | list | length }} point(s), {{ j.pistes | selectattr('retenue') | list | length }} piste(s) retenue(s), {{ j.pistes | rejectattr('retenue') | list | length }} écartée(s){% if j.pistes | selectattr('source','eq','openrouter') | list | length > 0 %} · ☁️ OpenRouter{% endif %}
{% for c in j.constats %}
<br><small>• {{ c.texte }}</small>
{% endfor %}
{% endfor %}
{% endif %}
<small>Tout est gardé en base (`revue_constat`, `revue_piste`, avec les pistes écartées et leur motif) pour rejouer le conseiller plus tard et mesurer s'il progresse.</small>
- type: markdown
title: Dialoguer avec le conseiller
content: |
Pour l'instant, le dialogue passe par **OpenClaw** (WhatsApp ou vocal) : « qu'est-ce qui a cloché hier ? » — il lit la revue et les pistes ci-dessus, et présente les pistes comme des avis.
- type: entities
title: Dernière observation
entities:
- entity: input_text.evcs_advisor_gravite
name: Gravité
icon: mdi:alert-circle-outline
- entity: input_text.evcs_advisor_constat
name: Ce qu'il voit
icon: mdi:eye-outline
- entity: input_text.evcs_advisor_ce_qui_cloche
name: Ce qui cloche
icon: mdi:help-rhombus-outline
- entity: input_text.evcs_advisor_reason
name: Piste (et sa confiance)
icon: mdi:magnify
- type: divider
- entity: input_text.evcs_advisor_status
name: Statut
- entity: input_text.evcs_advisor_model
name: Modèle LLM
- entity: input_text.evcs_advisor_last_run
name: Dernière analyse
- entity: input_number.evcs_advisor_agree_count
name: Cycles calmes
- entity: input_number.evcs_advisor_diverge_count
name: Signalements
- type: divider
- entity: input_boolean.evcs_advisor_enabled
name: Conseiller actif (kill-switch)