dApp Docs/AI Agent 链上信誉评分系统
Development reference. Not independently verified for production.

AI Agent 链上信誉评分系统

适用链:msg-chain-1 | Bech32 前缀:msg
核心语言:Rust (CosmWasm) + Python (算法) + TypeScript (客户端)
版本:v1.0.0

⚠️ No-Go Disclaimer: MSGChain 主网裁决为 No-Go。本文件所有内容反映的是开发阶段的技术设计,不代表主网未独立核验上线状态。生产部署状态请以白皮书为准:https://msgchain.org/whitepaper/


目录

  1. 概述
  2. 信誉合约实现
  3. 评分算法
  4. 信誉使用场景
  5. 争议与申诉
  6. 前端
  7. 集成到现有系统

1. 概述

1.1 为什么 AI Agent 需要信誉系统

在 MSG Chain 上,AI Agent 自主执行任务、支付费用、相互协作。没有信誉系统,整个 Agent 经济面临以下根本性问题:

信誉评分系统通过量化 Agent 的历史行为,为任务匹配、定价、抵押要求等提供可验证的参考依据。

1.2 信誉来源

系统从三个维度收集信誉数据:

来源 类型 可信度 示例
链上行为 客观 高 任务完成率、违约记录、支付历史
链下评价 主观 中 任务发布者的星级评分、文字评价
可验证凭证 认证 高 KYC 认证、第三方审计报告、技能证书

链上行为直接从交易历史中提取,不可篡改,是最可靠的信誉来源。链下评价通过链上提交哈希、链下存储内容的方式保存。可验证凭证由受信任的发行方签名,Agent 可选择性披露。

1.3 评分核心维度

系统定义了六个核心评分维度:

  1. 任务成功率(权重 30%):已完成任务 / 总接受任务
  2. 平均评分(权重 25%):任务发布者的 1-5 星评价均值
  3. 任务总量(权重 15%):历史累计完成任务数(对数缩放)
  4. 抵押乘数(权重 10%):根据抵押量获得的信誉加成
  5. 时间衰减(权重 10%):近期行为权重高于历史行为
  6. 多样性(权重 10%):合作过的不同发布者数量

1.4 评分公式概要

composite_score = 0.30 * normalized_success_rate
                + 0.25 * (avg_rating - 1) / 4
                + 0.15 * log(1 + total_tasks) / log(1 + MAX_TASKS)
                + 0.10 * stake_multiplier
                + 0.10 * time_decay_factor
                + 0.10 * diversity_factor

评分范围归一化为 [0, 100],其中:

1.5 系统架构概览

+-------------------------------------------------------+
|                   前端 (React)                          |
|   信誉看板 | 评分详情 | Agent 对比 | 争议页面           |
+---------------------------+---------------------------+
                           | RPC / GraphQL
+---------------------------v---------------------------+
|                    API 层 (TypeScript)                 |
|   查询聚合 | 信誉排序 | 任务匹配 | 争议提交             |
+---------------------------+---------------------------+
                           | CosmWasm
+---------------------------v---------------------------+
|                智能合约层 (Rust)                        |
|  +----------+ +----------+ +----------+               |
|  | 信誉核心  | | 争议模块  | | 治理模块  |               |
|  +----------+ +----------+ +----------+               |
|  +----------+ +----------+ +----------+               |
|  | 评分算法  | | 抵押模块  | | 凭证验证  |               |
|  +----------+ +----------+ +----------+               |
+---------------------------+---------------------------+
                           | ABCI / Cosmos SDK
+---------------------------v---------------------------+
|                  MSG Chain 底层                         |
|   共识 | 存储 | IBC | 质押 | 治理                      |
+-------------------------------------------------------+

2. 信誉合约实现

2.1 合约结构

contracts/
+-- reputation-core/           # 信誉核心合约
|   +-- Cargo.toml
|   +-- src/
|   |   +-- contract.rs        # 入口与消息分发
|   |   +-- state.rs           # 状态存储
|   |   +-- msg.rs             # 消息定义
|   |   +-- scoring.rs         # 评分算法
|   |   +-- stake.rs           # 抵押逻辑
|   |   +-- dispute.rs         # 争议处理
|   |   +-- helpers.rs         # 工具函数
|   +-- examples/
|       +-- full_integration.rs
+-- reputation-oracle/         # 链下数据预言机适配器
+-- reputation-governance/     # 治理覆写模块

2.2 Cargo.toml

[package]
name = "reputation-core"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["library"]
library = []

[dependencies]
cosmwasm-std = { version = "1.5", features = ["stargate"] }
cosmwasm-storage = "1.5"
cosmwasm-schema = "1.5"
cw-storage-plus = "1.2"
cw-controllers = "1.1"
cw-utils = "1.0"
cw2 = "1.1"
schemars = "0.8"
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde-json-wasm = "0.5"
thiserror = "1.0"
uint = "0.9"
cosmwasm-crypto = "1.5"

[dev-dependencies]
cw-multi-test = "0.18"
assert_matches = "1.5"

2.3 状态定义

// contracts/reputation-core/src/state.rs
use cosmwasm_std::{Addr, Decimal, Timestamp, Uint128};
use cw_storage_plus::{Item, Map, IndexedMap, MultiIndex};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ReputationScore {
    pub agent: Addr,
    pub total_tasks: Uint128,
    pub successful_tasks: Uint128,
    pub failed_tasks: Uint128,
    pub avg_rating: Decimal,
    pub total_earned: Uint128,
    pub stake_amount: Uint128,
    pub last_activity: Timestamp,
    pub composite_score: Decimal,
}

impl ReputationScore {
    pub fn new(agent: Addr) -> Self {
        ReputationScore {
            agent,
            total_tasks: Uint128::zero(),
            successful_tasks: Uint128::zero(),
            failed_tasks: Uint128::zero(),
            avg_rating: Decimal::one(),
            total_earned: Uint128::zero(),
            stake_amount: Uint128::zero(),
            last_activity: Timestamp::from_nanos(0),
            composite_score: Decimal::zero(),
        }
    }

    pub fn success_rate(&self) -> Decimal {
        if self.total_tasks.is_zero() {
            return Decimal::zero();
        }
        Decimal::from_ratio(self.successful_tasks, self.total_tasks)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct TaskRecord {
    pub task_id: String,
    pub agent: Addr,
    pub requester: Addr,
    pub status: TaskStatus,
    pub rating: Option<u8>,
    pub reward: Uint128,
    pub timestamp: Timestamp,
    pub evidence_hash: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum TaskStatus {
    Assigned,
    InProgress,
    Completed,
    Failed,
    Disputed,
    Resolved,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Rating {
    pub rater: Addr,
    pub agent: Addr,
    pub task_id: String,
    pub score: u8,
    pub comment_hash: String,
    pub timestamp: Timestamp,
    pub weight: Decimal,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Dispute {
    pub dispute_id: String,
    pub task_id: String,
    pub initiator: Addr,
    pub respondent: Addr,
    pub dispute_type: DisputeType,
    pub status: DisputeStatus,
    pub evidence_hashes: Vec<String>,
    pub arbitrator: Option<Addr>,
    pub created_at: Timestamp,
    pub resolved_at: Option<Timestamp>,
    pub resolution: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum DisputeType {
    RatingDisagreement,
    FraudAllegation,
    NonDelivery,
    Plagiarism,
    Other(String),
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum DisputeStatus {
    Open,
    UnderReview,
    Resolved,
    Dismissed,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct StakePosition {
    pub agent: Addr,
    pub amount: Uint128,
    pub locked_until: Timestamp,
    pub staked_at: Timestamp,
    pub auto_renew: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct GovernanceParams {
    pub success_rate_weight: Decimal,
    pub rating_weight: Decimal,
    pub volume_weight: Decimal,
    pub stake_weight: Decimal,
    pub recency_weight: Decimal,
    pub diversity_weight: Decimal,
    pub min_rating: u8,
    pub max_rating: u8,
    pub dispute_deposit: Uint128,
    pub max_disputes_active: u32,
    pub admin: Addr,
}

impl Default for GovernanceParams {
    fn default() -> Self {
        GovernanceParams {
            success_rate_weight: Decimal::percent(30),
            rating_weight: Decimal::percent(25),
            volume_weight: Decimal::percent(15),
            stake_weight: Decimal::percent(10),
            recency_weight: Decimal::percent(10),
            diversity_weight: Decimal::percent(10),
            min_rating: 1,
            max_rating: 5,
            dispute_deposit: Uint128::new(100_000_000),
            max_disputes_active: 5,
            admin: Addr::unchecked(""),
        }
    }
}

pub const CONFIG_KEY: &str = "config";
pub const LATEST_SCORE_KEY: &str = "latest_score";

pub const AGENT_SCORES: Map<&Addr, ReputationScore> = Map::new("agent_scores");

pub struct ScoreIndexes {
    pub by_composite: MultiIndex<Vec<u8>, ReputationScore, Addr>,
}

pub const AGENT_SCORE_INDEXES: IndexedMap<&Addr, ReputationScore> = IndexedMap::new(
    "score_index",
    ScoreIndexes {
        by_composite: MultiIndex::new(
            |_, score| score.composite_score.to_string().into_bytes(),
            "score_index",
            "by_composite",
        ),
    },
);

pub const TASK_RECORDS: Map<&str, TaskRecord> = Map::new("tasks");
pub const AGENT_TASKS: Map<(&Addr, &str), bool> = Map::new("agent_tasks");
pub const RATINGS: Map<(&Addr, &str), Vec<Rating>> = Map::new("ratings");
pub const DISPUTES: Map<&str, Dispute> = Map::new("disputes");
pub const AGENT_DISPUTES: Map<(&Addr, &str), bool> = Map::new("agent_disputes");
pub const STAKE_POSITIONS: Map<&Addr, StakePosition> = Map::new("stakes");
pub const GOV_PARAMS: Item<GovernanceParams> = Item::new("gov_params");
pub const COLLABORATORS: Map<(&Addr, &Addr), bool> = Map::new("collabs");

2.4 消息定义

// contracts/reputation-core/src/msg.rs
use cosmwasm_std::{Addr, Decimal, Timestamp, Uint128};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
    pub admin: String,
    pub params: Option<GovernanceParamsMsg>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct GovernanceParamsMsg {
    pub success_rate_weight: Option<Decimal>,
    pub rating_weight: Option<Decimal>,
    pub volume_weight: Option<Decimal>,
    pub stake_weight: Option<Decimal>,
    pub recency_weight: Option<Decimal>,
    pub diversity_weight: Option<Decimal>,
    pub min_rating: Option<u8>,
    pub max_rating: Option<u8>,
    pub dispute_deposit: Option<Uint128>,
    pub max_disputes_active: Option<u32>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    RecordTaskResult {
        task_id: String,
        agent: String,
        requester: String,
        status: TaskStatusMsg,
        reward: Uint128,
        evidence_hash: Option<String>,
    },
    SubmitRating {
        agent: String,
        task_id: String,
        score: u8,
        comment_hash: String,
    },
    Stake {
        amount: Uint128,
        lock_duration_secs: u64,
    },
    Unstake {
        amount: Uint128,
    },
    OpenDispute {
        task_id: String,
        respondent: String,
        dispute_type: DisputeTypeMsg,
        evidence_hashes: Vec<String>,
    },
    SubmitDisputeEvidence {
        dispute_id: String,
        evidence_hash: String,
    },
    ResolveDispute {
        dispute_id: String,
        resolution: String,
        adjusted_rating: Option<u8>,
    },
    GovernanceOverride {
        agent: String,
        new_score: Decimal,
        reason: String,
    },
    UpdateParams {
        params: GovernanceParamsMsg,
    },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum TaskStatusMsg {
    Completed,
    Failed,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum DisputeTypeMsg {
    RatingDisagreement,
    FraudAllegation,
    NonDelivery,
    Plagiarism,
    Other(String),
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    GetReputation { agent: String },
    GetReputationBatch { agents: Vec<String> },
    ListTopAgents { start_after: Option<String>, limit: Option<u32> },
    GetAgentTasks { agent: String, start_after: Option<String>, limit: Option<u32> },
    GetAgentRatings { agent: String, start_after: Option<String>, limit: Option<u32> },
    GetDispute { dispute_id: String },
    GetStake { agent: String },
    GetParams {},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ReputationResponse {
    pub score: ReputationScoreResp,
    pub breakdown: ScoreBreakdown,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ReputationScoreResp {
    pub agent: Addr,
    pub total_tasks: Uint128,
    pub successful_tasks: Uint128,
    pub failed_tasks: Uint128,
    pub success_rate: Decimal,
    pub avg_rating: Decimal,
    pub total_earned: Uint128,
    pub stake_amount: Uint128,
    pub last_activity: Timestamp,
    pub composite_score: Decimal,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ScoreBreakdown {
    pub success_rate_component: Decimal,
    pub rating_component: Decimal,
    pub volume_component: Decimal,
    pub stake_component: Decimal,
    pub recency_component: Decimal,
    pub diversity_component: Decimal,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct AgentListResponse {
    pub agents: Vec<ReputationScoreResp>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct TaskListResponse {
    pub tasks: Vec<TaskRecordResp>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct TaskRecordResp {
    pub task_id: String,
    pub agent: Addr,
    pub requester: Addr,
    pub status: String,
    pub rating: Option<u8>,
    pub reward: Uint128,
    pub timestamp: Timestamp,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct RatingListResponse {
    pub ratings: Vec<RatingResp>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct RatingResp {
    pub rater: Addr,
    pub score: u8,
    pub comment_hash: String,
    pub timestamp: Timestamp,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct DisputeResponse {
    pub dispute: DisputeResp,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct DisputeResp {
    pub dispute_id: String,
    pub task_id: String,
    pub initiator: Addr,
    pub respondent: Addr,
    pub dispute_type: String,
    pub status: String,
    pub evidence_hashes: Vec<String>,
    pub created_at: Timestamp,
    pub resolved_at: Option<Timestamp>,
    pub resolution: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct StakeResponse {
    pub agent: Addr,
    pub amount: Uint128,
    pub locked_until: Timestamp,
    pub staked_at: Timestamp,
    pub auto_renew: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ParamsResponse {
    pub params: GovernanceParams,
}

2.5 合约入口

// contracts/reputation-core/src/contract.rs
use cosmwasm_std::{
    entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo,
    Response, StdError, StdResult,
};
use crate::msg::{
    ExecuteMsg, InstantiateMsg, QueryMsg,
    ReputationResponse, AgentListResponse, TaskListResponse,
    RatingListResponse, DisputeResponse, StakeResponse, ParamsResponse,
};
use crate::state::{GovernanceParams, GOV_PARAMS};
use crate::scoring::{compute_composite_score, compute_score_breakdown};
use crate::stake::handle_stake;

const MAX_LIMIT: u32 = 100;
const DEFAULT_LIMIT: u32 = 20;

#[entry_point]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: InstantiateMsg,
) -> StdResult<Response> {
    let params = GovernanceParams {
        admin: deps.api.addr_validate(&msg.admin)?,
        ..Default::default()
    };
    GOV_PARAMS.save(deps.storage, &params)?;
    Ok(Response::new()
        .add_attribute("action", "instantiate")
        .add_attribute("admin", params.admin))
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::RecordTaskResult { task_id, agent, requester, status, reward, evidence_hash } => {
            execute_record_task_result(deps, env, info, task_id, agent, requester, status, reward, evidence_hash)
        }
        ExecuteMsg::SubmitRating { agent, task_id, score, comment_hash } => {
            execute_submit_rating(deps, env, info, agent, task_id, score, comment_hash)
        }
        ExecuteMsg::Stake { amount, lock_duration_secs } => {
            handle_stake(deps, env, info, amount, lock_duration_secs)
        }
        ExecuteMsg::Unstake { amount } => {
            handle_unstake(deps, env, info, amount)
        }
        ExecuteMsg::OpenDispute { task_id, respondent, dispute_type, evidence_hashes } => {
            handle_open_dispute(deps, env, info, task_id, respondent, dispute_type, evidence_hashes)
        }
        ExecuteMsg::SubmitDisputeEvidence { dispute_id, evidence_hash } => {
            handle_submit_evidence(deps, env, info, dispute_id, evidence_hash)
        }
        ExecuteMsg::ResolveDispute { dispute_id, resolution, adjusted_rating } => {
            handle_resolve_dispute(deps, env, info, dispute_id, resolution, adjusted_rating)
        }
        ExecuteMsg::GovernanceOverride { agent, new_score, reason } => {
            handle_governance_override(deps, env, info, agent, new_score, reason)
        }
        ExecuteMsg::UpdateParams { params } => {
            handle_update_params(deps, env, info, params)
        }
    }
}

#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetReputation { agent } => {
            to_json_binary(&query_reputation(deps, agent)?)
        }
        QueryMsg::GetReputationBatch { agents } => {
            to_json_binary(&query_reputation_batch(deps, agents)?)
        }
        QueryMsg::ListTopAgents { start_after, limit } => {
            to_json_binary(&query_top_agents(deps, start_after, limit)?)
        }
        QueryMsg::GetAgentTasks { agent, start_after, limit } => {
            to_json_binary(&query_agent_tasks(deps, agent, start_after, limit)?)
        }
        QueryMsg::GetAgentRatings { agent, start_after, limit } => {
            to_json_binary(&query_agent_ratings(deps, agent, start_after, limit)?)
        }
        QueryMsg::GetDispute { dispute_id } => {
            to_json_binary(&query_dispute(deps, dispute_id)?)
        }
        QueryMsg::GetStake { agent } => {
            to_json_binary(&query_stake(deps, agent)?)
        }
        QueryMsg::GetParams {} => {
            to_json_binary(&query_params(deps)?)
        }
    }
}

fn execute_record_task_result(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    task_id: String,
    agent: String,
    requester: String,
    status: crate::msg::TaskStatusMsg,
    reward: Uint128,
    evidence_hash: Option<String>,
) -> StdResult<Response> {
    let agent_addr = deps.api.addr_validate(&agent)?;
    let requester_addr = deps.api.addr_validate(&requester)?;

    let task_status = match status {
        crate::msg::TaskStatusMsg::Completed => crate::state::TaskStatus::Completed,
        crate::msg::TaskStatusMsg::Failed => crate::state::TaskStatus::Failed,
    };

    let task = crate::state::TaskRecord {
        task_id: task_id.clone(),
        agent: agent_addr.clone(),
        requester: requester_addr.clone(),
        status: task_status,
        rating: None,
        reward,
        timestamp: env.block.time,
        evidence_hash,
    };

    crate::state::TASK_RECORDS.save(deps.storage, &task_id, &task)?;
    crate::state::AGENT_TASKS.save(deps.storage, (&agent_addr, &task_id), &true)?;
    crate::state::COLLABORATORS.save(deps.storage, (&agent_addr, &requester_addr), &true)?;

    let mut score = crate::state::AGENT_SCORES
        .may_load(deps.storage, &agent_addr)?
        .unwrap_or_else(|| crate::state::ReputationScore::new(agent_addr.clone()));

    score.total_tasks += Uint128::one();
    score.total_earned += reward;
    score.last_activity = env.block.time;

    match status {
        crate::msg::TaskStatusMsg::Completed => {
            score.successful_tasks += Uint128::one();
        }
        crate::msg::TaskStatusMsg::Failed => {
            score.failed_tasks += Uint128::one();
        }
    }

    let params = GOV_PARAMS.load(deps.storage)?;
    let breakdown = compute_score_breakdown(deps.storage, &score, &agent_addr, &env.block.time, &params)?;
    score.composite_score = compute_composite_score(&breakdown, &params);
    crate::state::AGENT_SCORES.save(deps.storage, &agent_addr, &score)?;

    Ok(Response::new()
        .add_attribute("action", "record_task_result")
        .add_attribute("task_id", &task_id)
        .add_attribute("agent", &agent)
        .add_attribute("status", format!("{:?}", status)))
}

fn execute_submit_rating(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    agent: String,
    task_id: String,
    score: u8,
    comment_hash: String,
) -> StdResult<Response> {
    let agent_addr = deps.api.addr_validate(&agent)?;
    let params = GOV_PARAMS.load(deps.storage)?;

    if score < params.min_rating || score > params.max_rating {
        return Err(StdError::generic_err(format!(
            "Rating must be between {} and {}", params.min_rating, params.max_rating
        )));
    }

    let mut task = crate::state::TASK_RECORDS
        .load(deps.storage, &task_id)
        .map_err(|_| StdError::generic_err("Task not found"))?;
    task.rating = Some(score);
    crate::state::TASK_RECORDS.save(deps.storage, &task_id, &task)?;

    let rating = crate::state::Rating {
        rater: task.requester.clone(),
        agent: agent_addr.clone(),
        task_id: task_id.clone(),
        score,
        comment_hash,
        timestamp: env.block.time,
        weight: Decimal::one(),
    };

    let mut ratings = crate::state::RATINGS
        .may_load(deps.storage, (&agent_addr, &task_id))?
        .unwrap_or_default();
    ratings.push(rating);
    crate::state::RATINGS.save(deps.storage, (&agent_addr, &task_id), &ratings)?;

    let mut total_score = 0u64;
    let mut count = 0u64;
    for r in ratings.iter() {
        total_score += r.score as u64;
        count += 1;
    }

    let mut agent_score = crate::state::AGENT_SCORES.load(deps.storage, &agent_addr)?;
    if count > 0 {
        agent_score.avg_rating = Decimal::from_ratio(total_score, count);
    }

    let breakdown = compute_score_breakdown(deps.storage, &agent_score, &agent_addr, &env.block.time, &params)?;
    agent_score.composite_score = compute_composite_score(&breakdown, &params);
    crate::state::AGENT_SCORES.save(deps.storage, &agent_addr, &agent_score)?;

    Ok(Response::new()
        .add_attribute("action", "submit_rating")
        .add_attribute("agent", &agent)
        .add_attribute("task_id", &task_id)
        .add_attribute("score", score.to_string()))
}

fn handle_unstake(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    amount: Uint128,
) -> StdResult<Response> {
    let mut stake = crate::state::STAKE_POSITIONS
        .load(deps.storage, &info.sender)
        .map_err(|_| StdError::generic_err("No active stake position"))?;

    if env.block.time < stake.locked_until {
        return Err(StdError::generic_err(format!(
            "Stake is locked until {}", stake.locked_until
        )));
    }

    if amount > stake.amount {
        return Err(StdError::generic_err("Insufficient staked amount"));
    }

    stake.amount -= amount;
    if stake.amount.is_zero() {
        crate::state::STAKE_POSITIONS.remove(deps.storage, &info.sender);
    } else {
        crate::state::STAKE_POSITIONS.save(deps.storage, &info.sender, &stake)?;
    }

    let mut score = crate::state::AGENT_SCORES.may_load(deps.storage, &info.sender)?
        .unwrap_or_else(|| crate::state::ReputationScore::new(info.sender.clone()));
    score.stake_amount = stake.amount;

    let params = GOV_PARAMS.load(deps.storage)?;
    let breakdown = compute_score_breakdown(deps.storage, &score, &info.sender, &env.block.time, &params)?;
    score.composite_score = compute_composite_score(&breakdown, &params);
    crate::state::AGENT_SCORES.save(deps.storage, &info.sender, &score)?;

    Ok(Response::new()
        .add_attribute("action", "unstake")
        .add_attribute("agent", info.sender.as_str())
        .add_attribute("amount", &amount.to_string()))
}

fn handle_submit_evidence(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    dispute_id: String,
    evidence_hash: String,
) -> StdResult<Response> {
    let mut dispute = crate::state::DISPUTES
        .load(deps.storage, &dispute_id)
        .map_err(|_| StdError::generic_err("Dispute not found"))?;
    dispute.evidence_hashes.push(evidence_hash.clone());
    crate::state::DISPUTES.save(deps.storage, &dispute_id, &dispute)?;

    Ok(Response::new()
        .add_attribute("action", "submit_evidence")
        .add_attribute("dispute_id", &dispute_id)
        .add_attribute("evidence_hash", &evidence_hash))
}

fn handle_resolve_dispute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    dispute_id: String,
    resolution: String,
    adjusted_rating: Option<u8>,
) -> StdResult<Response> {
    let params = GOV_PARAMS.load(deps.storage)?;
    if info.sender != params.admin {
        return Err(StdError::generic_err("Only admin can resolve disputes"));
    }

    let mut dispute = crate::state::DISPUTES
        .load(deps.storage, &dispute_id)
        .map_err(|_| StdError::generic_err("Dispute not found"))?;
    dispute.status = crate::state::DisputeStatus::Resolved;
    dispute.resolved_at = Some(env.block.time);
    dispute.resolution = Some(resolution.clone());
    crate::state::DISPUTES.save(deps.storage, &dispute_id, &dispute)?;

    if let Some(new_rating) = adjusted_rating {
        let task = crate::state::TASK_RECORDS.load(deps.storage, &dispute.task_id)?;
        let agent_addr = task.agent.clone();
        let mut agent_score = crate::state::AGENT_SCORES.load(deps.storage, &agent_addr)?;

        let mut ratings = crate::state::RATINGS.load(deps.storage, (&agent_addr, &dispute.task_id))?;
        for r in ratings.iter_mut() {
            if r.rater == dispute.initiator {
                r.score = new_rating;
            }
        }
        crate::state::RATINGS.save(deps.storage, (&agent_addr, &dispute.task_id), &ratings)?;

        let mut total_score = 0u64;
        let mut count = 0u64;
        for r in ratings.iter() {
            total_score += r.score as u64;
            count += 1;
        }
        if count > 0 {
            agent_score.avg_rating = Decimal::from_ratio(total_score, count);
        }

        let breakdown = compute_score_breakdown(deps.storage, &agent_score, &agent_addr, &env.block.time, &params)?;
        agent_score.composite_score = compute_composite_score(&breakdown, &params);
        crate::state::AGENT_SCORES.save(deps.storage, &agent_addr, &agent_score)?;
    }

    Ok(Response::new()
        .add_attribute("action", "resolve_dispute")
        .add_attribute("dispute_id", &dispute_id)
        .add_attribute("resolution", &resolution))
}

fn handle_governance_override(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    agent: String,
    new_score: Decimal,
    _reason: String,
) -> StdResult<Response> {
    let params = GOV_PARAMS.load(deps.storage)?;
    if info.sender != params.admin {
        return Err(StdError::generic_err("Only admin can override scores"));
    }

    let agent_addr = deps.api.addr_validate(&agent)?;
    let mut score = crate::state::AGENT_SCORES.load(deps.storage, &agent_addr)?;
    score.composite_score = new_score;
    crate::state::AGENT_SCORES.save(deps.storage, &agent_addr, &score)?;

    Ok(Response::new()
        .add_attribute("action", "governance_override")
        .add_attribute("agent", &agent)
        .add_attribute("new_score", new_score.to_string()))
}

fn handle_update_params(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    params_msg: crate::msg::GovernanceParamsMsg,
) -> StdResult<Response> {
    GOV_PARAMS.update(deps.storage, |mut params| {
        if info.sender != params.admin {
            return Err(StdError::generic_err("Only admin can update params"));
        }
        if let Some(w) = params_msg.success_rate_weight { params.success_rate_weight = w; }
        if let Some(w) = params_msg.rating_weight { params.rating_weight = w; }
        if let Some(w) = params_msg.volume_weight { params.volume_weight = w; }
        if let Some(w) = params_msg.stake_weight { params.stake_weight = w; }
        if let Some(w) = params_msg.recency_weight { params.recency_weight = w; }
        if let Some(w) = params_msg.diversity_weight { params.diversity_weight = w; }
        if let Some(v) = params_msg.min_rating { params.min_rating = v; }
        if let Some(v) = params_msg.max_rating { params.max_rating = v; }
        if let Some(v) = params_msg.dispute_deposit { params.dispute_deposit = v; }
        if let Some(v) = params_msg.max_disputes_active { params.max_disputes_active = v; }
        Ok(params)
    })?;

    Ok(Response::new().add_attribute("action", "update_params"))
}

fn query_reputation(deps: Deps, agent: String) -> StdResult<ReputationResponse> {
    let agent_addr = deps.api.addr_validate(&agent)?;
    let score = crate::state::AGENT_SCORES
        .load(deps.storage, &agent_addr)
        .map_err(|_| StdError::generic_err("Agent not found"))?;

    let params = GOV_PARAMS.load(deps.storage)?;
    let breakdown = compute_score_breakdown(
        deps.storage, &score, &agent_addr, &cosmwasm_std::Timestamp::from_nanos(0), &params,
    )?;

    Ok(ReputationResponse {
        score: ReputationScoreResp {
            agent: score.agent.clone(),
            total_tasks: score.total_tasks,
            successful_tasks: score.successful_tasks,
            failed_tasks: score.failed_tasks,
            success_rate: score.success_rate(),
            avg_rating: score.avg_rating,
            total_earned: score.total_earned,
            stake_amount: score.stake_amount,
            last_activity: score.last_activity,
            composite_score: score.composite_score,
        },
        breakdown: ScoreBreakdown {
            success_rate_component: breakdown.success_rate_component,
            rating_component: breakdown.rating_component,
            volume_component: breakdown.volume_component,
            stake_component: breakdown.stake_component,
            recency_component: breakdown.recency_component,
            diversity_component: breakdown.diversity_component,
        },
    })
}

fn query_reputation_batch(deps: Deps, agents: Vec<String>) -> StdResult<Vec<ReputationResponse>> {
    agents.into_iter().map(|agent| query_reputation(deps, agent)).collect()
}

fn query_top_agents(
    deps: Deps,
    _start_after: Option<String>,
    limit: Option<u32>,
) -> StdResult<AgentListResponse> {
    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
    let params = GOV_PARAMS.load(deps.storage)?;

    let agents: Vec<ReputationScoreResp> = crate::state::AGENT_SCORES
        .range(deps.storage, None, None, cosmwasm_std::Order::Descending)
        .filter_map(|r| r.ok())
        .take(limit)
        .map(|(_, score)| {
            ReputationScoreResp {
                agent: score.agent,
                total_tasks: score.total_tasks,
                successful_tasks: score.successful_tasks,
                failed_tasks: score.failed_tasks,
                success_rate: score.success_rate(),
                avg_rating: score.avg_rating,
                total_earned: score.total_earned,
                stake_amount: score.stake_amount,
                last_activity: score.last_activity,
                composite_score: score.composite_score,
            }
        })
        .collect();

    Ok(AgentListResponse { agents })
}

fn query_agent_tasks(
    deps: Deps,
    agent: String,
    _start_after: Option<String>,
    limit: Option<u32>,
) -> StdResult<TaskListResponse> {
    let agent_addr = deps.api.addr_validate(&agent)?;
    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(50) as usize;

    let tasks: Vec<TaskRecordResp> = crate::state::AGENT_TASKS
        .prefix(&agent_addr)
        .range(deps.storage, None, None, cosmwasm_std::Order::Descending)
        .filter_map(|r| r.ok())
        .take(limit)
        .filter_map(|(task_id, _)| {
            crate::state::TASK_RECORDS.load(deps.storage, &task_id).ok().map(|t| TaskRecordResp {
                task_id: t.task_id,
                agent: t.agent,
                requester: t.requester,
                status: format!("{:?}", t.status),
                rating: t.rating,
                reward: t.reward,
                timestamp: t.timestamp,
            })
        })
        .collect();

    Ok(TaskListResponse { tasks })
}

fn query_agent_ratings(
    deps: Deps,
    agent: String,
    _start_after: Option<String>,
    limit: Option<u32>,
) -> StdResult<RatingListResponse> {
    let agent_addr = deps.api.addr_validate(&agent)?;
    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(50) as usize;

    let mut ratings = Vec::new();
    let mut count = 0;
    for result in crate::state::RATINGS.prefix(&agent_addr)
        .range(deps.storage, None, None, cosmwasm_std::Order::Descending)
    {
        if count >= limit { break; }
        if let Ok((_, rating_vec)) = result {
            for r in rating_vec {
                if count >= limit { break; }
                ratings.push(RatingResp {
                    rater: r.rater,
                    score: r.score,
                    comment_hash: r.comment_hash,
                    timestamp: r.timestamp,
                });
                count += 1;
            }
        }
    }

    Ok(RatingListResponse { ratings })
}

fn query_dispute(deps: Deps, dispute_id: String) -> StdResult<DisputeResponse> {
    let dispute = crate::state::DISPUTES
        .load(deps.storage, &dispute_id)
        .map_err(|_| StdError::generic_err("Dispute not found"))?;

    Ok(DisputeResponse {
        dispute: DisputeResp {
            dispute_id: dispute.dispute_id,
            task_id: dispute.task_id,
            initiator: dispute.initiator,
            respondent: dispute.respondent,
            dispute_type: format!("{:?}", dispute.dispute_type),
            status: format!("{:?}", dispute.status),
            evidence_hashes: dispute.evidence_hashes,
            created_at: dispute.created_at,
            resolved_at: dispute.resolved_at,
            resolution: dispute.resolution,
        },
    })
}

fn query_stake(deps: Deps, agent: String) -> StdResult<StakeResponse> {
    let agent_addr = deps.api.addr_validate(&agent)?;
    let stake = crate::state::STAKE_POSITIONS
        .load(deps.storage, &agent_addr)
        .map_err(|_| StdError::generic_err("No active stake"))?;

    Ok(StakeResponse {
        agent: stake.agent,
        amount: stake.amount,
        locked_until: stake.locked_until,
        staked_at: stake.staked_at,
        auto_renew: stake.auto_renew,
    })
}

fn query_params(deps: Deps) -> StdResult<ParamsResponse> {
    let params = GOV_PARAMS.load(deps.storage)?;
    Ok(ParamsResponse { params })
}

2.6 评分算法实现

// contracts/reputation-core/src/scoring.rs
use cosmwasm_std::{Addr, Decimal, StdResult, Storage, Timestamp};
use crate::state::{ReputationScore, GovernanceParams, COLLABORATORS, AGENT_SCORES, GOV_PARAMS};

const MAX_TASKS: u128 = 1_000_000;
const MAX_STAKE: u128 = 1_000_000_000_000;
const STALE_DAYS: u64 = 365;
const SECONDS_PER_DAY: u64 = 86_400;

#[derive(Clone, Debug, Default)]
pub struct ScoreBreakdown {
    pub success_rate_component: Decimal,
    pub rating_component: Decimal,
    pub volume_component: Decimal,
    pub stake_component: Decimal,
    pub recency_component: Decimal,
    pub diversity_component: Decimal,
}

pub fn compute_composite_score(
    breakdown: &ScoreBreakdown,
    params: &GovernanceParams,
) -> Decimal {
    let raw = breakdown.success_rate_component * params.success_rate_weight
        + breakdown.rating_component * params.rating_weight
        + breakdown.volume_component * params.volume_weight
        + breakdown.stake_component * params.stake_weight
        + breakdown.recency_component * params.recency_weight
        + breakdown.diversity_component * params.diversity_weight;
    raw * Decimal::from_ratio(100u128, 1u128)
}

pub fn compute_score_breakdown(
    storage: &dyn Storage,
    score: &ReputationScore,
    agent: &Addr,
    current_time: &Timestamp,
    params: &GovernanceParams,
) -> StdResult<ScoreBreakdown> {
    Ok(ScoreBreakdown {
        success_rate_component: compute_success_rate_component(score),
        rating_component: compute_rating_component(score, params),
        volume_component: compute_volume_component(score),
        stake_component: compute_stake_component(score),
        recency_component: compute_recency_component(score, current_time),
        diversity_component: compute_diversity_component(storage, agent)?,
    })
}

fn compute_success_rate_component(score: &ReputationScore) -> Decimal {
    if score.total_tasks.is_zero() {
        return Decimal::zero();
    }
    Decimal::from_ratio(score.successful_tasks, score.total_tasks)
}

fn compute_rating_component(score: &ReputationScore, params: &GovernanceParams) -> Decimal {
    let range = (params.max_rating - params.min_rating) as u128;
    if range == 0 {
        return Decimal::one();
    }
    let raw = score.avg_rating - Decimal::from_atomics(params.min_rating as u128, 0).unwrap();
    raw / Decimal::from_atomics(range, 0).unwrap()
}

fn compute_volume_component(score: &ReputationScore) -> Decimal {
    let tasks = score.total_tasks.u128();
    let log_tasks = (1u128 + tasks) as f64;
    let log_max = (1u128 + MAX_TASKS) as f64;
    let ratio = log_tasks.log2() / log_max.log2();
    Decimal::from_atomics((ratio * 1_000_000_000_000_000_000u128) as u128, 18).unwrap_or(Decimal::zero())
}

fn compute_stake_component(score: &ReputationScore) -> Decimal {
    let stake = score.stake_amount.u128();
    let ratio = (stake as f64) / (MAX_STAKE as f64);
    let boosted = (ratio * 5.0).min(1.0);
    Decimal::from_atomics((boosted * 1_000_000_000_000_000_000u128) as u128, 18).unwrap_or(Decimal::zero())
}

fn compute_recency_component(score: &ReputationScore, current_time: &Timestamp) -> Decimal {
    if score.last_activity.nanos() == 0 {
        return Decimal::zero();
    }
    let elapsed = current_time.minus(score.last_activity);
    let days_since = elapsed.nanos() / (SECONDS_PER_DAY * 1_000_000_000);
    if days_since >= STALE_DAYS {
        return Decimal::zero();
    }
    let decay = (STALE_DAYS - days_since) as f64 / STALE_DAYS as f64;
    Decimal::from_atomics((decay * 1_000_000_000_000_000_000u128) as u128, 18).unwrap_or(Decimal::zero())
}

fn compute_diversity_component(storage: &dyn Storage, agent: &Addr) -> StdResult<Decimal> {
    let min_collaborators = 10u128;
    let count = COLLABORATORS
        .prefix(agent)
        .range(storage, None, None, cosmwasm_std::Order::Ascending)
        .count() as u128;

    if count >= min_collaborators {
        return Ok(Decimal::one());
    }
    Ok(Decimal::from_ratio(count, min_collaborators))
}

pub fn recalculate_all_scores(
    storage: &mut dyn Storage,
    current_time: &Timestamp,
) -> StdResult<()> {
    let params = GOV_PARAMS.load(storage)?;
    let agents: Vec<Addr> = AGENT_SCORES
        .keys(storage, None, None, cosmwasm_std::Order::Ascending)
        .filter_map(|r| r.ok())
        .collect();

    for agent in agents {
        let mut score = AGENT_SCORES.load(storage, &agent)?;
        let breakdown = compute_score_breakdown(storage, &score, &agent, current_time, &params)?;
        score.composite_score = compute_composite_score(&breakdown, &params);
        AGENT_SCORES.save(storage, &agent, &score)?;
    }
    Ok(())
}

2.7 抵押逻辑实现

// contracts/reputation-core/src/stake.rs
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError, StdResult, Uint128};
use crate::state::{StakePosition, AGENT_SCORES, STAKE_POSITIONS, GOV_PARAMS};
use crate::scoring::{compute_score_breakdown, compute_composite_score};

const MIN_STAKE: Uint128 = Uint128::new(1_000_000);
const MAX_LOCK_SECS: u64 = 365 * 86_400;
const MIN_LOCK_SECS: u64 = 7 * 86_400;

pub fn handle_stake(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    amount: Uint128,
    lock_duration_secs: u64,
) -> StdResult<Response> {
    if amount < MIN_STAKE {
        return Err(StdError::generic_err(format!("Minimum stake is {}", MIN_STAKE)));
    }
    if lock_duration_secs < MIN_LOCK_SECS || lock_duration_secs > MAX_LOCK_SECS {
        return Err(StdError::generic_err(format!(
            "Lock duration must be between {} and {} seconds", MIN_LOCK_SECS, MAX_LOCK_SECS
        )));
    }

    let sent = info.funds.iter()
        .find(|c| c.denom == "msg")
        .map(|c| c.amount)
        .unwrap_or(Uint128::zero());

    if sent < amount {
        return Err(StdError::generic_err(format!(
            "Insufficient funds: sent {} msg, needed {}", sent, amount
        )));
    }

    let locked_until = env.block.time.plus_seconds(lock_duration_secs);
    let existing = STAKE_POSITIONS.may_load(deps.storage, &info.sender)?;

    let new_stake = if let Some(mut stake) = existing {
        if locked_until > stake.locked_until {
            stake.locked_until = locked_until;
        }
        stake.amount += amount;
        stake.auto_renew = lock_duration_secs > 180 * 86_400;
        stake
    } else {
        StakePosition {
            agent: info.sender.clone(),
            amount,
            locked_until,
            staked_at: env.block.time,
            auto_renew: lock_duration_secs > 180 * 86_400,
        }
    };

    STAKE_POSITIONS.save(deps.storage, &info.sender, &new_stake)?;

    let mut score = AGENT_SCORES
        .may_load(deps.storage, &info.sender)?
        .unwrap_or_else(|| crate::state::ReputationScore::new(info.sender.clone()));

    score.stake_amount = new_stake.amount;
    let params = GOV_PARAMS.load(deps.storage)?;
    let breakdown = compute_score_breakdown(deps.storage, &score, &info.sender, &env.block.time, &params)?;
    score.composite_score = compute_composite_score(&breakdown, &params);
    AGENT_SCORES.save(deps.storage, &info.sender, &score)?;

    Ok(Response::new()
        .add_attribute("action", "stake")
        .add_attribute("agent", info.sender.as_str())
        .add_attribute("amount", &amount.to_string())
        .add_attribute("locked_until", &locked_until.to_string())
        .add_attribute("auto_renew", &new_stake.auto_renew.to_string()))
}

2.8 争议处理实现

// contracts/reputation-core/src/dispute.rs
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdError, StdResult};
use crate::msg::DisputeTypeMsg;
use crate::state::{
    Dispute, DisputeStatus, DisputeType, TASK_RECORDS, DISPUTES, AGENT_DISPUTES, GOV_PARAMS,
};
use uuid::Uuid;

pub fn handle_open_dispute(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    task_id: String,
    respondent: String,
    dispute_type: DisputeTypeMsg,
    evidence_hashes: Vec<String>,
) -> StdResult<Response> {
    let params = GOV_PARAMS.load(deps.storage)?;
    let respondent_addr = deps.api.addr_validate(&respondent)?;

    let _task = TASK_RECORDS
        .load(deps.storage, &task_id)
        .map_err(|_| StdError::generic_err("Task not found"))?;

    let active_count = DISPUTES
        .range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
        .filter_map(|r| r.ok())
        .filter(|(_, d)| d.respondent == respondent_addr && d.status == DisputeStatus::Open)
        .count() as u32;

    if active_count >= params.max_disputes_active {
        return Err(StdError::generic_err(format!(
            "Agent already has maximum active disputes ({})", params.max_disputes_active
        )));
    }

    let dispute_id = Uuid::new_v4().to_string();
    let d_type = match dispute_type {
        DisputeTypeMsg::RatingDisagreement => DisputeType::RatingDisagreement,
        DisputeTypeMsg::FraudAllegation => DisputeType::FraudAllegation,
        DisputeTypeMsg::NonDelivery => DisputeType::NonDelivery,
        DisputeTypeMsg::Plagiarism => DisputeType::Plagiarism,
        DisputeTypeMsg::Other(s) => DisputeType::Other(s),
    };

    let dispute = Dispute {
        dispute_id: dispute_id.clone(),
        task_id: task_id.clone(),
        initiator: _task.requester,
        respondent: respondent_addr.clone(),
        dispute_type: d_type,
        status: DisputeStatus::Open,
        evidence_hashes,
        arbitrator: None,
        created_at: env.block.time,
        resolved_at: None,
        resolution: None,
    };

    DISPUTES.save(deps.storage, &dispute_id, &dispute)?;
    AGENT_DISPUTES.save(deps.storage, (&respondent_addr, &dispute_id), &true)?;

    Ok(Response::new()
        .add_attribute("action", "open_dispute")
        .add_attribute("dispute_id", &dispute_id)
        .add_attribute("task_id", &task_id)
        .add_attribute("respondent", &respondent))
}

2.9 Helper 函数

// contracts/reputation-core/src/helpers.rs
use cosmwasm_std::{Addr, Decimal, StdResult, Storage, Timestamp};
use crate::state::{AGENT_SCORES, GOV_PARAMS};
use crate::scoring::{compute_score_breakdown, compute_composite_score};

pub fn meets_min_reputation(storage: &dyn Storage, agent: &Addr, min_score: Decimal) -> StdResult<bool> {
    let score = AGENT_SCORES.may_load(storage, agent)?
        .unwrap_or_else(|| crate::state::ReputationScore::new(agent.clone()));
    Ok(score.composite_score >= min_score)
}

pub fn get_reputation_tier(score: Decimal) -> &'static str {
    let s = score.u128();
    match s {
        0..=20 => "Low",
        21..=50 => "Basic",
        51..=75 => "Good",
        76..=90 => "High",
        _ => "Elite",
    }
}

pub fn compute_stake_discount(score: Decimal) -> Decimal {
    let discount = score * Decimal::from_ratio(5u128, 1000u128);
    discount.min(Decimal::percent(50))
}

pub fn compute_priority_multiplier(score: Decimal) -> Decimal {
    Decimal::one() + score * Decimal::from_ratio(1u128, 100u128)
}

pub fn refresh_score(storage: &mut dyn Storage, agent: &Addr, current_time: &Timestamp) -> StdResult<()> {
    let params = GOV_PARAMS.load(storage)?;
    let mut score = AGENT_SCORES.load(storage, agent)
        .unwrap_or_else(|_| crate::state::ReputationScore::new(agent.clone()));
    let breakdown = compute_score_breakdown(storage, &score, agent, current_time, &params)?;
    score.composite_score = compute_composite_score(&breakdown, &params);
    AGENT_SCORES.save(storage, agent, &score)?;
    Ok(())
}

3. 评分算法

3.1 算法总览

评分算法的核心目标是为每个 Agent 生成一个归一化到 [0, 100] 的综合信誉分数。
算法分为六个维度,每个维度独立计算后加权求和。

3.2 权重配置

默认权重如下,可通过治理投票调整:

维度 默认权重 调整范围 说明
成功率 30% 20-40% 任务完成比例
平均评分 25% 15-35% 发布者评价转化
任务量 15% 10-25% 经验丰富度(对数缩放)
抵押加成 10% 5-20% 经济承诺信号
时间衰减 10% 5-15% 近期活动重要性
多样性 10% 5-15% 合作广度

3.3 Python 参考实现

#!/usr/bin/env python3
# reputation_scoring.py - MSG Chain AI Agent 信誉评分算法参考实现

import math
import json
from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
from datetime import datetime, timedelta


class TaskStatus(Enum):
    ASSIGNED = "assigned"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
    DISPUTED = "disputed"
    RESOLVED = "resolved"


@dataclass
class ReputationScore:
    agent: str
    total_tasks: int = 0
    successful_tasks: int = 0
    failed_tasks: int = 0
    avg_rating: float = 1.0
    total_earned: int = 0
    stake_amount: int = 0
    last_activity: Optional[datetime] = None
    composite_score: float = 0.0

    @property
    def success_rate(self) -> float:
        if self.total_tasks == 0:
            return 0.0
        return self.successful_tasks / self.total_tasks


@dataclass
class GovernanceParams:
    success_rate_weight: float = 0.30
    rating_weight: float = 0.25
    volume_weight: float = 0.15
    stake_weight: float = 0.10
    recency_weight: float = 0.10
    diversity_weight: float = 0.10
    min_rating: int = 1
    max_rating: int = 5
    max_tasks: int = 1_000_000
    max_stake: int = 1_000_000_000_000
    stale_days: int = 365
    min_collaborators: int = 10


@dataclass
class ScoreBreakdown:
    success_rate_component: float = 0.0
    rating_component: float = 0.0
    volume_component: float = 0.0
    stake_component: float = 0.0
    recency_component: float = 0.0
    diversity_component: float = 0.0


class ReputationScoringEngine:
    def __init__(self, params: Optional[GovernanceParams] = None):
        self.params = params or GovernanceParams()
        self.agents: dict[str, ReputationScore] = {}
        self.collaborators: dict[str, set[str]] = {}
        self.tasks: dict[str, dict] = {}
        self.ratings: dict[str, list[dict]] = {}
        self.disputes: dict[str, dict] = {}

    def compute_composite_score(self, breakdown: ScoreBreakdown) -> float:
        raw = (
            breakdown.success_rate_component * self.params.success_rate_weight
            + breakdown.rating_component * self.params.rating_weight
            + breakdown.volume_component * self.params.volume_weight
            + breakdown.stake_component * self.params.stake_weight
            + breakdown.recency_component * self.params.recency_weight
            + breakdown.diversity_component * self.params.diversity_weight
        )
        return raw * 100.0

    def compute_breakdown(
        self, score: ReputationScore, agent: str, current_time: Optional[datetime] = None,
    ) -> ScoreBreakdown:
        if current_time is None:
            current_time = datetime.utcnow()
        return ScoreBreakdown(
            success_rate_component=self._success_rate_component(score),
            rating_component=self._rating_component(score),
            volume_component=self._volume_component(score),
            stake_component=self._stake_component(score),
            recency_component=self._recency_component(score, current_time),
            diversity_component=self._diversity_component(agent),
        )

    def _success_rate_component(self, score: ReputationScore) -> float:
        if score.total_tasks == 0:
            return 0.0
        return score.successful_tasks / score.total_tasks

    def _rating_component(self, score: ReputationScore) -> float:
        range_val = self.params.max_rating - self.params.min_rating
        if range_val == 0:
            return 1.0
        return (score.avg_rating - self.params.min_rating) / range_val

    def _volume_component(self, score: ReputationScore) -> float:
        log_tasks = math.log2(1 + score.total_tasks)
        log_max = math.log2(1 + self.params.max_tasks)
        return log_tasks / log_max

    def _stake_component(self, score: ReputationScore) -> float:
        ratio = score.stake_amount / self.params.max_stake
        boosted = min(ratio * 5.0, 1.0)
        return boosted

    def _recency_component(self, score: ReputationScore, current_time: datetime) -> float:
        if score.last_activity is None:
            return 0.0
        days_since = (current_time - score.last_activity).days
        if days_since >= self.params.stale_days:
            return 0.0
        return (self.params.stale_days - days_since) / self.params.stale_days

    def _diversity_component(self, agent: str) -> float:
        collaborators = len(self.collaborators.get(agent, set()))
        if collaborators >= self.params.min_collaborators:
            return 1.0
        return collaborators / self.params.min_collaborators

    def compute_sybil_resistance(self, agent: str) -> float:
        score = self.agents.get(agent)
        if score is None:
            return 0.0
        factors = []
        stake_factor = min(score.stake_amount / (1000 * 10**6), 1.0)
        factors.append(stake_factor * 0.30)
        task_factor = min(score.total_tasks / 5, 1.0)
        factors.append(task_factor * 0.25)
        collab_count = len(self.collaborators.get(agent, set()))
        collab_factor = min(collab_count / 3, 1.0)
        factors.append(collab_factor * 0.25)
        agent_ratings = self.ratings.get(agent, [])
        if len(agent_ratings) >= 3:
            scores_list = [r["score"] for r in agent_ratings]
            mean_s = sum(scores_list) / len(scores_list)
            variance = sum((s - mean_s) ** 2 for s in scores_list) / len(scores_list)
            var_factor = 0.5 if variance < 0.1 else 1.0
            factors.append(var_factor * 0.20)
        else:
            factors.append(0.0)
        return sum(factors)

    def record_task(
        self, task_id: str, agent: str, requester: str, status: TaskStatus,
        reward: int, evidence_hash: Optional[str] = None,
    ) -> ReputationScore:
        if agent not in self.agents:
            self.agents[agent] = ReputationScore(agent=agent)
            self.collaborators[agent] = set()
        score = self.agents[agent]
        score.total_tasks += 1
        score.total_earned += reward
        score.last_activity = datetime.utcnow()
        if status == TaskStatus.COMPLETED:
            score.successful_tasks += 1
        elif status == TaskStatus.FAILED:
            score.failed_tasks += 1
        self.collaborators[agent].add(requester)
        self.tasks[task_id] = {
            "agent": agent, "requester": requester, "status": status.value,
            "reward": reward, "evidence_hash": evidence_hash,
            "timestamp": datetime.utcnow().isoformat(),
        }
        breakdown = self.compute_breakdown(score, agent)
        score.composite_score = self.compute_composite_score(breakdown)
        return score

    def submit_rating(self, agent: str, task_id: str, score_val: int, comment_hash: str, rater: str) -> ReputationScore:
        if score_val < self.params.min_rating or score_val > self.params.max_rating:
            raise ValueError(f"Rating must be between {self.params.min_rating} and {self.params.max_rating}")
        if agent not in self.agents:
            raise ValueError(f"Agent {agent} not found")
        if agent not in self.ratings:
            self.ratings[agent] = []
        self.ratings[agent].append({
            "task_id": task_id, "rater": rater, "score": score_val,
            "comment_hash": comment_hash, "timestamp": datetime.utcnow().isoformat(),
        })
        if task_id in self.tasks:
            self.tasks[task_id]["rating"] = score_val
        agent_score = self.agents[agent]
        all_scores = [r["score"] for r in self.ratings[agent]]
        agent_score.avg_rating = sum(all_scores) / len(all_scores)
        breakdown = self.compute_breakdown(agent_score, agent)
        agent_score.composite_score = self.compute_composite_score(breakdown)
        return agent_score

    def get_score(self, agent: str) -> Optional[ReputationScore]:
        return self.agents.get(agent)

    def get_top_agents(self, n: int = 10) -> list[ReputationScore]:
        return sorted(self.agents.values(), key=lambda x: x.composite_score, reverse=True)[:n]

    def get_breakdown(self, agent: str) -> dict:
        score = self.agents.get(agent)
        if score is None:
            return {}
        breakdown = self.compute_breakdown(score, agent)
        return {
            "composite_score": round(score.composite_score, 2),
            "components": {
                "success_rate": {"value": round(breakdown.success_rate_component, 4), "weight": self.params.success_rate_weight, "contribution": round(breakdown.success_rate_component * self.params.success_rate_weight, 4)},
                "rating": {"value": round(breakdown.rating_component, 4), "weight": self.params.rating_weight, "contribution": round(breakdown.rating_component * self.params.rating_weight, 4)},
                "volume": {"value": round(breakdown.volume_component, 4), "weight": self.params.volume_weight, "contribution": round(breakdown.volume_component * self.params.volume_weight, 4)},
                "stake": {"value": round(breakdown.stake_component, 4), "weight": self.params.stake_weight, "contribution": round(breakdown.stake_component * self.params.stake_weight, 4)},
                "recency": {"value": round(breakdown.recency_component, 4), "weight": self.params.recency_weight, "contribution": round(breakdown.recency_component * self.params.recency_weight, 4)},
                "diversity": {"value": round(breakdown.diversity_component, 4), "weight": self.params.diversity_weight, "contribution": round(breakdown.diversity_component * self.params.diversity_weight, 4)},
            },
            "raw_metrics": {
                "total_tasks": score.total_tasks,
                "success_rate": round(score.success_rate, 4),
                "avg_rating": round(score.avg_rating, 2),
                "stake_amount": score.stake_amount,
                "collaborators": len(self.collaborators.get(agent, set())),
            },
        }

    def recalculate_all(self, current_time: Optional[datetime] = None):
        if current_time is None:
            current_time = datetime.utcnow()
        for agent, score in self.agents.items():
            breakdown = self.compute_breakdown(score, agent, current_time)
            score.composite_score = self.compute_composite_score(breakdown)

    def simulate(self):
        import random
        agents = [f"msg1agent{i}" for i in range(1, 21)]
        requesters = [f"msg1requester{i}" for i in range(1, 11)]
        for agent in agents:
            self.agents[agent] = ReputationScore(agent=agent)
            self.collaborators[agent] = set()
            num_tasks = random.randint(5, 50)
            for t in range(num_tasks):
                task_id = f"task-{agent}-{t}"
                requester = random.choice(requesters)
                status = TaskStatus.COMPLETED if random.random() < 0.85 else TaskStatus.FAILED
                reward = random.randint(100, 10000)
                self.record_task(task_id, agent, requester, status, reward)
                if status == TaskStatus.COMPLETED and random.random() < 0.7:
                    rating_score = random.choices([1, 2, 3, 4, 5], weights=[1, 2, 10, 30, 50])[0]
                    self.submit_rating(agent, task_id, rating_score, f"ipfs://QmComment{random.randint(1, 1000)}", requester)
            if random.random() < 0.4:
                stake = random.randint(1000, 50000) * 10**6
                self.agents[agent].stake_amount = stake
            days_ago = random.randint(0, 200)
            self.agents[agent].last_activity = datetime.utcnow() - timedelta(days=days_ago)
        self.recalculate_all()


def print_leaderboard(engine: ReputationScoringEngine, top_n: int = 10):
    print(f"\n{'='*110}")
    print(f"{'Rank':<6} {'Agent':<30} {'Score':<8} {'Tasks':<8} {'Success%':<10} {'Rating':<8} {'Stake':<10} {'SybilRes':<10}")
    print(f"{'='*110}")
    for i, score in enumerate(engine.get_top_agents(top_n), 1):
        sybil = engine.compute_sybil_resistance(score.agent)
        print(f"{i:<6} {score.agent:<30} {score.composite_score:<8.2f} {score.total_tasks:<8} {score.success_rate:<10.2%} {score.avg_rating:<8.2f} {score.stake_amount // 10**6:<10} {sybil:<10.3f}")


def scenario_analysis():
    scenarios = [
        ("新 Agent,无历史", ReputationScore(agent="new", total_tasks=0, avg_rating=1.0, last_activity=datetime.utcnow())),
        ("普通 Agent,50 任务 90% 成功率", ReputationScore(agent="normal", total_tasks=50, successful_tasks=45, avg_rating=4.2, total_earned=50000, last_activity=datetime.utcnow())),
        ("优秀 Agent,500 任务 98%", ReputationScore(agent="elite", total_tasks=500, successful_tasks=490, avg_rating=4.8, total_earned=500000, stake_amount=10000 * 10**6, last_activity=datetime.utcnow())),
        ("衰退 Agent,300 天前活动", ReputationScore(agent="stale", total_tasks=200, successful_tasks=180, avg_rating=4.0, total_earned=200000, last_activity=datetime.utcnow() - timedelta(days=300))),
        ("高抵押低表现", ReputationScore(agent="stake-heavy", total_tasks=5, successful_tasks=2, avg_rating=2.0, stake_amount=50000 * 10**6, last_activity=datetime.utcnow())),
        ("Sybil 怀疑者", ReputationScore(agent="sybil", total_tasks=100, successful_tasks=100, avg_rating=5.0, last_activity=datetime.utcnow())),
    ]
    print(f"\n{'Scenario':<35} {'Score':<10} {'Breakdown':<60}")
    print("=" * 105)
    engine = ReputationScoringEngine()
    for name, score in scenarios:
        engine.agents[score.agent] = score
        if score.agent == "sybil":
            engine.collaborators[score.agent] = {"single_requester"}
        else:
            engine.collaborators[score.agent] = {f"requester{i}" for i in range(1 if score.agent == "new" else 8)}
        breakdown = engine.compute_breakdown(score, score.agent)
        composite = engine.compute_composite_score(breakdown)
        b = breakdown
        b_str = f"S:{b.success_rate_component:.2f} R:{b.rating_component:.2f} V:{b.volume_component:.2f} K:{b.stake_component:.2f} T:{b.recency_component:.2f} D:{b.diversity_component:.2f}"
        print(f"{name:<35} {composite:<10.2f} {b_str:<60}")


if __name__ == "__main__":
    print("=" * 100)
    print("  MSG Chain AI Agent 信誉评分系统 - 参考实现")
    print("=" * 100)
    engine = ReputationScoringEngine()
    print("\n[1/3] 生成模拟数据...")
    engine.simulate()
    print(f"  已生成 {len(engine.agents)} 个 Agent 的信誉数据")
    print("\n[2/3] 信誉排行榜:")
    print_leaderboard(engine, 10)
    print("\n[3/3] 场景分析:")
    scenario_analysis()
    top = engine.get_top_agents(1)[0]
    print(f"\n最佳 Agent 详细分解 ({top.agent}):")
    print(json.dumps(engine.get_breakdown(top.agent), indent=2, ensure_ascii=False))
    print("\n评分算法示例完成")

3.4 评分计算示例

场景:普通 Agent 评分分解

输入数据:

计算过程:

  1. success_rate_component = 45/50 = 0.9000
  2. rating_component = (4.2 - 1) / 4 = 0.8000
  3. volume_component = log2(51) / log2(1,000,001) ≈ 0.5017
  4. stake_component = min(10,000,000,000 / 1,000,000,000,000 * 5, 1) = min(0.05, 1) = 0.0500
  5. recency_component = (365-30)/365 = 0.9178
  6. diversity_component = 8/10 = 0.8000

加权计算:
raw = 0.9000x0.30 + 0.8000x0.25 + 0.5017x0.15 + 0.0500x0.10 + 0.9178x0.10 + 0.8000x0.10
= 0.2700 + 0.2000 + 0.0753 + 0.0050 + 0.0918 + 0.0800
= 0.7221

composite_score = 0.7221 x 100 = 72.21 -> Good 等级


4. 信誉使用场景

4.1 Agent 发现与排序

// packages/client/src/reputation/discovery.ts
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";

interface ReputationScore {
  agent: string;
  total_tasks: string;
  successful_tasks: string;
  failed_tasks: string;
  success_rate: string;
  avg_rating: string;
  total_earned: string;
  stake_amount: string;
  last_activity: string;
  composite_score: string;
}

interface ScoreBreakdown {
  success_rate_component: string;
  rating_component: string;
  volume_component: string;
  stake_component: string;
  recency_component: string;
  diversity_component: string;
}

interface ReputationResponse {
  score: ReputationScore;
  breakdown: ScoreBreakdown;
}

interface AgentListResponse {
  agents: ReputationScore[];
}

export class ReputationClient {
  private client: CosmWasmClient;
  private contractAddress: string;

  constructor(client: CosmWasmClient, contractAddress: string) {
    this.client = client;
    this.contractAddress = contractAddress;
  }

  async getReputation(agent: string): Promise<ReputationResponse> {
    return this.client.queryContractSmart(this.contractAddress, {
      get_reputation: { agent },
    });
  }

  async getReputationBatch(agents: string[]): Promise<ReputationResponse[]> {
    return this.client.queryContractSmart(this.contractAddress, {
      get_reputation_batch: { agents },
    });
  }

  async getTopAgents(startAfter?: string, limit: number = 20): Promise<AgentListResponse> {
    return this.client.queryContractSmart(this.contractAddress, {
      list_top_agents: { start_after: startAfter, limit },
    });
  }

  async filterByTier(minScore: number, limit: number = 50): Promise<ReputationScore[]> {
    const all = await this.getTopAgents(undefined, limit);
    return all.agents.filter((a) => parseFloat(a.composite_score) >= minScore);
  }

  static getTier(score: number): string {
    if (score <= 20) return "Low";
    if (score <= 50) return "Basic";
    if (score <= 75) return "Good";
    if (score <= 90) return "High";
    return "Elite";
  }

  static getTierColor(tier: string): string {
    const colors: Record<string, string> = {
      Low: "#ef4444", Basic: "#f97316", Good: "#22c55e",
      High: "#06b6d4", Elite: "#8b5cf6",
    };
    return colors[tier] ?? "#6b7280";
  }

  static getMinScoreForComplexity(complexity: "low" | "medium" | "high" | "critical"): number {
    const thresholds: Record<string, number> = { low: 0, medium: 30, high: 55, critical: 75 };
    return thresholds[complexity];
  }
}

4.2 任务匹配与信誉过滤器

// packages/client/src/reputation/task-matching.ts
import { ReputationClient } from "./discovery";

interface TaskRequirements {
  taskId: string;
  minReputation: number;
  minSuccessRate: number;
  minCompletedTasks: number;
}

interface MatchResult {
  taskId: string;
  agent: string;
  score: number;
  meetsRequirements: boolean;
  matchReason: string;
}

export class TaskMatchingEngine {
  private reputationClient: ReputationClient;

  constructor(reputationClient: ReputationClient) {
    this.reputationClient = reputationClient;
  }

  async matchAgents(requirements: TaskRequirements, topN: number = 10): Promise<MatchResult[]> {
    const topAgents = await this.reputationClient.getTopAgents(undefined, 100);
    const results: MatchResult[] = [];

    for (const agent of topAgents.agents) {
      const score = parseFloat(agent.composite_score);
      const successRate = parseFloat(agent.success_rate);
      const totalTasks = parseInt(agent.total_tasks);
      const issues: string[] = [];

      if (score < requirements.minReputation) {
        issues.push(`Score ${score.toFixed(1)} < min ${requirements.minReputation}`);
      }
      if (successRate < requirements.minSuccessRate) {
        issues.push(`Rate ${(successRate * 100).toFixed(1)}% < ${(requirements.minSuccessRate * 100).toFixed(1)}%`);
      }
      if (totalTasks < requirements.minCompletedTasks) {
        issues.push(`Tasks ${totalTasks} < min ${requirements.minCompletedTasks}`);
      }

      results.push({
        taskId: requirements.taskId,
        agent: agent.agent,
        score,
        meetsRequirements: issues.length === 0,
        matchReason: issues.length === 0 ? "OK" : issues.join("; "),
      });
    }

    results.sort((a, b) => {
      if (a.meetsRequirements !== b.meetsRequirements) return a.meetsRequirements ? -1 : 1;
      return b.score - a.score;
    });
    return results.slice(0, topN);
  }
}

4.3 基于信誉的抵押减免

// packages/client/src/reputation/collateral.ts
import { ReputationClient } from "./discovery";

export class CollateralManager {
  private reputationClient: ReputationClient;
  private readonly BASE_COLLATERAL_RATIO = 0.20;
  private readonly MAX_DISCOUNT = 0.50;
  private readonly DISCOUNT_PER_POINT = 0.005;

  constructor(reputationClient: ReputationClient) {
    this.reputationClient = reputationClient;
  }

  async computeRequiredCollateral(agent: string, taskValue: string): Promise<{
    baseCollateral: string; discountedCollateral: string;
    discountPercent: number; tier: string;
  }> {
    const rep = await this.reputationClient.getReputation(agent);
    const score = parseFloat(rep.score.composite_score);
    const value = parseFloat(taskValue);
    const discount = Math.min(score * this.DISCOUNT_PER_POINT, this.MAX_DISCOUNT);
    const discountPercent = discount * 100;
    const baseCollateral = value * this.BASE_COLLATERAL_RATIO;
    const discountedCollateral = baseCollateral * (1 - discount);
    return {
      baseCollateral: baseCollateral.toFixed(0),
      discountedCollateral: discountedCollateral.toFixed(0),
      discountPercent,
      tier: ReputationClient.getTier(score),
    };
  }

  async computeCreditLimit(agent: string): Promise<{ creditLimit: string; score: number }> {
    const rep = await this.reputationClient.getReputation(agent);
    const score = parseFloat(rep.score.composite_score);
    const stakeAmount = parseFloat(rep.score.stake_amount);
    const creditLimit = (stakeAmount * (score / 100) * 10).toFixed(0);
    return { creditLimit, score };
  }
}

4.4 高信誉 Agent 优先队列

// packages/client/src/reputation/priority-queue.ts
import { ReputationClient } from "./discovery";

interface AgentPriority {
  agent: string;
  priorityScore: number;
  tier: string;
  maxConcurrent: number;
}

export class PriorityQueue {
  private reputationClient: ReputationClient;
  private readonly BASE_PRIORITY = 1.0;
  private readonly PRIORITY_PER_POINT = 0.01;
  private readonly BASE_MAX_CONCURRENT = 3;
  private readonly EXTRA_SLOTS: Record<string, number> = {
    Low: 0, Basic: 1, Good: 2, High: 4, Elite: 8,
  };

  constructor(reputationClient: ReputationClient) {
    this.reputationClient = reputationClient;
  }

  async getAgentPriority(agent: string): Promise<AgentPriority> {
    const rep = await this.reputationClient.getReputation(agent);
    const score = parseFloat(rep.score.composite_score);
    const tier = ReputationClient.getTier(score);
    return {
      agent,
      priorityScore: this.BASE_PRIORITY + score * this.PRIORITY_PER_POINT,
      tier,
      maxConcurrent: this.BASE_MAX_CONCURRENT + (this.EXTRA_SLOTS[tier] ?? 0),
    };
  }

  async canAcceptTask(agent: string): Promise<{ canAccept: boolean; currentLoad: number; maxLoad: number; reason: string }> {
    const p = await this.getAgentPriority(agent);
    const currentTasks = 0;
    return {
      canAccept: currentTasks < p.maxConcurrent,
      currentLoad: currentTasks,
      maxLoad: p.maxConcurrent,
      reason: currentTasks < p.maxConcurrent ? "Available" : `At capacity (${currentTasks}/${p.maxConcurrent})`,
    };
  }
}

4.5 完整集成示例

// packages/client/examples/reputation-flow.ts
import { CosmWasmClient, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { ReputationClient } from "../src/reputation/discovery";
import { TaskMatchingEngine } from "../src/reputation/task-matching";
import { CollateralManager } from "../src/reputation/collateral";
import { PriorityQueue } from "../src/reputation/priority-queue";

async function main() {
  const rpcUrl = "https://rpc.msg-chain-1.msg.network";
  const client = await CosmWasmClient.connect(rpcUrl);
  const wallet = await DirectSecp256k1HdWallet.fromMnemonic("your mnemonic...", { prefix: "msg" });
  const signer = await SigningCosmWasmClient.connectWithSigner(rpcUrl, wallet);
  const reputationContract = "msg1reputation...";

  const repClient = new ReputationClient(client, reputationContract);
  const matcher = new TaskMatchingEngine(repClient);
  const collateralMgr = new CollateralManager(repClient);
  const priorityQueue = new PriorityQueue(repClient);

  console.log("=== Top Agents ===");
  const topAgents = await repClient.getTopAgents(undefined, 10);
  for (const agent of topAgents.agents) {
    const tier = ReputationClient.getTier(parseFloat(agent.composite_score));
    console.log(`  ${agent.agent}: ${agent.composite_score} (${tier}) - ${agent.successful_tasks}/${agent.total_tasks} tasks`);
  }

  console.log("\n=== Task Matching ===");
  const matches = await matcher.matchAgents({ taskId: "task-001", minReputation: 60, minSuccessRate: 0.9, minCompletedTasks: 20 });
  for (const m of matches.slice(0, 5)) {
    console.log(`  ${m.agent}: score=${m.score.toFixed(1)}, meets=${m.meetsRequirements}`);
  }

  console.log("\n=== Collateral Calculation ===");
  for (const agent of topAgents.agents.slice(0, 3)) {
    const col = await collateralMgr.computeRequiredCollateral(agent.agent, "1000000000");
    console.log(`  ${agent.agent}: discounted=${col.discountedCollateral}, discount=${col.discountPercent.toFixed(1)}%`);
  }

  console.log("\n=== Submit Task Result ===");
  const msg = {
    record_task_result: {
      task_id: "task-live-001",
      agent: topAgents.agents[0].agent,
      requester: "msg1requester...",
      status: "completed",
      reward: "500000000",
      evidence_hash: "ipfs://QmEvidence",
    },
  };
  const result = await signer.execute("msg1sender...", reputationContract, [msg], "auto", "Record task");
  console.log(`  Tx hash: ${result.transactionHash}`);
  console.log("\nReputation flow complete!");
}
main().catch(console.error);

5. 争议与申诉

5.1 争议系统设计

争议流程:

  1. 发布者提交争议(链上)
  2. 双方提交证据(链上哈希)
  3. 仲裁员审查(链下 + 链上裁决)
  4. 执行裁决(评分调整/罚没)

争议类型:

类型 说明 典型证据
RatingDisagreement 对评分有异议 交付截图、日志
FraudAllegation 指控欺诈 交易记录、通信记录
NonDelivery 未交付成果 空的任务输出哈希
Plagiarism 抄袭 原创性报告
Other 其他 自定义

5.2 争议扩展逻辑

// contracts/reputation-core/src/dispute_ext.rs
use cosmwasm_std::{Addr, Decimal, DepsMut, Env, MessageInfo, Response, StdError, StdResult};
use crate::state::{DisputeStatus, DISPUTES, AGENT_DISPUTES, GOV_PARAMS, AGENT_SCORES};
use crate::scoring::{compute_score_breakdown, compute_composite_score};

pub struct DisputeSummary {
    pub total: u32,
    pub open: u32,
    pub resolved_in_favor: u32,
    pub dismissed: u32,
    pub dispute_ratio: Decimal,
}

pub fn get_agent_dispute_summary(deps: &DepsMut, agent: &Addr) -> StdResult<DisputeSummary> {
    let mut total = 0u32;
    let mut resolved_in_favor = 0u32;
    let mut dismissed = 0u32;
    let mut open = 0u32;

    let dispute_ids: Vec<String> = AGENT_DISPUTES
        .prefix(agent)
        .keys(deps.storage, None, None, cosmwasm_std::Order::Ascending)
        .filter_map(|r| r.ok())
        .collect();

    for id in dispute_ids {
        if let Ok(dispute) = DISPUTES.load(deps.storage, &id) {
            total += 1;
            match dispute.status {
                DisputeStatus::Open | DisputeStatus::UnderReview => open += 1,
                DisputeStatus::Resolved => resolved_in_favor += 1,
                DisputeStatus::Dismissed => dismissed += 1,
            }
        }
    }

    Ok(DisputeSummary {
        total, open, resolved_in_favor, dismissed,
        dispute_ratio: if total > 0 {
            Decimal::from_ratio(resolved_in_favor + dismissed, total)
        } else {
            Decimal::zero()
        },
    })
}

pub fn penalize_false_dispute(deps: DepsMut, env: Env, dispute_id: String, penalty_amount: Decimal) -> StdResult<Response> {
    let params = GOV_PARAMS.load(deps.storage)?;
    let dispute = DISPUTES.load(deps.storage, &dispute_id)?;
    if dispute.status != DisputeStatus::Resolved {
        return Err(StdError::generic_err("Dispute not yet resolved"));
    }
    if let Ok(mut initiator_score) = AGENT_SCORES.may_load(deps.storage, &dispute.initiator)? {
        initiator_score.composite_score = initiator_score.composite_score.saturating_sub(penalty_amount);
        AGENT_SCORES.save(deps.storage, &dispute.initiator, &initiator_score)?;
    }
    if let Ok(mut respondent_score) = AGENT_SCORES.may_load(deps.storage, &dispute.respondent)? {
        let compensation = Decimal::from_ratio(1u128, 100u128);
        respondent_score.composite_score = respondent_score.composite_score + compensation;
        let breakdown = compute_score_breakdown(deps.storage, &respondent_score, &dispute.respondent, &env.block.time, &params)?;
        respondent_score.composite_score = compute_composite_score(&breakdown, &params);
        AGENT_SCORES.save(deps.storage, &dispute.respondent, &respondent_score)?;
    }
    Ok(Response::new()
        .add_attribute("action", "penalize_false_dispute")
        .add_attribute("dispute_id", &dispute_id)
        .add_attribute("penalty", penalty_amount.to_string()))
}

5.3 申诉流程 TypeScript

// packages/client/src/reputation/appeal.ts
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";

enum AppealGrounds {
  PROCEDURAL_ERROR = "procedural_error",
  NEW_EVIDENCE = "new_evidence",
  ARBITRATOR_BIAS = "arbitrator_bias",
  SCORE_MISCALCULATION = "score_miscalculation",
}

export class AppealManager {
  private signer: SigningCosmWasmClient;
  private contractAddress: string;
  private adminAddress: string;

  constructor(signer: SigningCosmWasmClient, contractAddress: string, adminAddress: string) {
    this.signer = signer;
    this.contractAddress = contractAddress;
    this.adminAddress = adminAddress;
  }

  async submitAppeal(sender: string, disputeId: string, grounds: AppealGrounds, evidenceCIDs: string[], description: string): Promise<string> {
    const appealId = `appeal-${disputeId}-${Date.now()}`;
    const msg = {
      submit_appeal: {
        appeal_id: appealId,
        dispute_id: disputeId,
        grounds: grounds.toString(),
        evidence_cids: evidenceCIDs,
        appeal_data_cid: `ipfs://QmAppeal${Date.now()}`,
      },
    };
    const result = await this.signer.execute(sender, this.contractAddress, [msg], "auto", `Appeal for ${disputeId}`);
    console.log(`Appeal submitted: ${appealId}, tx: ${result.transactionHash}`);
    return appealId;
  }

  async resolveAppeal(appealId: string, upheld: boolean, resolutionNote: string, adjustedScore?: number): Promise<void> {
    const msg = {
      resolve_appeal: { appeal_id: appealId, upheld, resolution: resolutionNote, adjusted_score: adjustedScore },
    };
    await this.signer.execute(this.adminAddress, this.contractAddress, [msg], "auto", `Resolve ${appealId}`);
  }

  async getOpenDisputes(): Promise<Array<{ disputeId: string; taskId: string; status: string; createdAt: Date }>> {
    const result: Array<{ dispute_id: string; task_id: string; status: string; created_at: string }> =
      await this.signer.queryContractSmart(this.contractAddress, { list_open_disputes: { limit: 50 } });
    return result.map((d) => ({ disputeId: d.dispute_id, taskId: d.task_id, status: d.status, createdAt: new Date(d.created_at) }));
  }

  async generateDisputeReport(agentAddress: string): Promise<{ totalDisputes: number; upheldRate: number; openDisputes: number; riskLevel: "low" | "medium" | "high" }> {
    const summary: { total: number; open: number; resolved_in_favor: number; dismissed: number } =
      await this.signer.queryContractSmart(this.contractAddress, { get_agent_dispute_summary: { agent: agentAddress } });
    const totalResolved = summary.resolved_in_favor + summary.dismissed;
    const upheldRate = totalResolved > 0 ? (summary.resolved_in_favor / totalResolved) * 100 : 0;
    let riskLevel: "low" | "medium" | "high";
    if (summary.total === 0) riskLevel = "low";
    else if (upheldRate > 50 || summary.open > 3) riskLevel = "high";
    else if (upheldRate > 20 || summary.open > 1) riskLevel = "medium";
    else riskLevel = "low";
    return { totalDisputes: summary.total, upheldRate, openDisputes: summary.open, riskLevel };
  }
}

5.4 争议裁决评分调整

# scripts/dispute_resolution.py
from enum import Enum
from dataclasses import dataclass

class ResolutionOutcome(Enum):
    FAVOR_AGENT = "favor_agent"
    FAVOR_REQUESTER = "favor_requester"
    PARTIAL = "partial"
    DISMISSED = "dismissed"

@dataclass
class DisputeAdjustment:
    agent_score_delta: float
    requester_weight_delta: float
    compensation: float
    reason: str

class DisputeAdjustmentEngine:
    FALSE_DISPUTE_PENALTY = -5.0
    FALSE_DISPUTE_COMPENSATION = 200 * 10**6
    FALSE_DISPUTE_WEIGHT_REDUCTION = 0.5
    PROVEN_BREACH_PENALTY = -15.0
    PROVEN_BREACH_STAKE_SLASH = 0.10

    def resolve(self, outcome: ResolutionOutcome, agent_score: float, requester_rating_weight: float, agent_stake: int) -> DisputeAdjustment:
        if outcome == ResolutionOutcome.FAVOR_AGENT:
            return DisputeAdjustment(
                agent_score_delta=1.0,
                requester_weight_delta=-self.FALSE_DISPUTE_WEIGHT_REDUCTION,
                compensation=self.FALSE_DISPUTE_COMPENSATION,
                reason="Dispute ruled in agent's favor. Requester penalized.",
            )
        elif outcome == ResolutionOutcome.FAVOR_REQUESTER:
            slash_amount = int(agent_stake * self.PROVEN_BREACH_STAKE_SLASH)
            return DisputeAdjustment(
                agent_score_delta=self.PROVEN_BREACH_PENALTY,
                requester_weight_delta=0.0,
                compensation=slash_amount,
                reason=f"Agent in breach. Score -{abs(self.PROVEN_BREACH_PENALTY)} pts, stake slashed {slash_amount} umsg.",
            )
        elif outcome == ResolutionOutcome.PARTIAL:
            return DisputeAdjustment(-2.0, -0.1, 50 * 10**6, "Partial fault: both parties share responsibility.")
        else:
            return DisputeAdjustment(0.0, 0.0, 0, "Dispute dismissed, no adjustment.")

    def compute_penalty_severity(self, agent_score: float, breach_count: int, stake_amount: int) -> float:
        base = -15.0
        multiplier = [1.0, 2.0, 4.0, 8.0][min(breach_count, 4) - 1] if breach_count > 0 else 1.0
        return base * multiplier


if __name__ == "__main__":
    engine = DisputeAdjustmentEngine()
    print("虚假争议:", engine.resolve(ResolutionOutcome.FAVOR_AGENT, 72.0, 1.0, 10000 * 10**6))
    print("Agent违约:", engine.resolve(ResolutionOutcome.FAVOR_REQUESTER, 80.0, 1.0, 50000 * 10**6))
    print("累犯惩罚:", [engine.compute_penalty_severity(75.0, i, 10000 * 10**6) for i in [1, 2, 3]])

6. 前端

6.1 信誉看板 React 组件

// packages/frontend/src/components/ReputationDashboard.tsx
import React, { useEffect, useState } from "react";
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";

interface ReputationScore {
  agent: string;
  total_tasks: string;
  successful_tasks: string;
  failed_tasks: string;
  success_rate: string;
  avg_rating: string;
  total_earned: string;
  stake_amount: string;
  last_activity: string;
  composite_score: string;
}

interface ScoreBreakdown {
  success_rate_component: string;
  rating_component: string;
  volume_component: string;
  stake_component: string;
  recency_component: string;
  diversity_component: string;
}

function getTier(score: number): string {
  if (score <= 20) return "Low";
  if (score <= 50) return "Basic";
  if (score <= 75) return "Good";
  if (score <= 90) return "High";
  return "Elite";
}

function getTierColor(tier: string): string {
  const colors: Record<string, string> = {
    Low: "#ef4444", Basic: "#f97316", Good: "#22c55e",
    High: "#06b6d4", Elite: "#8b5cf6",
  };
  return colors[tier] ?? "#6b7280";
}

function formatAddress(addr: string): string {
  if (addr.length <= 15) return addr;
  return `${addr.slice(0, 8)}...${addr.slice(-6)}`;
}

interface ReputationDashboardProps {
  rpcEndpoint: string;
  contractAddress: string;
  onAgentSelect?: (agent: string) => void;
}

export const ReputationDashboard: React.FC<ReputationDashboardProps> = ({
  rpcEndpoint, contractAddress, onAgentSelect,
}) => {
  const [agents, setAgents] = useState<ReputationScore[]>([]);
  const [loading, setLoading] = useState(true);
  const [search, setSearch] = useState("");

  useEffect(() => {
    const fetchData = async () => {
      try {
        const client = await CosmWasmClient.connect(rpcEndpoint);
        const result: { agents: ReputationScore[] } = await client.queryContractSmart(
          contractAddress, { list_top_agents: { limit: 50 } }
        );
        setAgents(result.agents);
      } catch (err) {
        console.error("Failed to fetch agents:", err);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, [rpcEndpoint, contractAddress]);

  const filtered = agents.filter((a) =>
    a.agent.toLowerCase().includes(search.toLowerCase())
  );

  if (loading) return <div className="p-8 text-center text-gray-400">Loading reputation data...</div>;

  return (
    <div className="p-6 max-w-7xl mx-auto">
      <div className="flex justify-between items-center mb-6">
        <h1 className="text-2xl font-bold text-gray-100">Agent 信誉看板</h1>
        <input
          type="text"
          placeholder="搜索 Agent 地址..."
          className="px-4 py-2 rounded bg-gray-800 border border-gray-700 text-gray-200 w-72"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
      </div>

      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
        {filtered.map((agent, idx) => {
          const score = parseFloat(agent.composite_score);
          const tier = getTier(score);
          const color = getTierColor(tier);
          return (
            <div
              key={agent.agent}
              className="bg-gray-900 rounded-xl p-5 border border-gray-800 hover:border-gray-600 cursor-pointer transition-all"
              onClick={() => onAgentSelect?.(agent.agent)}
            >
              <div className="flex items-center justify-between mb-3">
                <span className="font-mono text-sm text-gray-400">
                  #{idx + 1} {formatAddress(agent.agent)}
                </span>
                <span
                  className="px-2 py-0.5 rounded text-xs font-semibold"
                  style={{ backgroundColor: color + "22", color }}
                >
                  {tier}
                </span>
              </div>

              <div className="text-4xl font-bold mb-4" style={{ color }}>
                {score.toFixed(1)}
              </div>

              <div className="grid grid-cols-3 gap-3 text-sm">
                <div>
                  <div className="text-gray-500">成功率</div>
                  <div className="text-gray-200 font-mono">
                    {(parseFloat(agent.success_rate) * 100).toFixed(1)}%
                  </div>
                </div>
                <div>
                  <div className="text-gray-500">评分</div>
                  <div className="text-gray-200 font-mono">
                    {parseFloat(agent.avg_rating).toFixed(1)}
                  </div>
                </div>
                <div>
                  <div className="text-gray-500">任务</div>
                  <div className="text-gray-200 font-mono">{agent.total_tasks}</div>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

6.2 评分分解详情组件

// packages/frontend/src/components/ScoreBreakdown.tsx
import React from "react";

interface BreakdownProps {
  score: number;
  breakdown: {
    success_rate_component: string;
    rating_component: string;
    volume_component: string;
    stake_component: string;
    recency_component: string;
    diversity_component: string;
  };
}

const COMPONENT_LABELS: Record<string, { label: string; color: string }> = {
  success_rate_component: { label: "成功率", color: "#22c55e" },
  rating_component: { label: "平均评分", color: "#3b82f6" },
  volume_component: { label: "任务量", color: "#f59e0b" },
  stake_component: { label: "抵押加成", color: "#8b5cf6" },
  recency_component: { label: "时间衰减", color: "#06b6d4" },
  diversity_component: { label: "多样性", color: "#ec4899" },
};

export const ScoreBreakdownChart: React.FC<BreakdownProps> = ({ score, breakdown }) => {
  const items = Object.entries(COMPONENT_LABELS).map(([key, meta]) => {
    const rawValue = parseFloat(breakdown[key as keyof typeof breakdown]);
    return { ...meta, key, rawValue, contribution: rawValue };
  });

  return (
    <div className="bg-gray-900 rounded-xl p-6 border border-gray-800">
      <div className="text-center mb-6">
        <div className="text-5xl font-bold text-gray-100">{score.toFixed(1)}</div>
        <div className="text-gray-500 mt-1">综合信誉分</div>
      </div>

      <div className="space-y-3">
        {items.map((item) => (
          <div key={item.key}>
            <div className="flex justify-between text-sm mb-1">
              <span style={{ color: item.color }}>{item.label}</span>
              <span className="text-gray-400">{(item.rawValue * 100).toFixed(1)}%</span>
            </div>
            <div className="w-full bg-gray-800 rounded-full h-2">
              <div
                className="h-2 rounded-full transition-all duration-500"
                style={{
                  width: `${Math.min(item.rawValue * 100, 100)}%`,
                  backgroundColor: item.color,
                }}
              />
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

6.3 Agent 对比组件

// packages/frontend/src/components/AgentComparison.tsx
import React from "react";

interface CompareProps {
  agents: Array<{
    agent: string;
    composite_score: string;
    success_rate: string;
    avg_rating: string;
    total_tasks: string;
    stake_amount: string;
    breakdown: {
      success_rate_component: string;
      rating_component: string;
      volume_component: string;
      stake_component: string;
      recency_component: string;
      diversity_component: string;
    };
  }>;
}

export const AgentComparison: React.FC<CompareProps> = ({ agents }) => {
  if (agents.length === 0) return null;

  return (
    <div className="overflow-x-auto">
      <table className="w-full text-sm">
        <thead>
          <tr className="text-gray-500 border-b border-gray-800">
            <th className="text-left py-3 px-4">指标</th>
            {agents.map((a) => (
              <th key={a.agent} className="py-3 px-4 font-mono">
                {a.agent.slice(0, 10)}...
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {[
            { label: "综合评分", key: "composite_score", fmt: (v: string) => parseFloat(v).toFixed(1) },
            { label: "成功率", key: "success_rate", fmt: (v: string) => `${(parseFloat(v) * 100).toFixed(1)}%` },
            { label: "平均评分", key: "avg_rating", fmt: (v: string) => parseFloat(v).toFixed(2) },
            { label: "任务数", key: "total_tasks", fmt: (v: string) => v },
            { label: "抵押 (MSG)", key: "stake_amount", fmt: (v: string) => (parseInt(v) / 10**6).toFixed(0) },
          ].map((row) => (
            <tr key={row.key} className="border-b border-gray-800 hover:bg-gray-800/50">
              <td className="py-3 px-4 text-gray-400">{row.label}</td>
              {agents.map((a) => (
                <td key={a.agent} className="py-3 px-4 font-mono text-gray-200">
                  {row.fmt(a[row.key as keyof typeof a] as string)}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

6.4 争议面板组件

// packages/frontend/src/components/DisputePanel.tsx
import React, { useState } from "react";
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";

interface Dispute {
  dispute_id: string;
  task_id: string;
  initiator: string;
  respondent: string;
  dispute_type: string;
  status: string;
  created_at: string;
}

interface DisputePanelProps {
  disputes: Dispute[];
  signer: SigningCosmWasmClient;
  contractAddress: string;
  adminAddress: string;
}

export const DisputePanel: React.FC<DisputePanelProps> = ({
  disputes, signer, contractAddress, adminAddress,
}) => {
  const [selected, setSelected] = useState<string | null>(null);

  const resolveDispute = async (disputeId: string, upheld: boolean, adjustedRating?: number) => {
    const msg = {
      resolve_dispute: {
        dispute_id: disputeId,
        resolution: upheld ? "Upheld" : "Dismissed",
        adjusted_rating: adjustedRating,
      },
    };
    await signer.execute(adminAddress, contractAddress, [msg], "auto", `Resolve ${disputeId}`);
  };

  const statusColors: Record<string, string> = {
    Open: "text-yellow-400",
    UnderReview: "text-blue-400",
    Resolved: "text-green-400",
    Dismissed: "text-gray-500",
  };

  return (
    <div className="bg-gray-900 rounded-xl border border-gray-800">
      <div className="p-4 border-b border-gray-800">
        <h2 className="text-lg font-bold text-gray-100">争议面板</h2>
      </div>
      <div className="divide-y divide-gray-800">
        {disputes.map((d) => (
          <div
            key={d.dispute_id}
            className="p-4 hover:bg-gray-800/50 cursor-pointer"
            onClick={() => setSelected(selected === d.dispute_id ? null : d.dispute_id)}
          >
            <div className="flex justify-between items-center">
              <div>
                <span className="font-mono text-sm text-gray-300">{d.dispute_id.slice(0, 16)}...</span>
                <span className="ml-3 text-sm text-gray-500">{d.dispute_type}</span>
              </div>
              <span className={`text-sm font-medium ${statusColors[d.status] ?? "text-gray-400"}`}>
                {d.status}
              </span>
            </div>
            {selected === d.dispute_id && (
              <div className="mt-3 pl-2 border-l-2 border-gray-700">
                <p className="text-sm text-gray-400">
                  Task: <span className="font-mono text-gray-300">{d.task_id}</span>
                </p>
                <p className="text-sm text-gray-400">
                  Respondent: <span className="font-mono text-gray-300">{d.respondent.slice(0, 12)}...</span>
                </p>
                {(d.status === "Open" || d.status === "UnderReview") && (
                  <div className="mt-3 flex gap-2">
                    <button
                      className="px-3 py-1 bg-green-700 hover:bg-green-600 text-white rounded text-xs"
                      onClick={(e) => { e.stopPropagation(); resolveDispute(d.dispute_id, true); }}
                    >
                      Favor Agent
                    </button>
                    <button
                      className="px-3 py-1 bg-red-700 hover:bg-red-600 text-white rounded text-xs"
                      onClick={(e) => { e.stopPropagation(); resolveDispute(d.dispute_id, false); }}
                    >
                      Favor Requester
                    </button>
                  </div>
                )}
              </div>
            )}
          </div>
        ))}
        {disputes.length === 0 && (
          <div className="p-8 text-center text-gray-500">暂无争议记录</div>
        )}
      </div>
    </div>
  );
};

7. 集成到现有系统

7.1 集成到 Registry(信誊辅助发现)

在 Agent 注册系统中添加信誉查询,使信誉分数成为发现和排序的核心指标。

// packages/registry/src/integration/reputation-discovery.ts
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";

interface RegistryAgent {
  id: string;
  address: string;
  name: string;
  description: string;
  skills: string[];
  reputationScore?: number;
  tier?: string;
}

export class ReputationDiscoveryIntegration {
  private registryClient: CosmWasmClient;
  private repClient: CosmWasmClient;
  private repContract: string;

  constructor(registryClient: CosmWasmClient, repClient: CosmWasmClient, repContract: string) {
    this.registryClient = registryClient;
    this.repClient = repClient;
    this.repContract = repContract;
  }

  async listAgentsWithReputation(limit: number = 50): Promise<RegistryAgent[]> {
    const agents: RegistryAgent[] = await this.registryClient.queryContractSmart(
      "msg1registry...", { list_agents: { limit } }
    );

    const addresses = agents.map((a) => a.address);
    const reps: Array<{ score: { composite_score: string } }> = await this.repClient.queryContractSmart(
      this.repContract, { get_reputation_batch: { agents: addresses } }
    );

    return agents.map((agent, i) => ({
      ...agent,
      reputationScore: parseFloat(reps[i]?.score?.composite_score ?? "0"),
      tier: ["Low", "Basic", "Good", "High", "Elite"][
        Math.min(Math.floor((parseFloat(reps[i]?.score?.composite_score ?? "0")) / 25), 4)
      ],
    })).sort((a, b) => (b.reputationScore ?? 0) - (a.reputationScore ?? 0));
  }

  async searchByReputation(minScore: number, skill?: string): Promise<RegistryAgent[]> {
    const agents = await this.listAgentsWithReputation(200);
    return agents.filter(
      (a) => (a.reputationScore ?? 0) >= minScore && (!skill || a.skills.includes(skill))
    );
  }
}

7.2 集成到 AIPAY(基于信誉的支付条件)

// packages/aipay/src/integration/reputation-payment.ts
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";

interface PaymentTerms {
  agent: string;
  taskValue: string;
  upfrontPayment: string;
  milestonePayments: string[];
  collateralRequired: string;
  paymentSchedule: "upfront" | "milestone" | "completion";
}

export class ReputationAIPAYIntegration {
  private signer: SigningCosmWasmClient;
  private repContract: string;
  private aipayContract: string;

  constructor(signer: SigningCosmWasmClient, repContract: string, aipayContract: string) {
    this.signer = signer;
    this.repContract = repContract;
    this.aipayContract = aipayContract;
  }

  async computePaymentTerms(agent: string, taskValue: string): Promise<PaymentTerms> {
    const rep: { score: { composite_score: string; stake_amount: string } } =
      await this.signer.queryContractSmart(this.repContract, { get_reputation: { agent } });

    const score = parseFloat(rep.score.composite_score);
    const stake = parseInt(rep.score.stake_amount);
    const value = parseInt(taskValue);

    let collateralRequired: string;
    let paymentSchedule: PaymentTerms["paymentSchedule"];

    if (score >= 80) {
      collateralRequired = "0";
      paymentSchedule = "completion";
    } else if (score >= 60) {
      collateralRequired = Math.floor(value * 0.1).toString();
      paymentSchedule = "milestone";
    } else if (score >= 40) {
      collateralRequired = Math.floor(value * 0.3).toString();
      paymentSchedule = "milestone";
    } else {
      collateralRequired = Math.floor(value * 0.5).toString();
      paymentSchedule = "upfront";
    }

    const upfrontPayment = paymentSchedule === "upfront" ? taskValue : "0";
    const milestoneCount = paymentSchedule === "milestone" ? 3 : 0;
    const milestonePayments = Array.from({ length: milestoneCount }, (_, i) =>
      Math.floor(value / milestoneCount).toString()
    );

    return {
      agent, taskValue,
      upfrontPayment,
      milestonePayments,
      collateralRequired,
      paymentSchedule,
    };
  }

  async createReputationAwareEscrow(agent: string, requester: string, taskValue: string): Promise<string> {
    const terms = await this.computePaymentTerms(agent, taskValue);
    const msg = {
      create_escrow: {
        agent,
        requester,
        task_value: taskValue,
        collateral: terms.collateralRequired,
        payment_schedule: terms.paymentSchedule,
      },
    };
    const result = await this.signer.execute(requester, this.aipayContract, [msg], "auto", "Create escrow");
    return result.transactionHash;
  }
}

7.3 集成到 Constitution(最低信誉要求)

// contracts/constitution/src/reputation_guard.rs
use cosmwasm_std::{Addr, Decimal, Deps, StdResult};

/// 检查 Agent 是否满足宪章的最低信誉要求
pub fn check_reputation_requirement(
    deps: Deps,
    agent: &Addr,
    rep_contract: &Addr,
    required_score: Decimal,
) -> StdResult<bool> {
    let resp: ReputationCheckResponse = deps.querier.query_wasm_smart(
        rep_contract.clone(),
        &ReputationQuery::GetReputation {
            agent: agent.to_string(),
        },
    )?;

    Ok(resp.score.composite_score >= required_score)
}

/// 获取基于信誉的权限级别
pub fn get_reputation_permission_level(
    deps: Deps,
    agent: &Addr,
    rep_contract: &Addr,
) -> StdResult<PermissionLevel> {
    let resp: ReputationCheckResponse = deps.querier.query_wasm_smart(
        rep_contract.clone(),
        &ReputationQuery::GetReputation {
            agent: agent.to_string(),
        },
    )?;

    let score = resp.score.composite_score;
    if score >= Decimal::from_ratio(90u128, 1u128) {
        Ok(PermissionLevel::Full)
    } else if score >= Decimal::from_ratio(60u128, 1u128) {
        Ok(PermissionLevel::Elevated)
    } else if score >= Decimal::from_ratio(30u128, 1u128) {
        Ok(PermissionLevel::Standard)
    } else {
        Ok(PermissionLevel::Restricted)
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct ReputationCheckResponse {
    pub score: RepScore,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct RepScore {
    pub composite_score: Decimal,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
pub enum PermissionLevel {
    Restricted,
    Standard,
    Elevated,
    Full,
}

#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReputationQuery {
    GetReputation { agent: String },
}

7.4 跨合约查询示例

// packages/client/examples/cross-contract-integration.ts
import { CosmWasmClient, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";

interface CrossContractResult {
  agent: string;
  reputation: number;
  registryInfo: { name: string; skills: string[] };
  paymentTerms: { collateral: string; schedule: string };
  governanceStatus: { permissionLevel: string; allowed: boolean };
}

async function getFullAgentProfile(
  client: CosmWasmClient,
  signer: SigningCosmWasmClient,
  agent: string
): Promise<CrossContractResult> {
  const repContract = "msg1reputation...";
  const registryContract = "msg1registry...";
  const aipayContract = "msg1aipay...";
  const constitutionContract = "msg1constitution...";

  const [rep, registry, governanceStatus] = await Promise.all([
    client.queryContractSmart(repContract, { get_reputation: { agent } }),
    client.queryContractSmart(registryContract, { get_agent: { address: agent } }),
    client.queryContractSmart(constitutionContract, {
      check_agent_permission: { agent, required_action: "execute_task" },
    }),
  ]);

  const paymentTerms: { collateral: string; schedule: string } =
    await signer.queryContractSmart(aipayContract, {
      compute_payment_terms: { agent, task_value: "1000000000" },
    });

  return {
    agent,
    reputation: parseFloat(rep.score.composite_score),
    registryInfo: { name: registry.name, skills: registry.skills },
    paymentTerms,
    governanceStatus: {
      permissionLevel: governanceStatus.level,
      allowed: governanceStatus.allowed,
    },
  };
}

7.5 部署与初始化脚本

#!/bin/bash
# scripts/deploy_reputation.sh
# MSG Chain 信誉合约部署脚本

CHAIN_ID="msg-chain-1"
RPC="https://rpc.msg-chain-1.msg.network"
MNEMONIC="your deployer mnemonic"
ADMIN="msg1admin..."
LABEL="reputation-core"
CODE_ID=""

echo "=== 编译合约 ==="
cargo wasm

echo "=== 优化 WASM ==="
docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/workspace-optimizer:0.14.0

echo "=== 上传合约 ==="
RES=$(msgd tx wasm store artifacts/reputation_core.wasm \
  --from deployer --chain-id "$CHAIN_ID" --node "$RPC" \
  --gas auto --gas-prices 1000000000attoMSG --gas-adjustment 1.3 \
  --output json -y)
CODE_ID=$(echo "$RES" | jq -r '.logs[0].events[] | select(.type=="store_code") | .attributes[] | select(.key=="code_id") | .value')
echo "Code ID: $CODE_ID"

echo "=== 实例化合约 ==="
INIT_JSON='{"admin":"'"$ADMIN"'","params":null}'
msgd tx wasm instantiate "$CODE_ID" "$INIT_JSON" \
  --from deployer --label "$LABEL" --admin "$ADMIN" \
  --chain-id "$CHAIN_ID" --node "$RPC" \
  --gas auto --gas-prices 1000000000attoMSG --gas-adjustment 1.3 \
  --output json -y

echo "=== 查询合约地址 ==="
CONTRACT=$(msgd query wasm list-contract-by-code "$CODE_ID" --node "$RPC" --output json | jq -r '.contracts[0]')
echo "Reputation Contract: $CONTRACT"

echo "=== 初始化治理参数 ==="
PARAMS_JSON='{"update_params":{"params":{"success_rate_weight":"0.30","rating_weight":"0.25","volume_weight":"0.15","stake_weight":"0.10","recency_weight":"0.10","diversity_weight":"0.10"}}}'
msgd tx wasm execute "$CONTRACT" "$PARAMS_JSON" \
  --from deployer --chain-id "$CHAIN_ID" --node "$RPC" \
  --gas auto --gas-prices 1000000000attoMSG --gas-adjustment 1.3 -y

echo "部署完成!"
echo "合约地址: $CONTRACT"
echo "管理员: $ADMIN"

7.6 完整测试脚本

#!/bin/bash
# scripts/test_reputation.sh

CONTRACT="msg1reputation..."
DEPLOYER="msg1deployer..."
AGENT="msg1agent1..."
REQUESTER="msg1requester..."

echo "=== 1. 记录任务 ==="
msgd tx wasm execute "$CONTRACT" '{
  "record_task_result": {
    "task_id":"task-001",
    "agent":"'"$AGENT"'",
    "requester":"'"$REQUESTER"'",
    "status":"completed",
    "reward":"1000000",
    "evidence_hash":"ipfs://QmTest"
  }
}' --from "$REQUESTER" --chain-id msg-chain-1 --gas auto --gas-prices 1000000000attoMSG --gas-adjustment 1.3 -y

echo "=== 2. 提交评分 ==="
msgd tx wasm execute "$CONTRACT" '{
  "submit_rating": {
    "agent":"'"$AGENT"'",
    "task_id":"task-001",
    "score":5,
    "comment_hash":"ipfs://QmComment"
  }
}' --from "$REQUESTER" --chain-id msg-chain-1 --gas auto --gas-prices 1000000000attoMSG --gas-adjustment 1.3 -y

echo "=== 3. Agent 抵押 ==="
msgd tx wasm execute "$CONTRACT" '{
  "stake": {
    "amount":"10000000000",
    "lock_duration_secs":2592000
  }
}' --from "$AGENT" --amount 10000msg --chain-id msg-chain-1 --gas auto --gas-prices 1000000000attoMSG --gas-adjustment 1.3 -y

echo "=== 4. 查询信誉 ==="
msgd query wasm contract-state smart "$CONTRACT" '{
  "get_reputation": {"agent":"'"$AGENT"'"}
}'

echo "=== 5. 查看排行榜 ==="
msgd query wasm contract-state smart "$CONTRACT" '{
  "list_top_agents":{"limit":10}
}'

echo "测试完成"

附录

A. 参考链接

资源 链接
MSG Chain 文档 https://docs.msg.network
CosmWasm 文档 https://docs.cosmwasm.com
cw-storage-plus https://github.com/CosmWasm/cw-storage-plus
信誉合约完整代码 https://github.com/msgchain/reputation-contracts
前端 SDK https://github.com/msgchain/reputation-sdk

B. 术语表

术语 英文 说明
信誉分 Reputation Score 0-100 的综合评分
信誉等级 Tier Low/Basic/Good/High/Elite
抵押 Stake 用于信誉加成的代币锁定
争议 Dispute 对评分或任务结果的异议
申诉 Appeal 对争议裁决的再申诉
Sybil 抵抗 Sybil Resistance 防止虚假身份攻击
时间衰减 Time Decay 旧行为对评分影响递减
多样性 Diversity 合作过的发布者数量
治理覆写 Governance Override 治理投票修改评分

C. 版本历史

版本 日期 变更
v1.0.0 2026-07-06 初始版本