397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
35 lines
2.2 KiB
JavaScript
35 lines
2.2 KiB
JavaScript
#!/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: 'непроверенный узел может сработать хуже привычного; включается только по твоему «да»',
|
||
};
|
||
}
|