Agent IA planificateur EVCS en MODE OMBRE (Advisor Shadow V1)
- Workflow n8n tdfInZns7cqo8fU2 actif (15 min): snapshot HA compact -> prompt FR -> LLM local Spark (modèle dynamique via /v1/models) avec fallback OpenRouter claude-haiku-4.5 (credential chiffré, clé hors git) -> validation stricte (enums, bornes, rejet total si invalide) -> helpers advisor_* + divergence + compteurs accord/divergence + logbook. - GARANTIE OMBRE structurelle: écritures limitées à advisor_*/logbook, vérifiée par tests/advisor/run.mjs (16 tests, bloquants au push). - Kill-switch input_boolean.evcs_advisor_enabled. - Extension R2 multi-workflows (lib-node-map WORKFLOWS[], build/extract/push par fichier, n8n-create.mjs, gen-advisor-workflow.mjs). - Helpers HA via package NFS + carte dashboard EVCS Master (backup fait). - 1re reco réelle: auto_solar_plus, cible 35%, accord avec le réel. - Fix sécurité n8n-push: plus de fallback d'id implicite vers le workflow EVCS.
This commit is contained in:
70
src/advisor/BUILD_HA_CALLS.js
Normal file
70
src/advisor/BUILD_HA_CALLS.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// =====================================================
|
||||
// EVCS ADVISOR SHADOW V1 — BUILD_HA_CALLS
|
||||
// Écrit les helpers advisor_* + divergence + compteurs + logbook.
|
||||
// GARANTIE MODE OMBRE : ce node ne produit QUE des écritures vers
|
||||
// des entités advisor_* et le logbook. AUCUN switch, AUCUNE stratégie
|
||||
// réelle, AUCUNE commande borne. Ne pas ajouter d'autres entités ici.
|
||||
// =====================================================
|
||||
|
||||
const { status, detail, advice, modelUsed, context } = $input.first().json;
|
||||
|
||||
function stampParis() {
|
||||
const p = new Intl.DateTimeFormat('fr-FR', {
|
||||
timeZone: 'Europe/Paris', day: '2-digit', month: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit', hour12: false,
|
||||
}).formatToParts(new Date());
|
||||
const g = t => p.find(x => x.type === t)?.value || '';
|
||||
return `${g('day')}/${g('month')} ${g('hour')}:${g('minute')}`;
|
||||
}
|
||||
|
||||
const calls = [];
|
||||
function push(domain, service, data) {
|
||||
calls.push({ json: { domain, service, data } });
|
||||
}
|
||||
function setText(entity, value) {
|
||||
push('input_text', 'set_value', { entity_id: entity, value: (value ?? '').toString().slice(0, 250) });
|
||||
}
|
||||
function setNumber(entity, value) {
|
||||
push('input_number', 'set_value', { entity_id: entity, value });
|
||||
}
|
||||
|
||||
// Statut + horodatage + modèle : écrits à CHAQUE run
|
||||
setText('input_text.evcs_advisor_status', status + (detail ? `: ${detail}` : ''));
|
||||
setText('input_text.evcs_advisor_last_run', stampParis());
|
||||
setText('input_text.evcs_advisor_model', modelUsed);
|
||||
|
||||
if (status === 'ok' && advice) {
|
||||
// Recommandations
|
||||
setText('input_text.evcs_advisor_strategy', advice.strategy);
|
||||
setNumber('input_number.evcs_advisor_night_target_soc', advice.nightTargetSoc);
|
||||
setNumber('input_number.evcs_advisor_current_max_a', advice.currentMaxA);
|
||||
setText('input_text.evcs_advisor_ev_charge_tonight', advice.evChargeTonight);
|
||||
setText('input_text.evcs_advisor_reason', `[${Math.round(advice.confidence * 100)}%] ${advice.reason}`);
|
||||
|
||||
// Divergence vs réel (stratégie + cible nuit)
|
||||
const diffs = [];
|
||||
if (advice.strategy !== context.userStrategy) {
|
||||
diffs.push(`stratégie: agent=${advice.strategy} ≠ réel=${context.userStrategy}`);
|
||||
}
|
||||
if (Math.abs(advice.nightTargetSoc - context.nightTargetActual) > 10) {
|
||||
diffs.push(`cible nuit: agent=${advice.nightTargetSoc}% vs réel=${context.nightTargetActual}%`);
|
||||
}
|
||||
const divergenceText = diffs.length ? diffs.join(' | ') : 'accord avec le réel';
|
||||
setText('input_text.evcs_advisor_divergence', divergenceText);
|
||||
|
||||
// Compteurs accord/divergence sur la stratégie (base des critères de bascule)
|
||||
if (advice.strategy === context.userStrategy) {
|
||||
setNumber('input_number.evcs_advisor_agree_count', context.agreeCount + 1);
|
||||
} else {
|
||||
setNumber('input_number.evcs_advisor_diverge_count', context.divergeCount + 1);
|
||||
}
|
||||
|
||||
// Trace logbook pour l'historique long terme
|
||||
push('logbook', 'log', {
|
||||
name: 'EVCS Advisor (ombre)',
|
||||
message: `${advice.strategy} | cible nuit ${advice.nightTargetSoc}% | ${advice.evChargeTonight === 'oui' ? 'charge VE cette nuit' : advice.evChargeTonight === 'non' ? 'pas de charge nuit' : 'VE sans objet'} | ${divergenceText} | ${advice.reason}`,
|
||||
entity_id: 'input_text.evcs_advisor_strategy',
|
||||
});
|
||||
}
|
||||
|
||||
return calls;
|
||||
Reference in New Issue
Block a user