215 lines
6.7 KiB
Rust
215 lines
6.7 KiB
Rust
// 自优化报告生成 — 7 项指标 + 退化检测
|
||
// 深度整合步骤 5:收集所有步骤结果 → 生成 WebSocket 推送的报告
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// 整合完整报告
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ConsolidationReport {
|
||
pub timestamp: String,
|
||
pub duration_secs: f64,
|
||
|
||
// Step 1: 聚类
|
||
pub clusters_found: usize,
|
||
pub noise_points: usize,
|
||
pub duplicate_pairs: usize,
|
||
|
||
// Step 2: 修剪
|
||
pub pruned: Option<PruneStats>,
|
||
|
||
// Step 3: 衰减校准
|
||
pub decay_rates: HashMap<String, f64>,
|
||
pub r_squared_values: HashMap<String, f64>,
|
||
|
||
// Step 4: 质量回溯
|
||
pub quality_score: f64,
|
||
pub low_info_count: usize,
|
||
pub hallucinations: usize,
|
||
|
||
// Step 5: 自优化仪表盘
|
||
pub dashboard: DashboardSnapshot,
|
||
|
||
// 退化检测
|
||
pub degradation: Vec<DegradationAlert>,
|
||
}
|
||
|
||
/// 修剪统计(引用 graph_prune 模块)
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct PruneStats {
|
||
pub isolated_nodes_removed: usize,
|
||
pub low_weight_edges_removed: usize,
|
||
pub redundant_edges_merged: usize,
|
||
pub nodes_before: usize,
|
||
pub nodes_after: usize,
|
||
pub edges_before: usize,
|
||
pub edges_after: usize,
|
||
}
|
||
|
||
/// 仪表盘快照
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct DashboardSnapshot {
|
||
/// 召回有用率
|
||
pub recall_useful_rate: f64,
|
||
/// 召回命中率
|
||
pub recall_hit_rate: f64,
|
||
/// 缺口闭环率
|
||
pub gap_closure_rate: f64,
|
||
/// 修正传播率
|
||
pub cascade_propagation_rate: f64,
|
||
/// 垃圾淘汰速度 (条/天)
|
||
pub deprecation_rate: f64,
|
||
/// 蒸馏信息损失率
|
||
pub distill_loss_rate: f64,
|
||
/// 冲突自动裁决率
|
||
pub auto_resolve_rate: f64,
|
||
}
|
||
|
||
/// 退化告警
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct DegradationAlert {
|
||
pub metric: String,
|
||
pub current_value: f64,
|
||
pub previous_value: f64,
|
||
pub severity: String, // warning / critical
|
||
pub message: String,
|
||
}
|
||
|
||
/// 报告生成器
|
||
pub struct ReportGenerator {
|
||
previous_metrics: Option<DashboardSnapshot>,
|
||
}
|
||
|
||
impl ReportGenerator {
|
||
pub fn new() -> Self {
|
||
Self { previous_metrics: None }
|
||
}
|
||
|
||
/// 生成整合报告
|
||
pub fn generate(
|
||
&self,
|
||
clusters: usize,
|
||
noise: usize,
|
||
duplicate_pairs: usize,
|
||
pruned: Option<PruneStats>,
|
||
decay_rates: HashMap<String, f64>,
|
||
r_squared: HashMap<String, f64>,
|
||
quality_score: f64,
|
||
low_info: usize,
|
||
hallucinations: usize,
|
||
dash: &DashboardSnapshot,
|
||
) -> ConsolidationReport {
|
||
let degradation = self.detect_degradation(dash);
|
||
|
||
let report = ConsolidationReport {
|
||
timestamp: chrono_now(),
|
||
duration_secs: 0.0,
|
||
clusters_found: clusters,
|
||
noise_points: noise,
|
||
duplicate_pairs,
|
||
pruned,
|
||
decay_rates,
|
||
r_squared_values: r_squared,
|
||
quality_score,
|
||
low_info_count: low_info,
|
||
hallucinations,
|
||
dashboard: dash.clone(),
|
||
degradation,
|
||
};
|
||
|
||
eprintln!(
|
||
"[report] consolidation complete: clusters={}, quality={:.3}, alerts={}",
|
||
report.clusters_found, report.quality_score, report.degradation.len(),
|
||
);
|
||
|
||
report
|
||
}
|
||
|
||
/// 退化检测
|
||
fn detect_degradation(&self, current: &DashboardSnapshot) -> Vec<DegradationAlert> {
|
||
let mut alerts = Vec::new();
|
||
|
||
let prev = match &self.previous_metrics {
|
||
Some(p) => p,
|
||
None => return alerts, // 首次运行
|
||
};
|
||
|
||
// 检查各项指标的退化
|
||
let checks: Vec<(&str, f64, f64, f64, &str)> = vec![
|
||
("召回有用率", current.recall_useful_rate, prev.recall_useful_rate, 0.7, "连续下降 → 检查 not_useful 共性"),
|
||
("召回命中率", current.recall_hit_rate, prev.recall_hit_rate, 0.5, "审查 Embedding/Rerank 管线"),
|
||
("缺口闭环率", current.gap_closure_rate, prev.gap_closure_rate, 0.0, "14天=0 → 审查缺口分类准确性"),
|
||
("蒸馏信息损失率", current.distill_loss_rate, prev.distill_loss_rate, 0.3, "信息损失 > 0.3 → 重蒸馏受影响批次"),
|
||
];
|
||
|
||
for (name, cur, prev_val, threshold, msg) in checks {
|
||
let drop = prev_val - cur;
|
||
if drop > 0.1 && cur < threshold {
|
||
alerts.push(DegradationAlert {
|
||
metric: name.to_string(),
|
||
current_value: cur,
|
||
previous_value: prev_val,
|
||
severity: if cur < threshold * 0.5 { "critical".into() } else { "warning".into() },
|
||
message: msg.to_string(),
|
||
});
|
||
}
|
||
}
|
||
|
||
// 垃圾淘汰异常检测
|
||
let deprec = current.deprecation_rate;
|
||
if deprec > 20.0 {
|
||
alerts.push(DegradationAlert {
|
||
metric: "垃圾淘汰速度".into(),
|
||
current_value: deprec,
|
||
previous_value: prev.deprecation_rate,
|
||
severity: "critical".into(),
|
||
message: "> 20/天 异常 → 自动暂停遗忘".into(),
|
||
});
|
||
} else if deprec == 0.0 && prev.deprecation_rate > 0.0 {
|
||
alerts.push(DegradationAlert {
|
||
metric: "垃圾淘汰速度".into(),
|
||
current_value: 0.0,
|
||
previous_value: prev.deprecation_rate,
|
||
severity: "warning".into(),
|
||
message: "= 0/天 → 遗忘可能失效".into(),
|
||
});
|
||
}
|
||
|
||
alerts
|
||
}
|
||
}
|
||
|
||
/// 生成简易 ISO 8601 时间戳
|
||
fn chrono_now() -> String {
|
||
use std::time::SystemTime;
|
||
let now = SystemTime::now()
|
||
.duration_since(SystemTime::UNIX_EPOCH)
|
||
.unwrap_or_default();
|
||
|
||
let secs = now.as_secs();
|
||
let days = secs / 86400;
|
||
let remaining = secs % 86400;
|
||
let hours = remaining / 3600;
|
||
let minutes = (remaining % 3600) / 60;
|
||
let seconds = remaining % 60;
|
||
|
||
// 从 Unix epoch 推算日期 (简化版,生产建议用 chrono crate)
|
||
let (y, m, d) = civil_from_days(days as i64 + 719468);
|
||
|
||
format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", y, m, d, hours, minutes, seconds)
|
||
}
|
||
|
||
fn civil_from_days(days: i64) -> (i64, u32, u32) {
|
||
let z = days + 719468;
|
||
let era = if z >= 0 { z } else { z - 146096 } / 146097;
|
||
let doe = (z - era * 146097) as u32;
|
||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||
let y = yoe as i64 + era * 400;
|
||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||
let mp = (5 * doy + 2) / 153;
|
||
let d = doy - (153 * mp + 2) / 5 + 1;
|
||
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
||
let y = if m <= 2 { y + 1 } else { y };
|
||
(y, m, d)
|
||
}
|