#!/usr/bin/env node /** * router-exploration — ручка «% разведки» (#3 крит-разбор). По умолчанию ВЫКЛючена * (rate=0) — анти-самоослепление петли обучения предусмотрено СЛОТОМ, но включается * позже. Проба нового узла = ВОПРОС владельцу с обоснованием+риском (никогда авто; * наполняется только по «да», как 3.4/3.5). Высокорисковые узлы к пробе не предлагаются. */ export const DEFAULT_EXPLORATION = Object.freeze({ rate: 0 }); /** Счётчик использования узлов (для выбора наименее пробованного). */ export function tallyUsage(counts, nodeId) { const c = { ...(counts || {}) }; c[nodeId] = (c[nodeId] || 0) + 1; return c; } /** * Предложить пробу наименее использованного узла. rate<=0 → null (выкл). Иначе среди * кандидатов, НЕ высокорисковых (riskOf инъектируется), берём с минимальным счётчиком → * вопрос владельцу (requiresOwnerApproval=true) с обоснованием+риском. Не применяет сам. */ export function proposeProbe({ rate = 0, usageCounts = {}, candidates = [], riskOf = () => false } = {}) { if (!(rate > 0)) return null; const eligible = candidates.filter((c) => !riskOf(c.id)); if (eligible.length === 0) return null; const least = eligible.reduce((best, c) => (usageCounts[c.id] || 0) < (usageCounts[best.id] || 0) ? c : best, eligible[0]); return { probe: least.id, requiresOwnerApproval: true, justification: `узел ${least.id} почти не пробован (использований: ${usageCounts[least.id] || 0}) — разведка может найти лучший инструмент`, risk: 'непроверенный узел может сработать хуже привычного; включается только по твоему «да»', }; }