memoryweave/rust/src/report.rs

215 lines
6.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 自优化报告生成 — 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)
}