100 lines
3.0 KiB
JavaScript
100 lines
3.0 KiB
JavaScript
#!/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 };
|