Skip to main content

dhive_core/
trust.rs

1//! 信任标注 — 置信度阶梯、溯源链、审计日志
2//!
3//! D-HIVE 信任模型:置信度(5级) × 溯源链深度 × 审计日志完整性 × 争议解决率
4
5use serde::{Deserialize, Serialize};
6
7/// 置信度阶梯 (5 级有序)
8///
9/// speculative → uncertain → medium → high → certain
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
11#[serde(rename_all = "camelCase")]
12pub enum Confidence {
13    /// 推测,可能错误 — 低置信度 AI 输出
14    Speculative,
15    /// 默认,未经验证 — 新创建的记忆
16    Uncertain,
17    /// 经过一次确认
18    Medium,
19    /// 反复验证
20    High,
21    /// 确定为真 — 基石原则、用户显式标记
22    Certain,
23}
24
25impl Confidence {
26    pub fn as_str(&self) -> &'static str {
27        match self {
28            Confidence::Speculative => "speculative",
29            Confidence::Uncertain => "uncertain",
30            Confidence::Medium => "medium",
31            Confidence::High => "high",
32            Confidence::Certain => "certain",
33        }
34    }
35
36    pub fn from_str(s: &str) -> Option<Self> {
37        match s {
38            "speculative" => Some(Confidence::Speculative),
39            "uncertain" => Some(Confidence::Uncertain),
40            "medium" => Some(Confidence::Medium),
41            "high" => Some(Confidence::High),
42            "certain" => Some(Confidence::Certain),
43            _ => None,
44        }
45    }
46
47    /// 置信度系数 (用于策展评分)
48    pub fn factor(&self) -> f64 {
49        match self {
50            Confidence::Speculative => 0.15,
51            Confidence::Uncertain => 0.4,
52            Confidence::Medium => 0.7,
53            Confidence::High => 0.9,
54            Confidence::Certain => 1.0,
55        }
56    }
57
58    /// 搜索加权系数
59    pub fn search_boost(&self) -> f64 {
60        match self {
61            Confidence::Speculative => 0.5,
62            Confidence::Uncertain => 0.8,
63            Confidence::Medium => 1.0,
64            Confidence::High => 1.2,
65            Confidence::Certain => 1.5,
66        }
67    }
68
69    /// confirm 升级
70    pub fn confirm(&self) -> (Confidence, bool) {
71        match self {
72            Confidence::Speculative => (Confidence::Uncertain, true),
73            Confidence::Uncertain => (Confidence::Medium, true),
74            Confidence::Medium => (Confidence::High, true),
75            Confidence::High => (Confidence::Certain, true),
76            Confidence::Certain => (Confidence::Certain, false), // already max
77        }
78    }
79}
80
81/// 溯源信息 — 记忆从哪里来
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct Provenance {
84    /// 信息来源 (CLI/MCP/hook/auto/user/session-id)
85    pub source: String,
86    /// 推导来源记忆 ID 列表
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub derived_from: Option<Vec<String>>,
89    /// 确认者
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub confirmed_by: Option<String>,
92    /// 确认时间 (ISO 8601)
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub confirmed_at: Option<String>,
95    /// 是否 AI 自动生成
96    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
97    pub auto_generated: bool,
98    /// 是否需要人工审核
99    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
100    pub needs_review: bool,
101}
102
103impl Provenance {
104    pub fn new(source: &str) -> Self {
105        Provenance {
106            source: source.to_string(),
107            derived_from: None,
108            confirmed_by: None,
109            confirmed_at: None,
110            auto_generated: false,
111            needs_review: false,
112        }
113    }
114}
115
116/// 争议记录
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct Dispute {
119    pub id: String,
120    pub reason: String,
121    #[serde(rename = "disputedAt")]
122    pub disputed_at: String,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    #[serde(rename = "resolvedAt")]
125    pub resolved_at: Option<String>,
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub resolution: Option<String>,
128}
129
130/// 审计日志条目
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct AuditEntry {
133    pub action: String,
134    pub by: String,
135    pub at: String,
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub detail: Option<String>,
138    #[serde(skip_serializing_if = "Option::is_none")]
139    #[serde(rename = "mergedFrom")]
140    pub merged_from: Option<String>,
141}
142
143impl AuditEntry {
144    pub fn new(action: &str, by: &str, at: &str) -> Self {
145        AuditEntry {
146            action: action.to_string(),
147            by: by.to_string(),
148            at: at.to_string(),
149            detail: None,
150            merged_from: None,
151        }
152    }
153}
154
155/// 完整的信任数据
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct TrustData {
158    pub confidence: Confidence,
159    pub provenance: Provenance,
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub disputes: Option<Vec<Dispute>>,
162    #[serde(skip_serializing_if = "Option::is_none")]
163    #[serde(rename = "auditLog")]
164    pub audit_log: Option<Vec<AuditEntry>>,
165}
166
167impl TrustData {
168    pub fn new(confidence: Confidence, source: &str) -> Self {
169        TrustData {
170            confidence,
171            provenance: Provenance::new(source),
172            disputes: None,
173            audit_log: None,
174        }
175    }
176
177    /// 追加审计日志条目 (不可变性 — 仅追加)
178    pub fn append_audit(&mut self, entry: AuditEntry) {
179        if let Some(ref mut log) = self.audit_log {
180            log.push(entry);
181        } else {
182            self.audit_log = Some(vec![entry]);
183        }
184    }
185
186    /// 争议衰减系数 (用于策展评分)
187    pub fn dispute_decay(&self) -> f64 {
188        match &self.disputes {
189            Some(d) => d.len() as f64 * 0.15,
190            None => 0.0,
191        }
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_confidence_ladder() {
201        let (next, changed) = Confidence::Uncertain.confirm();
202        assert_eq!(next, Confidence::Medium);
203        assert!(changed);
204
205        let (next, changed) = Confidence::Certain.confirm();
206        assert_eq!(next, Confidence::Certain);
207        assert!(!changed);
208    }
209
210    #[test]
211    fn test_confidence_factor() {
212        assert!(Confidence::Certain.factor() > Confidence::Speculative.factor());
213        assert_eq!(Confidence::Certain.factor(), 1.0);
214        assert_eq!(Confidence::Speculative.factor(), 0.15);
215    }
216
217    #[test]
218    fn test_provenance_new() {
219        let p = Provenance::new("CLI");
220        assert_eq!(p.source, "CLI");
221        assert!(!p.auto_generated);
222    }
223
224    #[test]
225    fn test_audit_append() {
226        let mut td = TrustData::new(Confidence::Uncertain, "test");
227        td.append_audit(AuditEntry::new("recorded", "CLI", "2026-01-01T00:00:00Z"));
228        assert_eq!(td.audit_log.as_ref().unwrap().len(), 1);
229    }
230
231    #[test]
232    fn test_dispute_decay() {
233        let mut td = TrustData::new(Confidence::High, "test");
234        assert_eq!(td.dispute_decay(), 0.0);
235
236        td.disputes = Some(vec![Dispute {
237            id: "d1".into(),
238            reason: "测试质疑".into(),
239            disputed_at: "2026-01-01T00:00:00Z".into(),
240            resolved_at: None,
241            resolution: None,
242        }]);
243        assert_eq!(td.dispute_decay(), 0.15);
244    }
245}