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
A线 Embedding 接通: - A1: MemoryRetriever.set_embedding_client() + GrowthIntegration.configure_embedding() + Kernel.set_embedding_client() + viking_configure_embedding 传播到 Kernel - A2: Skill 路由替换 new_tf_idf_only() 为 EmbeddingAdapter + LlmSkillFallback B线 自学习自动化: - B1: evolution_bridge.rs — candidate_to_manifest() (PromptOnly, disabled by default) - B2: Kernel::generate_and_register_skill() 全链路 (LLM→parse→QualityGate→manifest→persist) - B3: EvolutionMiddleware 双模式 (auto_mode 跳过注入, 留给 kernel 自动处理) - B4: QualityGate 加固 (body ≥100字符 + 必须含标题 + 置信度上限 1.0) 验证: 934 tests PASS, 0 failures
153 lines
5.5 KiB
Rust
153 lines
5.5 KiB
Rust
//! Skills management methods
|
|
|
|
use std::sync::Arc;
|
|
use zclaw_types::Result;
|
|
|
|
use super::Kernel;
|
|
|
|
impl Kernel {
|
|
/// Get the skills registry
|
|
pub fn skills(&self) -> &Arc<zclaw_skills::SkillRegistry> {
|
|
&self.skills
|
|
}
|
|
|
|
/// List all discovered skills
|
|
pub async fn list_skills(&self) -> Vec<zclaw_skills::SkillManifest> {
|
|
self.skills.list().await
|
|
}
|
|
|
|
/// Refresh skills from a directory
|
|
pub async fn refresh_skills(&self, dir: Option<std::path::PathBuf>) -> Result<()> {
|
|
if let Some(path) = dir {
|
|
self.skills.add_skill_dir(path).await?;
|
|
} else if let Some(ref skills_dir) = self.config.skills_dir {
|
|
self.skills.add_skill_dir(skills_dir.clone()).await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Get the configured skills directory
|
|
pub fn skills_dir(&self) -> Option<&std::path::PathBuf> {
|
|
self.config.skills_dir.as_ref()
|
|
}
|
|
|
|
/// Create a new skill in the skills directory
|
|
pub async fn create_skill(&self, manifest: zclaw_skills::SkillManifest) -> Result<()> {
|
|
let skills_dir = self.config.skills_dir.as_ref()
|
|
.ok_or_else(|| zclaw_types::ZclawError::InvalidInput(
|
|
"Skills directory not configured".into()
|
|
))?;
|
|
self.skills.create_skill(skills_dir, manifest).await
|
|
}
|
|
|
|
/// Update an existing skill
|
|
pub async fn update_skill(
|
|
&self,
|
|
id: &zclaw_types::SkillId,
|
|
manifest: zclaw_skills::SkillManifest,
|
|
) -> Result<zclaw_skills::SkillManifest> {
|
|
let skills_dir = self.config.skills_dir.as_ref()
|
|
.ok_or_else(|| zclaw_types::ZclawError::InvalidInput(
|
|
"Skills directory not configured".into()
|
|
))?;
|
|
self.skills.update_skill(skills_dir, id, manifest).await
|
|
}
|
|
|
|
/// Delete a skill
|
|
pub async fn delete_skill(&self, id: &zclaw_types::SkillId) -> Result<()> {
|
|
let skills_dir = self.config.skills_dir.as_ref()
|
|
.ok_or_else(|| zclaw_types::ZclawError::InvalidInput(
|
|
"Skills directory not configured".into()
|
|
))?;
|
|
self.skills.delete_skill(skills_dir, id).await
|
|
}
|
|
|
|
/// Execute a skill with the given ID and input
|
|
pub async fn execute_skill(
|
|
&self,
|
|
id: &str,
|
|
context: zclaw_skills::SkillContext,
|
|
input: serde_json::Value,
|
|
) -> Result<zclaw_skills::SkillResult> {
|
|
// Inject LLM completer into context for PromptOnly skills
|
|
let mut ctx = context;
|
|
if ctx.llm.is_none() {
|
|
ctx.llm = Some(self.llm_completer.clone());
|
|
}
|
|
self.skills.execute(&zclaw_types::SkillId::new(id), &ctx, input).await
|
|
}
|
|
|
|
/// Generate a skill from an aggregated pattern and register it.
|
|
///
|
|
/// Full pipeline:
|
|
/// 1. Build LLM prompt from pattern
|
|
/// 2. Call LLM to get JSON response
|
|
/// 3. Parse response into SkillCandidate
|
|
/// 4. Validate through QualityGate (threshold 0.85 for auto-mode)
|
|
/// 5. Convert to SkillManifest (PromptOnly, disabled by default)
|
|
/// 6. Persist to disk via SkillRegistry
|
|
pub async fn generate_and_register_skill(
|
|
&self,
|
|
pattern: &zclaw_growth::pattern_aggregator::AggregatedPattern,
|
|
) -> Result<String> {
|
|
// 1. Build prompt
|
|
let prompt = zclaw_growth::skill_generator::SkillGenerator::build_prompt(pattern);
|
|
|
|
// 2. Call LLM
|
|
let request = zclaw_runtime::driver::CompletionRequest {
|
|
model: self.driver.provider().to_string(),
|
|
system: Some("你是技能设计专家,只返回 JSON 格式的技能定义。".to_string()),
|
|
messages: vec![zclaw_types::Message::user(prompt)],
|
|
max_tokens: Some(1024),
|
|
temperature: Some(0.3),
|
|
stream: false,
|
|
..Default::default()
|
|
};
|
|
|
|
let response = self.driver.complete(request).await?;
|
|
let text = response.content.iter()
|
|
.filter_map(|block| match block {
|
|
zclaw_runtime::driver::ContentBlock::Text { text } => Some(text.as_str()),
|
|
_ => None,
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.join("");
|
|
|
|
// 3. Parse into SkillCandidate
|
|
let candidate = zclaw_growth::skill_generator::SkillGenerator::parse_response(
|
|
&text, pattern,
|
|
)?;
|
|
|
|
// 4. Validate through QualityGate (higher threshold for auto-generation)
|
|
let existing_triggers: Vec<String> = self.skills.list().await
|
|
.into_iter()
|
|
.flat_map(|m| m.triggers)
|
|
.collect();
|
|
let gate = zclaw_growth::quality_gate::QualityGate::new(0.85, existing_triggers);
|
|
let report = gate.validate_skill(&candidate);
|
|
if !report.passed {
|
|
return Err(zclaw_types::ZclawError::ConfigError(format!(
|
|
"QualityGate rejected: {}", report.issues.join("; ")
|
|
)));
|
|
}
|
|
|
|
// 5. Convert to SkillManifest (PromptOnly, disabled)
|
|
let manifest = super::evolution_bridge::candidate_to_manifest(&candidate);
|
|
let skill_id = manifest.id.to_string();
|
|
|
|
// 6. Persist to disk
|
|
let skills_dir = self.config.skills_dir.as_ref()
|
|
.ok_or_else(|| zclaw_types::ZclawError::InvalidInput(
|
|
"Skills directory not configured".into()
|
|
))?;
|
|
self.skills.create_skill(skills_dir, manifest).await?;
|
|
|
|
tracing::info!(
|
|
"[Kernel] Auto-generated skill '{}' (id={}) registered (disabled)",
|
|
candidate.name, skill_id
|
|
);
|
|
|
|
Ok(skill_id)
|
|
}
|
|
}
|