feat(growth): 新增 Evolution Engine 核心类型 — ExperienceCandidate/CombinedExtraction/EvolutionEvent

This commit is contained in:
iven
2026-04-18 20:47:30 +08:00
parent e88c51fd85
commit 7e8eb64c4a

View File

@@ -394,6 +394,81 @@ pub struct DecayResult {
pub archived: u64,
}
// === Evolution Engine Types ===
/// 经验提取结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExperienceCandidate {
pub pain_pattern: String,
pub context: String,
pub solution_steps: Vec<String>,
pub outcome: Outcome,
pub confidence: f32,
pub tools_used: Vec<String>,
pub industry_context: Option<String>,
}
/// 结果状态
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum Outcome {
Success,
Partial,
Failed,
}
/// 合并提取结果(单次 LLM 调用的全部输出)
#[derive(Debug, Clone, Default)]
pub struct CombinedExtraction {
pub memories: Vec<ExtractedMemory>,
pub experiences: Vec<ExperienceCandidate>,
pub profile_signals: ProfileSignals,
}
/// 画像更新信号(从提取结果中推断,不额外调用 LLM
#[derive(Debug, Clone, Default)]
pub struct ProfileSignals {
pub industry: Option<String>,
pub recent_topic: Option<String>,
pub pain_point: Option<String>,
pub preferred_tool: Option<String>,
pub communication_style: Option<String>,
}
/// 进化事件
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvolutionEvent {
pub id: String,
pub event_type: EvolutionEventType,
pub artifact_type: ArtifactType,
pub artifact_id: String,
pub status: EvolutionStatus,
pub confidence: f32,
pub user_feedback: Option<String>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EvolutionEventType {
SkillGenerated,
SkillOptimized,
WorkflowGenerated,
WorkflowOptimized,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ArtifactType {
Skill,
Pipeline,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EvolutionStatus {
Pending,
Confirmed,
Rejected,
Optimized,
}
/// Compute effective importance with time decay.
///
/// Uses exponential decay: each 30-day period of non-access reduces
@@ -524,4 +599,61 @@ mod tests {
assert!(!result.is_empty());
assert_eq!(result.total_count(), 1);
}
#[test]
fn test_experience_candidate_roundtrip() {
let candidate = ExperienceCandidate {
pain_pattern: "报表生成".to_string(),
context: "月度销售报表".to_string(),
solution_steps: vec!["查询数据库".to_string(), "格式化输出".to_string()],
outcome: Outcome::Success,
confidence: 0.85,
tools_used: vec!["researcher".to_string()],
industry_context: Some("healthcare".to_string()),
};
let json = serde_json::to_string(&candidate).unwrap();
let decoded: ExperienceCandidate = serde_json::from_str(&json).unwrap();
assert_eq!(decoded.pain_pattern, "报表生成");
assert_eq!(decoded.outcome, Outcome::Success);
assert_eq!(decoded.solution_steps.len(), 2);
}
#[test]
fn test_evolution_event_roundtrip() {
let event = EvolutionEvent {
id: uuid::Uuid::new_v4().to_string(),
event_type: EvolutionEventType::SkillGenerated,
artifact_type: ArtifactType::Skill,
artifact_id: "daily-report".to_string(),
status: EvolutionStatus::Pending,
confidence: 0.8,
user_feedback: None,
created_at: chrono::Utc::now(),
};
let json = serde_json::to_string(&event).unwrap();
let decoded: EvolutionEvent = serde_json::from_str(&json).unwrap();
assert_eq!(decoded.event_type, EvolutionEventType::SkillGenerated);
assert_eq!(decoded.status, EvolutionStatus::Pending);
}
#[test]
fn test_combined_extraction_default() {
let combined = CombinedExtraction::default();
assert!(combined.memories.is_empty());
assert!(combined.experiences.is_empty());
assert!(combined.profile_signals.industry.is_none());
}
#[test]
fn test_profile_signals() {
let signals = ProfileSignals {
industry: Some("healthcare".to_string()),
recent_topic: Some("报表".to_string()),
pain_point: None,
preferred_tool: Some("researcher".to_string()),
communication_style: Some("concise".to_string()),
};
assert_eq!(signals.industry.as_deref(), Some("healthcare"));
assert!(signals.pain_point.is_none());
}
}