From 7e8eb64c4a48f28ddd3b1ff0f27f72f2779c2ef8 Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 18 Apr 2026 20:47:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(growth):=20=E6=96=B0=E5=A2=9E=20Evolution?= =?UTF-8?q?=20Engine=20=E6=A0=B8=E5=BF=83=E7=B1=BB=E5=9E=8B=20=E2=80=94=20?= =?UTF-8?q?ExperienceCandidate/CombinedExtraction/EvolutionEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/zclaw-growth/src/types.rs | 132 +++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/crates/zclaw-growth/src/types.rs b/crates/zclaw-growth/src/types.rs index 6c540c9..5b4d282 100644 --- a/crates/zclaw-growth/src/types.rs +++ b/crates/zclaw-growth/src/types.rs @@ -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, + pub outcome: Outcome, + pub confidence: f32, + pub tools_used: Vec, + pub industry_context: Option, +} + +/// 结果状态 +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum Outcome { + Success, + Partial, + Failed, +} + +/// 合并提取结果(单次 LLM 调用的全部输出) +#[derive(Debug, Clone, Default)] +pub struct CombinedExtraction { + pub memories: Vec, + pub experiences: Vec, + pub profile_signals: ProfileSignals, +} + +/// 画像更新信号(从提取结果中推断,不额外调用 LLM) +#[derive(Debug, Clone, Default)] +pub struct ProfileSignals { + pub industry: Option, + pub recent_topic: Option, + pub pain_point: Option, + pub preferred_tool: Option, + pub communication_style: Option, +} + +/// 进化事件 +#[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, + pub created_at: DateTime, +} + +#[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()); + } }