feat(growth): L2 技能进化核心 — PatternAggregator+SkillGenerator+QualityGate+EvolutionEngine
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
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
- PatternAggregator: 经验模式聚合,找出 reuse_count>=threshold 的可固化模式 - SkillGenerator: LLM prompt 构建 + JSON 解析 + 自动提取 JSON 块 - QualityGate: 置信度/冲突/格式质量门控 - EvolutionEngine: 中枢调度器,协调 L2 触发检查+技能生成+质量验证 新增 24 个测试(87→111),全 workspace 0 error。
This commit is contained in:
159
crates/zclaw-growth/src/quality_gate.rs
Normal file
159
crates/zclaw-growth/src/quality_gate.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
//! 质量门控
|
||||
//! 验证生成的技能/工作流是否满足质量标准
|
||||
//! 包括:置信度阈值、触发词冲突检查、格式校验
|
||||
|
||||
use crate::skill_generator::SkillCandidate;
|
||||
|
||||
/// 质量验证报告
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QualityReport {
|
||||
pub passed: bool,
|
||||
pub issues: Vec<String>,
|
||||
pub confidence: f32,
|
||||
}
|
||||
|
||||
/// 质量门控验证器
|
||||
pub struct QualityGate {
|
||||
min_confidence: f32,
|
||||
existing_triggers: Vec<String>,
|
||||
}
|
||||
|
||||
impl QualityGate {
|
||||
pub fn new(min_confidence: f32, existing_triggers: Vec<String>) -> Self {
|
||||
Self {
|
||||
min_confidence,
|
||||
existing_triggers,
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证技能候选项
|
||||
pub fn validate_skill(&self, candidate: &SkillCandidate) -> QualityReport {
|
||||
let mut issues = Vec::new();
|
||||
|
||||
// 1. 置信度检查
|
||||
if candidate.confidence < self.min_confidence {
|
||||
issues.push(format!(
|
||||
"置信度 {:.2} 低于阈值 {:.2}",
|
||||
candidate.confidence, self.min_confidence
|
||||
));
|
||||
}
|
||||
|
||||
// 2. 名称非空
|
||||
if candidate.name.trim().is_empty() {
|
||||
issues.push("技能名称不能为空".to_string());
|
||||
}
|
||||
|
||||
// 3. 至少一个触发词
|
||||
if candidate.triggers.is_empty() {
|
||||
issues.push("至少需要一个触发词".to_string());
|
||||
}
|
||||
|
||||
// 4. 触发词不与现有技能冲突
|
||||
let conflicts: Vec<_> = candidate
|
||||
.triggers
|
||||
.iter()
|
||||
.filter(|t| self.existing_triggers.iter().any(|et| et == *t))
|
||||
.collect();
|
||||
if !conflicts.is_empty() {
|
||||
issues.push(format!("触发词冲突: {:?}", conflicts));
|
||||
}
|
||||
|
||||
// 5. SKILL.md 正文非空
|
||||
if candidate.body_markdown.trim().is_empty() {
|
||||
issues.push("技能正文不能为空".to_string());
|
||||
}
|
||||
|
||||
QualityReport {
|
||||
passed: issues.is_empty(),
|
||||
issues,
|
||||
confidence: candidate.confidence,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn make_valid_candidate() -> SkillCandidate {
|
||||
SkillCandidate {
|
||||
name: "每日报表".to_string(),
|
||||
description: "生成每日报表".to_string(),
|
||||
triggers: vec!["报表".to_string(), "日报".to_string()],
|
||||
tools: vec!["researcher".to_string()],
|
||||
body_markdown: "# 每日报表\n步骤1\n步骤2".to_string(),
|
||||
source_pattern: "报表生成".to_string(),
|
||||
confidence: 0.85,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_valid_skill() {
|
||||
let gate = QualityGate::new(0.7, vec!["搜索".to_string()]);
|
||||
let candidate = make_valid_candidate();
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(report.passed);
|
||||
assert!(report.issues.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_low_confidence() {
|
||||
let gate = QualityGate::new(0.7, vec![]);
|
||||
let mut candidate = make_valid_candidate();
|
||||
candidate.confidence = 0.5;
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(!report.passed);
|
||||
assert!(report.issues.iter().any(|i| i.contains("置信度")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_empty_name() {
|
||||
let gate = QualityGate::new(0.5, vec![]);
|
||||
let mut candidate = make_valid_candidate();
|
||||
candidate.name = "".to_string();
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(!report.passed);
|
||||
assert!(report.issues.iter().any(|i| i.contains("名称")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_empty_triggers() {
|
||||
let gate = QualityGate::new(0.5, vec![]);
|
||||
let mut candidate = make_valid_candidate();
|
||||
candidate.triggers = vec![];
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(!report.passed);
|
||||
assert!(report.issues.iter().any(|i| i.contains("触发词")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_trigger_conflict() {
|
||||
let gate = QualityGate::new(0.5, vec!["报表".to_string()]);
|
||||
let candidate = make_valid_candidate();
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(!report.passed);
|
||||
assert!(report.issues.iter().any(|i| i.contains("冲突")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_empty_body() {
|
||||
let gate = QualityGate::new(0.5, vec![]);
|
||||
let mut candidate = make_valid_candidate();
|
||||
candidate.body_markdown = "".to_string();
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(!report.passed);
|
||||
assert!(report.issues.iter().any(|i| i.contains("正文")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_multiple_issues() {
|
||||
let gate = QualityGate::new(0.9, vec![]);
|
||||
let mut candidate = make_valid_candidate();
|
||||
candidate.confidence = 0.3;
|
||||
candidate.triggers = vec![];
|
||||
candidate.body_markdown = "".to_string();
|
||||
let report = gate.validate_skill(&candidate);
|
||||
assert!(!report.passed);
|
||||
assert!(report.issues.len() >= 3);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user