Files
zclaw_openfang/crates/zclaw-growth/src/evolution_engine.rs
iven d9b0b4f4f7
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
fix(audit): Batch 7-9 dead_code 标注 + TODO 清理 + 文档同步
Batch 7: dead_code 标注统一 (16 处)
- crates/ 9 处: growth, kernel, pipeline, runtime, saas, skills
- src-tauri/ 7 处: classroom, intelligence, browser, mcp
- 统一格式: #[allow(dead_code)] // @reserved: <原因>

Batch 7+: EvolutionEngine L2/L3 10 个未使用 pub 函数
- 全部标注 @reserved: EvolutionEngine L2/L3, post-release integration

Batch 9: TODO → FUTURE 标记 (4 处)
- html.rs: template-based export
- nl_schedule.rs: LLM-assisted parsing
- knowledge/handlers.rs: category_id from upload
- personality_detector.rs: VikingStorage persistence

Batch 5+: Cargo.lock 更新 (serde_yaml_bw 迁移)

全量测试通过: 719 passed, 0 failed
2026-04-19 08:54:57 +08:00

306 lines
10 KiB
Rust

//! 进化引擎中枢
//! 协调 L1/L2/L3 三层进化的触发和执行
//! L1 (记忆进化) 在 GrowthIntegration 中处理
//! L2 (技能进化) 通过 PatternAggregator + SkillGenerator + QualityGate 协调
//! L3 (工作流进化) 通过 WorkflowComposer 协调
//! 反馈闭环通过 FeedbackCollector 管理
use std::sync::Arc;
use crate::experience_store::ExperienceStore;
use crate::feedback_collector::{
FeedbackCollector, FeedbackEntry, TrustUpdate,
};
use crate::pattern_aggregator::{AggregatedPattern, PatternAggregator};
use crate::quality_gate::{QualityGate, QualityReport};
use crate::skill_generator::{SkillCandidate, SkillGenerator};
use crate::workflow_composer::{ToolChainPattern, WorkflowComposer};
use crate::VikingAdapter;
use zclaw_types::Result;
/// 进化引擎配置
#[derive(Debug, Clone)]
pub struct EvolutionConfig {
/// 经验复用次数达到此阈值触发 L2
pub min_reuse_for_skill: u32,
/// 置信度阈值
pub quality_confidence_threshold: f32,
/// 是否启用进化引擎
pub enabled: bool,
}
impl Default for EvolutionConfig {
fn default() -> Self {
Self {
min_reuse_for_skill: 3,
quality_confidence_threshold: 0.7,
enabled: true,
}
}
}
/// 进化引擎中枢
pub struct EvolutionEngine {
viking: Arc<VikingAdapter>,
feedback: Arc<tokio::sync::Mutex<FeedbackCollector>>,
config: EvolutionConfig,
}
impl EvolutionEngine {
pub fn new(viking: Arc<VikingAdapter>) -> Self {
Self {
viking: viking.clone(),
feedback: Arc::new(tokio::sync::Mutex::new(
FeedbackCollector::with_viking(viking),
)),
config: EvolutionConfig::default(),
}
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// Backward-compatible constructor
/// 从 ExperienceStore 中提取共享的 VikingAdapter 实例
pub fn from_experience_store(experience_store: Arc<ExperienceStore>) -> Self {
let viking = experience_store.viking().clone();
Self {
viking: viking.clone(),
feedback: Arc::new(tokio::sync::Mutex::new(
FeedbackCollector::with_viking(viking),
)),
config: EvolutionConfig::default(),
}
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
pub fn with_config(mut self, config: EvolutionConfig) -> Self {
self.config = config;
self
}
pub fn set_enabled(&mut self, enabled: bool) {
self.config.enabled = enabled;
}
/// L2 检查:是否有可进化的模式
pub async fn check_evolvable_patterns(
&self,
agent_id: &str,
) -> Result<Vec<AggregatedPattern>> {
if !self.config.enabled {
return Ok(Vec::new());
}
let store = ExperienceStore::new(self.viking.clone());
let aggregator = PatternAggregator::new(store);
aggregator
.find_evolvable_patterns(agent_id, self.config.min_reuse_for_skill)
.await
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// L2 执行:为给定模式构建技能生成 prompt
/// 返回 (prompt_string, pattern) 供上层通过 LLM 调用后 parse
pub fn build_skill_prompt(&self, pattern: &AggregatedPattern) -> String {
SkillGenerator::build_prompt(pattern)
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// L2 执行:解析 LLM 返回的技能 JSON 并进行质量门控
pub fn validate_skill_candidate(
&self,
json_str: &str,
pattern: &AggregatedPattern,
existing_triggers: Vec<String>,
) -> Result<(SkillCandidate, QualityReport)> {
let candidate = SkillGenerator::parse_response(json_str, pattern)?;
let gate = QualityGate::new(self.config.quality_confidence_threshold, existing_triggers);
let report = gate.validate_skill(&candidate);
Ok((candidate, report))
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// 获取当前配置
pub fn config(&self) -> &EvolutionConfig {
&self.config
}
// -----------------------------------------------------------------------
// L3: 工作流进化
// -----------------------------------------------------------------------
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// L3: 从轨迹数据中提取重复的工具链模式
pub fn analyze_trajectory_patterns(
&self,
trajectories: &[(String, Vec<String>)], // (session_id, tools_used)
) -> Vec<(ToolChainPattern, Vec<String>)> {
if !self.config.enabled {
return Vec::new();
}
WorkflowComposer::extract_patterns(trajectories)
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// L3: 为给定工具链模式构建工作流生成 prompt
pub fn build_workflow_prompt(
&self,
pattern: &ToolChainPattern,
frequency: usize,
industry: Option<&str>,
) -> String {
WorkflowComposer::build_prompt(pattern, frequency, industry)
}
// -----------------------------------------------------------------------
// 反馈闭环
// -----------------------------------------------------------------------
/// 提交反馈并获取信任度更新,自动持久化
pub async fn submit_feedback(&self, entry: FeedbackEntry) -> TrustUpdate {
let mut feedback = self.feedback.lock().await;
let update = feedback.submit_feedback(entry);
// 非阻塞持久化:失败仅打日志,不影响返回值
if let Err(e) = feedback.save().await {
tracing::warn!("[EvolutionEngine] Failed to persist trust records: {}", e);
}
update
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// 获取需要优化的进化产物
pub async fn get_artifacts_needing_optimization(&self) -> Vec<String> {
self.feedback
.lock()
.await
.get_artifacts_needing_optimization()
.iter()
.map(|r| r.artifact_id.clone())
.collect()
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// 获取建议归档的进化产物
pub async fn get_artifacts_to_archive(&self) -> Vec<String> {
self.feedback
.lock()
.await
.get_artifacts_to_archive()
.iter()
.map(|r| r.artifact_id.clone())
.collect()
}
/// @reserved: EvolutionEngine L2/L3 feature, post-release integration
/// 获取推荐产物
pub async fn get_recommended_artifacts(&self) -> Vec<String> {
self.feedback
.lock()
.await
.get_recommended_artifacts()
.iter()
.map(|r| r.artifact_id.clone())
.collect()
}
/// 启动时加载已持久化的信任度记录
pub async fn load_feedback(&self) -> Result<usize> {
self.feedback
.lock()
.await
.load()
.await
.map_err(|e| zclaw_types::ZclawError::Internal(e))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::experience_store::Experience;
#[tokio::test]
async fn test_disabled_returns_empty() {
let viking = Arc::new(crate::VikingAdapter::in_memory());
let mut engine = EvolutionEngine::new(viking);
engine.set_enabled(false);
let patterns = engine.check_evolvable_patterns("agent-1").await.unwrap();
assert!(patterns.is_empty());
}
#[tokio::test]
async fn test_no_evolvable_patterns() {
let viking = Arc::new(crate::VikingAdapter::in_memory());
let engine = EvolutionEngine::new(viking);
let patterns = engine.check_evolvable_patterns("unknown-agent").await.unwrap();
assert!(patterns.is_empty());
}
#[tokio::test]
async fn test_finds_evolvable_pattern() {
let viking = Arc::new(crate::VikingAdapter::in_memory());
let store_inner = ExperienceStore::new(viking.clone());
let mut exp = Experience::new(
"agent-1",
"report generation",
"researcher",
vec!["query db".into(), "format".into()],
"success",
);
exp.reuse_count = 5;
store_inner.store_experience(&exp).await.unwrap();
let engine = EvolutionEngine::new(viking);
let patterns = engine.check_evolvable_patterns("agent-1").await.unwrap();
assert_eq!(patterns.len(), 1);
assert_eq!(patterns[0].pain_pattern, "report generation");
}
#[test]
fn test_build_skill_prompt() {
let viking = Arc::new(crate::VikingAdapter::in_memory());
let engine = EvolutionEngine::new(viking);
let exp = Experience::new(
"a", "report", "researcher", vec!["step1".into()], "ok",
);
let pattern = AggregatedPattern {
pain_pattern: "report".to_string(),
experiences: vec![exp],
common_steps: vec!["step1".into()],
total_reuse: 5,
tools_used: vec!["researcher".into()],
industry_context: None,
};
let prompt = engine.build_skill_prompt(&pattern);
assert!(prompt.contains("report"));
}
#[test]
fn test_validate_skill_candidate() {
let viking = Arc::new(crate::VikingAdapter::in_memory());
let engine = EvolutionEngine::new(viking);
let exp = Experience::new(
"a", "report", "researcher", vec!["step1".into()], "ok",
);
let pattern = AggregatedPattern {
pain_pattern: "report".to_string(),
experiences: vec![exp],
common_steps: vec!["step1".into()],
total_reuse: 5,
tools_used: vec!["researcher".into()],
industry_context: None,
};
let json = r##"{"name":"报表技能","description":"生成报表","triggers":["报表","日报"],"tools":["researcher"],"body_markdown":"# 报表\n步骤","confidence":0.9}"##;
let (candidate, report) = engine
.validate_skill_candidate(json, &pattern, vec!["搜索".to_string()])
.unwrap();
assert_eq!(candidate.name, "报表技能");
assert!(report.passed);
}
}