feat: 新增技能编排引擎和工作流构建器组件
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

refactor: 统一Hands系统常量到单个源文件
refactor: 更新Hands中文名称和描述

fix: 修复技能市场在连接状态变化时重新加载
fix: 修复身份变更提案的错误处理逻辑

docs: 更新多个功能文档的验证状态和实现位置
docs: 更新Hands系统文档

test: 添加测试文件验证工作区路径
This commit is contained in:
iven
2026-03-25 08:27:25 +08:00
parent 9c781f5f2a
commit aa6a9cbd84
110 changed files with 12384 additions and 1337 deletions

View File

@@ -9,6 +9,7 @@ mod export;
mod http;
mod skill;
mod hand;
mod orchestration;
pub use llm::*;
pub use parallel::*;
@@ -17,6 +18,7 @@ pub use export::*;
pub use http::*;
pub use skill::*;
pub use hand::*;
pub use orchestration::*;
use std::collections::HashMap;
use std::sync::Arc;
@@ -57,6 +59,9 @@ pub enum ActionError {
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Orchestration error: {0}")]
Orchestration(String),
}
/// Action registry - holds references to all action executors
@@ -70,6 +75,9 @@ pub struct ActionRegistry {
/// Hand registry (injected from kernel)
hand_registry: Option<Arc<dyn HandActionDriver>>,
/// Orchestration driver (injected from kernel)
orchestration_driver: Option<Arc<dyn OrchestrationActionDriver>>,
/// Template directory
template_dir: Option<std::path::PathBuf>,
}
@@ -81,6 +89,7 @@ impl ActionRegistry {
llm_driver: None,
skill_registry: None,
hand_registry: None,
orchestration_driver: None,
template_dir: None,
}
}
@@ -103,6 +112,12 @@ impl ActionRegistry {
self
}
/// Set orchestration driver
pub fn with_orchestration_driver(mut self, driver: Arc<dyn OrchestrationActionDriver>) -> Self {
self.orchestration_driver = Some(driver);
self
}
/// Set template directory
pub fn with_template_dir(mut self, dir: std::path::PathBuf) -> Self {
self.template_dir = Some(dir);
@@ -166,6 +181,22 @@ impl ActionRegistry {
}
}
/// Execute a skill orchestration
pub async fn execute_orchestration(
&self,
graph_id: Option<&str>,
graph: Option<&Value>,
input: HashMap<String, Value>,
) -> Result<Value, ActionError> {
if let Some(driver) = &self.orchestration_driver {
driver.execute(graph_id, graph, input)
.await
.map_err(ActionError::Orchestration)
} else {
Err(ActionError::Orchestration("Orchestration driver not configured".to_string()))
}
}
/// Render classroom
pub async fn render_classroom(&self, data: &Value) -> Result<Value, ActionError> {
// This will integrate with the classroom renderer
@@ -377,3 +408,14 @@ pub trait HandActionDriver: Send + Sync {
params: HashMap<String, Value>,
) -> Result<Value, String>;
}
/// Orchestration action driver trait
#[async_trait]
pub trait OrchestrationActionDriver: Send + Sync {
async fn execute(
&self,
graph_id: Option<&str>,
graph: Option<&Value>,
input: HashMap<String, Value>,
) -> Result<Value, String>;
}

View File

@@ -0,0 +1,61 @@
//! Skill orchestration action
//!
//! Executes skill graphs (DAGs) with data passing and parallel execution.
use std::collections::HashMap;
use std::sync::Arc;
use serde_json::Value;
use async_trait::async_trait;
use super::OrchestrationActionDriver;
/// Orchestration driver that uses the skill orchestration engine
pub struct SkillOrchestrationDriver {
/// Skill registry for executing skills
skill_registry: Arc<zclaw_skills::SkillRegistry>,
}
impl SkillOrchestrationDriver {
/// Create a new orchestration driver
pub fn new(skill_registry: Arc<zclaw_skills::SkillRegistry>) -> Self {
Self { skill_registry }
}
}
#[async_trait]
impl OrchestrationActionDriver for SkillOrchestrationDriver {
async fn execute(
&self,
graph_id: Option<&str>,
graph: Option<&Value>,
input: HashMap<String, Value>,
) -> Result<Value, String> {
use zclaw_skills::orchestration::{SkillGraph, DefaultExecutor, SkillGraphExecutor};
// Load or parse the graph
let skill_graph = if let Some(graph_value) = graph {
// Parse inline graph definition
serde_json::from_value::<SkillGraph>(graph_value.clone())
.map_err(|e| format!("Failed to parse graph: {}", e))?
} else if let Some(id) = graph_id {
// Load graph from registry (TODO: implement graph storage)
return Err(format!("Graph loading by ID not yet implemented: {}", id));
} else {
return Err("Either graph_id or graph must be provided".to_string());
};
// Create executor
let executor = DefaultExecutor::new(self.skill_registry.clone());
// Create skill context with default values
let context = zclaw_skills::SkillContext::default();
// Execute the graph
let result = executor.execute(&skill_graph, input, &context)
.await
.map_err(|e| format!("Orchestration execution failed: {}", e))?;
// Return the output
Ok(result.output)
}
}

View File

@@ -281,6 +281,16 @@ impl PipelineExecutor {
tokio::time::sleep(tokio::time::Duration::from_millis(*ms)).await;
Ok(Value::Null)
}
Action::SkillOrchestration { graph_id, graph, input } => {
let resolved_input = context.resolve_map(input)?;
self.action_registry.execute_orchestration(
graph_id.as_deref(),
graph.as_ref(),
resolved_input,
).await
.map_err(|e| ExecuteError::Action(e.to_string()))
}
}
}.boxed()
}

View File

@@ -326,6 +326,19 @@ pub enum Action {
/// Duration in milliseconds
ms: u64,
},
/// Skill orchestration - execute multiple skills in a DAG
SkillOrchestration {
/// Graph ID (reference to a pre-defined graph) or inline definition
graph_id: Option<String>,
/// Inline graph definition (alternative to graph_id)
graph: Option<serde_json::Value>,
/// Input variables
#[serde(default)]
input: HashMap<String, String>,
},
}
fn default_http_method() -> String {