feat(runtime): DeerFlow 模式中间件链 Phase 1-4 全部完成
借鉴 DeerFlow 架构,实现完整中间件链系统: Phase 1 - Agent 中间件链基础设施 - MiddlewareChain Clone 支持 - LoopRunner 双路径集成 (middleware/legacy) - Kernel create_middleware_chain() 工厂方法 Phase 2 - 技能按需注入 - SkillIndexMiddleware (priority 200) - SkillLoadTool 工具 - SkillDetail/SkillIndexEntry 结构体 - KernelSkillExecutor trait 扩展 Phase 3 - Guardrail 安全护栏 - GuardrailMiddleware (priority 400, fail_open) - ShellExecRule / FileWriteRule / WebFetchRule Phase 4 - 记忆闭环统一 - MemoryMiddleware (priority 150, 30s 防抖) - after_completion 双路径调用 中间件注册顺序: 100 Compaction | 150 Memory | 200 SkillIndex 400 Guardrail | 500 LoopGuard | 700 TokenCalibration 向后兼容:Option<MiddlewareChain> 默认 None 走旧路径
This commit is contained in:
@@ -5,6 +5,7 @@ mod file_write;
|
||||
mod shell_exec;
|
||||
mod web_fetch;
|
||||
mod execute_skill;
|
||||
mod skill_load;
|
||||
mod path_validator;
|
||||
|
||||
pub use file_read::FileReadTool;
|
||||
@@ -12,6 +13,7 @@ pub use file_write::FileWriteTool;
|
||||
pub use shell_exec::ShellExecTool;
|
||||
pub use web_fetch::WebFetchTool;
|
||||
pub use execute_skill::ExecuteSkillTool;
|
||||
pub use skill_load::SkillLoadTool;
|
||||
pub use path_validator::{PathValidator, PathValidatorConfig};
|
||||
|
||||
use crate::tool::ToolRegistry;
|
||||
@@ -23,4 +25,5 @@ pub fn register_builtin_tools(registry: &mut ToolRegistry) {
|
||||
registry.register(Box::new(ShellExecTool::new()));
|
||||
registry.register(Box::new(WebFetchTool::new()));
|
||||
registry.register(Box::new(ExecuteSkillTool::new()));
|
||||
registry.register(Box::new(SkillLoadTool::new()));
|
||||
}
|
||||
|
||||
81
crates/zclaw-runtime/src/tool/builtin/skill_load.rs
Normal file
81
crates/zclaw-runtime/src/tool/builtin/skill_load.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
//! Skill load tool — on-demand retrieval of full skill details.
|
||||
//!
|
||||
//! When the `SkillIndexMiddleware` is active, the system prompt contains only a lightweight
|
||||
//! skill index. This tool allows the LLM to load full skill details (description, input schema,
|
||||
//! capabilities) on demand, exactly when the LLM decides a particular skill is relevant.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{json, Value};
|
||||
use zclaw_types::{Result, ZclawError};
|
||||
|
||||
use crate::tool::{Tool, ToolContext};
|
||||
|
||||
pub struct SkillLoadTool;
|
||||
|
||||
impl SkillLoadTool {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Tool for SkillLoadTool {
|
||||
fn name(&self) -> &str {
|
||||
"skill_load"
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Load full details for a skill by its ID. Use this when you need to understand a skill's \
|
||||
input parameters, capabilities, or usage instructions before calling execute_skill. \
|
||||
Returns the skill description, input schema, and trigger conditions."
|
||||
}
|
||||
|
||||
fn input_schema(&self) -> Value {
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skill_id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the skill to load details for"
|
||||
}
|
||||
},
|
||||
"required": ["skill_id"]
|
||||
})
|
||||
}
|
||||
|
||||
async fn execute(&self, input: Value, context: &ToolContext) -> Result<Value> {
|
||||
let skill_id = input["skill_id"].as_str()
|
||||
.ok_or_else(|| ZclawError::InvalidInput("Missing 'skill_id' parameter".into()))?;
|
||||
|
||||
let executor = context.skill_executor.as_ref()
|
||||
.ok_or_else(|| ZclawError::ToolError("Skill executor not available".into()))?;
|
||||
|
||||
match executor.get_skill_detail(skill_id) {
|
||||
Some(detail) => {
|
||||
let mut result = json!({
|
||||
"id": detail.id,
|
||||
"name": detail.name,
|
||||
"description": detail.description,
|
||||
"triggers": detail.triggers,
|
||||
});
|
||||
if let Some(schema) = &detail.input_schema {
|
||||
result["input_schema"] = schema.clone();
|
||||
}
|
||||
if let Some(cat) = &detail.category {
|
||||
result["category"] = json!(cat);
|
||||
}
|
||||
if !detail.capabilities.is_empty() {
|
||||
result["capabilities"] = json!(detail.capabilities);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
None => Err(ZclawError::ToolError(format!("Skill not found: {}", skill_id))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SkillLoadTool {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user