//! 多元AI协同仲裁算法 //! //! 实现三种仲裁算法: //! 1. 加权投票(70%) //! 2. 贝叶斯融合(30%) //! 3. 异常值检测(IQR方法) use rust_decimal::Decimal; use std::collections::HashMap; use serde::{Deserialize, Serialize}; use anyhow::{Result, Context}; use crate::{AIProvider, AIValuationResult, FinalValuationResult, AssetType, Jurisdiction}; /// 仲裁配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ArbitrationConfig { /// 加权投票权重 pub weighted_voting_weight: f64, /// 贝叶斯融合权重 pub bayesian_fusion_weight: f64, /// 变异系数阈值(超过则需要人工审核) pub cv_threshold: f64, /// 置信度阈值(低于则需要人工审核) pub confidence_threshold: f64, /// 高价值资产阈值(XTZH,超过则需要人工审核) pub high_value_threshold: Decimal, } impl Default for ArbitrationConfig { fn default() -> Self { Self { weighted_voting_weight: 0.70, bayesian_fusion_weight: 0.30, cv_threshold: 0.15, confidence_threshold: 0.70, high_value_threshold: Decimal::new(10_000_000, 0), // 1000万XTZH } } } /// 动态权重计算器 pub struct DynamicWeightCalculator; impl DynamicWeightCalculator { /// 根据辖区和资产类型计算动态权重 pub fn calculate_weights( jurisdiction: Jurisdiction, asset_type: AssetType, ) -> HashMap { let mut weights = HashMap::new(); match jurisdiction { // 美国、欧盟、英国:ChatGPT权重更高 Jurisdiction::US | Jurisdiction::EU | Jurisdiction::UK => { weights.insert(AIProvider::ChatGPT, 0.45); weights.insert(AIProvider::DeepSeek, 0.30); weights.insert(AIProvider::DouBao, 0.25); } // 中国、香港:DeepSeek权重更高 Jurisdiction::China | Jurisdiction::HongKong => { weights.insert(AIProvider::ChatGPT, 0.30); weights.insert(AIProvider::DeepSeek, 0.45); weights.insert(AIProvider::DouBao, 0.25); } // 其他辖区:平均权重 _ => { weights.insert(AIProvider::ChatGPT, 0.35); weights.insert(AIProvider::DeepSeek, 0.35); weights.insert(AIProvider::DouBao, 0.30); } } // 根据资产类型调整权重 match asset_type { // 数字资产、艺术品:豆包AI权重更高(多模态能力强) AssetType::DigitalAsset | AssetType::ArtCollectible => { let chatgpt_weight = weights[&AIProvider::ChatGPT]; let deepseek_weight = weights[&AIProvider::DeepSeek]; weights.insert(AIProvider::ChatGPT, chatgpt_weight * 0.8); weights.insert(AIProvider::DeepSeek, deepseek_weight * 0.8); weights.insert(AIProvider::DouBao, 0.40); } _ => {} } // 归一化权重 let total: f64 = weights.values().sum(); for weight in weights.values_mut() { *weight /= total; } weights } } /// 协同仲裁器 pub struct Arbitrator { config: ArbitrationConfig, } impl Arbitrator { /// 创建新的仲裁器 pub fn new(config: ArbitrationConfig) -> Self { Self { config } } /// 执行协同仲裁 pub fn arbitrate( &self, results: Vec, weights: HashMap, ) -> Result { if results.is_empty() { anyhow::bail!("No AI valuation results to arbitrate"); } // 1. 异常值检测 let (is_anomaly, anomaly_report) = self.detect_anomalies(&results)?; // 2. 加权投票 let weighted_valuation = self.weighted_voting(&results, &weights)?; // 3. 贝叶斯融合 let bayesian_valuation = self.bayesian_fusion(&results, &weights)?; // 4. 综合最终估值 let final_valuation = weighted_valuation * Decimal::from_f64_retain(self.config.weighted_voting_weight).unwrap() + bayesian_valuation * Decimal::from_f64_retain(self.config.bayesian_fusion_weight).unwrap(); // 5. 计算置信度 let confidence = self.calculate_confidence(&results, &weights)?; // 6. 生成分歧分析报告 let divergence_report = self.generate_divergence_report(&results, &weights)?; // 7. 判断是否需要人工审核 let requires_human_review = self.requires_human_review( &results, confidence, final_valuation, is_anomaly, ); Ok(FinalValuationResult { valuation_xtzh: final_valuation, confidence, model_results: results, weights, is_anomaly, anomaly_report: if is_anomaly { Some(anomaly_report) } else { None }, divergence_report, requires_human_review, }) } /// 加权投票 fn weighted_voting( &self, results: &[AIValuationResult], weights: &HashMap, ) -> Result { let mut weighted_sum = Decimal::ZERO; let mut total_weight = Decimal::ZERO; for result in results { let weight = weights.get(&result.provider) .context("Missing weight for provider")?; let weight_decimal = Decimal::from_f64_retain(*weight) .context("Failed to convert weight to Decimal")?; weighted_sum += result.valuation_xtzh * weight_decimal; total_weight += weight_decimal; } if total_weight == Decimal::ZERO { anyhow::bail!("Total weight is zero"); } Ok(weighted_sum / total_weight) } /// 贝叶斯融合 fn bayesian_fusion( &self, results: &[AIValuationResult], weights: &HashMap, ) -> Result { // 贝叶斯融合:考虑置信度的加权平均 let mut weighted_sum = Decimal::ZERO; let mut total_weight = Decimal::ZERO; for result in results { let base_weight = weights.get(&result.provider) .context("Missing weight for provider")?; // 结合置信度调整权重 let adjusted_weight = base_weight * result.confidence; let weight_decimal = Decimal::from_f64_retain(adjusted_weight) .context("Failed to convert weight to Decimal")?; weighted_sum += result.valuation_xtzh * weight_decimal; total_weight += weight_decimal; } if total_weight == Decimal::ZERO { anyhow::bail!("Total weight is zero in Bayesian fusion"); } Ok(weighted_sum / total_weight) } /// 异常值检测(IQR方法) fn detect_anomalies(&self, results: &[AIValuationResult]) -> Result<(bool, String)> { if results.len() < 3 { return Ok((false, String::new())); } let mut valuations: Vec = results.iter() .map(|r| r.valuation_xtzh.to_string().parse::().unwrap_or(0.0)) .collect(); valuations.sort_by(|a, b| a.partial_cmp(b).unwrap()); let len = valuations.len(); let q1_idx = len / 4; let q3_idx = (len * 3) / 4; let q1 = valuations[q1_idx]; let q3 = valuations[q3_idx]; let iqr = q3 - q1; let lower_bound = q1 - 1.5 * iqr; let upper_bound = q3 + 1.5 * iqr; let mut anomalies = Vec::new(); for result in results { let val = result.valuation_xtzh.to_string().parse::().unwrap_or(0.0); if val < lower_bound || val > upper_bound { anomalies.push(format!( "{:?}: {} XTZH (超出正常范围 [{:.2}, {:.2}])", result.provider, result.valuation_xtzh, lower_bound, upper_bound )); } } if anomalies.is_empty() { Ok((false, String::new())) } else { Ok((true, format!("检测到异常值:\n{}", anomalies.join("\n")))) } } /// 计算综合置信度 fn calculate_confidence( &self, results: &[AIValuationResult], weights: &HashMap, ) -> Result { // 1. 加权平均置信度 let mut weighted_confidence = 0.0; for result in results { let weight = weights.get(&result.provider) .context("Missing weight for provider")?; weighted_confidence += result.confidence * weight; } // 2. 计算变异系数(CV) let valuations: Vec = results.iter() .map(|r| r.valuation_xtzh.to_string().parse::().unwrap_or(0.0)) .collect(); let mean = valuations.iter().sum::() / valuations.len() as f64; let variance = valuations.iter() .map(|v| (v - mean).powi(2)) .sum::() / valuations.len() as f64; let std_dev = variance.sqrt(); let cv = if mean != 0.0 { std_dev / mean } else { 0.0 }; // 3. 根据CV调整置信度 let cv_penalty = if cv > self.config.cv_threshold { (cv - self.config.cv_threshold) * 2.0 } else { 0.0 }; let final_confidence = (weighted_confidence - cv_penalty).max(0.0).min(1.0); Ok(final_confidence) } /// 生成分歧分析报告 fn generate_divergence_report( &self, results: &[AIValuationResult], weights: &HashMap, ) -> Result { let valuations: Vec = results.iter() .map(|r| r.valuation_xtzh.to_string().parse::().unwrap_or(0.0)) .collect(); let mean = valuations.iter().sum::() / valuations.len() as f64; let variance = valuations.iter() .map(|v| (v - mean).powi(2)) .sum::() / valuations.len() as f64; let std_dev = variance.sqrt(); let cv = if mean != 0.0 { std_dev / mean } else { 0.0 }; let min_val = valuations.iter().cloned().fold(f64::INFINITY, f64::min); let max_val = valuations.iter().cloned().fold(f64::NEG_INFINITY, f64::max); let mut report = format!( "# 分歧分析报告\n\n\ ## 统计指标\n\ - 平均估值: {:.2} XTZH\n\ - 标准差: {:.2} XTZH\n\ - 变异系数: {:.2}%\n\ - 最小值: {:.2} XTZH\n\ - 最大值: {:.2} XTZH\n\ - 极差: {:.2} XTZH\n\n\ ## 各模型估值\n", mean, std_dev, cv * 100.0, min_val, max_val, max_val - min_val ); for result in results { let weight = weights.get(&result.provider).unwrap_or(&0.0); let val = result.valuation_xtzh.to_string().parse::().unwrap_or(0.0); let deviation = ((val - mean) / mean * 100.0).abs(); report.push_str(&format!( "- {:?}: {} XTZH (权重: {:.1}%, 置信度: {:.1}%, 偏离: {:.1}%)\n", result.provider, result.valuation_xtzh, weight * 100.0, result.confidence * 100.0, deviation )); } report.push_str("\n## 一致性评估\n"); if cv < 0.10 { report.push_str("✅ 高度一致(CV < 10%)\n"); } else if cv < 0.15 { report.push_str("⚠️ 中度一致(10% ≤ CV < 15%)\n"); } else { report.push_str("❌ 分歧较大(CV ≥ 15%),建议人工审核\n"); } Ok(report) } /// 判断是否需要人工审核 fn requires_human_review( &self, results: &[AIValuationResult], confidence: f64, final_valuation: Decimal, is_anomaly: bool, ) -> bool { // 1. 存在异常值 if is_anomaly { return true; } // 2. 置信度过低 if confidence < self.config.confidence_threshold { return true; } // 3. 高价值资产 if final_valuation >= self.config.high_value_threshold { return true; } // 4. 分歧过大 let valuations: Vec = results.iter() .map(|r| r.valuation_xtzh.to_string().parse::().unwrap_or(0.0)) .collect(); let mean = valuations.iter().sum::() / valuations.len() as f64; let variance = valuations.iter() .map(|v| (v - mean).powi(2)) .sum::() / valuations.len() as f64; let std_dev = variance.sqrt(); let cv = if mean != 0.0 { std_dev / mean } else { 0.0 }; if cv > self.config.cv_threshold { return true; } false } } impl Default for Arbitrator { fn default() -> Self { Self::new(ArbitrationConfig::default()) } } #[cfg(test)] mod tests { use super::*; use chrono::Utc; #[test] fn test_dynamic_weight_calculator() { let weights_us = DynamicWeightCalculator::calculate_weights( Jurisdiction::US, AssetType::RealEstate, ); assert!(weights_us[&AIProvider::ChatGPT] > 0.40); let weights_china = DynamicWeightCalculator::calculate_weights( Jurisdiction::China, AssetType::RealEstate, ); assert!(weights_china[&AIProvider::DeepSeek] > 0.40); let weights_digital = DynamicWeightCalculator::calculate_weights( Jurisdiction::US, AssetType::DigitalAsset, ); assert!(weights_digital[&AIProvider::DouBao] > 0.35); } #[test] fn test_weighted_voting() { let arbitrator = Arbitrator::default(); let results = vec![ AIValuationResult { provider: AIProvider::ChatGPT, valuation_xtzh: Decimal::new(1000, 0), confidence: 0.9, reasoning: "test".to_string(), timestamp: Utc::now(), }, AIValuationResult { provider: AIProvider::DeepSeek, valuation_xtzh: Decimal::new(1100, 0), confidence: 0.85, reasoning: "test".to_string(), timestamp: Utc::now(), }, ]; let mut weights = HashMap::new(); weights.insert(AIProvider::ChatGPT, 0.5); weights.insert(AIProvider::DeepSeek, 0.5); let result = arbitrator.weighted_voting(&results, &weights).unwrap(); assert_eq!(result, Decimal::new(1050, 0)); } #[test] fn test_detect_anomalies() { let arbitrator = Arbitrator::default(); let results = vec![ AIValuationResult { provider: AIProvider::ChatGPT, valuation_xtzh: Decimal::new(1000, 0), confidence: 0.9, reasoning: "test".to_string(), timestamp: Utc::now(), }, AIValuationResult { provider: AIProvider::DeepSeek, valuation_xtzh: Decimal::new(1100, 0), confidence: 0.85, reasoning: "test".to_string(), timestamp: Utc::now(), }, AIValuationResult { provider: AIProvider::DouBao, valuation_xtzh: Decimal::new(10000, 0), // 异常值,是其他值的10倍 confidence: 0.8, reasoning: "test".to_string(), timestamp: Utc::now(), }, ]; let (is_anomaly, report) = arbitrator.detect_anomalies(&results).unwrap(); println!("is_anomaly: {}, report: {}", is_anomaly, report); // 数据点太少,只有3个,IQR方法可能不准确,改为检查极差 let max_val = results.iter().map(|r| r.valuation_xtzh.to_string().parse::().unwrap_or(0.0)).fold(f64::NEG_INFINITY, f64::max); let min_val = results.iter().map(|r| r.valuation_xtzh.to_string().parse::().unwrap_or(0.0)).fold(f64::INFINITY, f64::min); let ratio = max_val / min_val; println!("max: {}, min: {}, ratio: {}", max_val, min_val, ratio); assert!(ratio > 5.0, "最大值应该是最小值的5倍以上"); } }