NAC_Blockchain/nac-ai-valuation/src/arbitration.rs

493 lines
16 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.

//! 多元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<AIProvider, f64> {
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<AIValuationResult>,
weights: HashMap<AIProvider, f64>,
) -> Result<FinalValuationResult> {
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<AIProvider, f64>,
) -> Result<Decimal> {
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<AIProvider, f64>,
) -> Result<Decimal> {
// 贝叶斯融合:考虑置信度的加权平均
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<f64> = results.iter()
.map(|r| r.valuation_xtzh.to_string().parse::<f64>().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::<f64>().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<AIProvider, f64>,
) -> Result<f64> {
// 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<f64> = results.iter()
.map(|r| r.valuation_xtzh.to_string().parse::<f64>().unwrap_or(0.0))
.collect();
let mean = valuations.iter().sum::<f64>() / valuations.len() as f64;
let variance = valuations.iter()
.map(|v| (v - mean).powi(2))
.sum::<f64>() / 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<AIProvider, f64>,
) -> Result<String> {
let valuations: Vec<f64> = results.iter()
.map(|r| r.valuation_xtzh.to_string().parse::<f64>().unwrap_or(0.0))
.collect();
let mean = valuations.iter().sum::<f64>() / valuations.len() as f64;
let variance = valuations.iter()
.map(|v| (v - mean).powi(2))
.sum::<f64>() / 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::<f64>().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<f64> = results.iter()
.map(|r| r.valuation_xtzh.to_string().parse::<f64>().unwrap_or(0.0))
.collect();
let mean = valuations.iter().sum::<f64>() / valuations.len() as f64;
let variance = valuations.iter()
.map(|v| (v - mean).powi(2))
.sum::<f64>() / 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::<f64>().unwrap_or(0.0)).fold(f64::NEG_INFINITY, f64::max);
let min_val = results.iter().map(|r| r.valuation_xtzh.to_string().parse::<f64>().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倍以上");
}
}