#!/usr/bin/env node /** * v5.6 T10: 路由权重看板报告 * 分析 debug/route-weights.json 的权重分布和异常 * * 使用: node scripts/route-weights-report.js [--json] */ const fs = require('fs'); const path = require('path'); let debugDir; try { debugDir = require('./paths.config.js').PATHS.debugDir; } catch { debugDir = path.resolve(__dirname, '..', 'debug'); } function loadWeights() { const f = path.join(debugDir, 'route-weights.json'); try { if (!fs.existsSync(f)) return null; return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return null; } } function analyze(weights) { if (!weights || !weights.deltas) return { status: 'empty', count: 0, deltas: {} }; const deltas = weights.deltas; const entries = Object.entries(deltas); const count = entries.length; if (count === 0) return { status: 'empty', count: 0, deltas: {} }; const values = entries.map(([, v]) => v); const maxAbs = Math.max(...values.map(Math.abs)); const positives = entries.filter(([, v]) => v > 0); const negatives = entries.filter(([, v]) => v < 0); const nearLimit = entries.filter(([, v]) => Math.abs(v) >= 0.4); const sorted = [...entries].sort((a, b) => b[1] - a[1]); const topBoosts = sorted.slice(0, 5); const topPenalties = sorted.slice(-5).reverse(); return { status: maxAbs >= 0.4 ? 'saturating' : maxAbs >= 0.2 ? 'active' : 'stable', count, positives: positives.length, negatives: negatives.length, maxAbsDelta: maxAbs, nearLimit: nearLimit.map(([k, v]) => ({ skill: k, delta: v })), topBoosts: topBoosts.map(([k, v]) => ({ skill: k, delta: v })), topPenalties: topPenalties.map(([k, v]) => ({ skill: k, delta: v })), lastUpdated: weights.lastUpdated || null, snapshotTs: weights.snapshotTs || null, }; } function printReport(report) { console.log('=== 路由权重看板 ==='); console.log('状态:', report.status); console.log('权重条目:', report.count, '(正:', report.positives, '/ 负:', report.negatives, ')'); console.log('最大偏移:', report.maxAbsDelta?.toFixed(3) || 'N/A'); if (report.nearLimit?.length > 0) { console.log('\n--- 接近限幅 (|delta| >= 0.4) ---'); for (const { skill, delta } of report.nearLimit) { console.log(' ', skill, ':', delta > 0 ? '+' : '', delta.toFixed(3)); } } if (report.topBoosts?.length > 0) { console.log('\n--- Top 5 增强 ---'); for (const { skill, delta } of report.topBoosts) { console.log(' ', skill, ': +' + delta.toFixed(3)); } } if (report.topPenalties?.length > 0) { console.log('\n--- Top 5 惩罚 ---'); for (const { skill, delta } of report.topPenalties) { console.log(' ', skill, ':', delta.toFixed(3)); } } console.log('\n上次更新:', report.lastUpdated || 'N/A'); } if (require.main === module) { const weights = loadWeights(); const report = analyze(weights); if (process.argv.includes('--json')) { console.log(JSON.stringify(report, null, 2)); } else { printReport(report); } } module.exports = { loadWeights, analyze };