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:
@@ -86,6 +86,32 @@ impl SkillExecutor for KernelSkillExecutor {
|
||||
let result = self.skills.execute(&zclaw_types::SkillId::new(skill_id), &context, input).await?;
|
||||
Ok(result.output)
|
||||
}
|
||||
|
||||
fn get_skill_detail(&self, skill_id: &str) -> Option<zclaw_runtime::tool::SkillDetail> {
|
||||
let manifests = self.skills.manifests_snapshot();
|
||||
let manifest = manifests.get(&zclaw_types::SkillId::new(skill_id))?;
|
||||
Some(zclaw_runtime::tool::SkillDetail {
|
||||
id: manifest.id.as_str().to_string(),
|
||||
name: manifest.name.clone(),
|
||||
description: manifest.description.clone(),
|
||||
category: manifest.category.clone(),
|
||||
input_schema: manifest.input_schema.clone(),
|
||||
triggers: manifest.triggers.clone(),
|
||||
capabilities: manifest.capabilities.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_skill_index(&self) -> Vec<zclaw_runtime::tool::SkillIndexEntry> {
|
||||
let manifests = self.skills.manifests_snapshot();
|
||||
manifests.values()
|
||||
.filter(|m| m.enabled)
|
||||
.map(|m| zclaw_runtime::tool::SkillIndexEntry {
|
||||
id: m.id.as_str().to_string(),
|
||||
description: m.description.clone(),
|
||||
triggers: m.triggers.clone(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// The ZCLAW Kernel
|
||||
@@ -205,6 +231,68 @@ impl Kernel {
|
||||
tools
|
||||
}
|
||||
|
||||
/// Create the middleware chain for the agent loop.
|
||||
///
|
||||
/// When middleware is configured, cross-cutting concerns (compaction, loop guard,
|
||||
/// token calibration, etc.) are delegated to the chain. When no middleware is
|
||||
/// registered, the legacy inline path in `AgentLoop` is used instead.
|
||||
fn create_middleware_chain(&self) -> Option<zclaw_runtime::middleware::MiddlewareChain> {
|
||||
let mut chain = zclaw_runtime::middleware::MiddlewareChain::new();
|
||||
|
||||
// Compaction middleware — only register when threshold > 0
|
||||
let threshold = self.config.compaction_threshold();
|
||||
if threshold > 0 {
|
||||
use std::sync::Arc;
|
||||
let mw = zclaw_runtime::middleware::compaction::CompactionMiddleware::new(
|
||||
threshold,
|
||||
zclaw_runtime::CompactionConfig::default(),
|
||||
Some(self.driver.clone()),
|
||||
None, // growth not wired in kernel yet
|
||||
);
|
||||
chain.register(Arc::new(mw));
|
||||
}
|
||||
|
||||
// Loop guard middleware
|
||||
{
|
||||
use std::sync::Arc;
|
||||
let mw = zclaw_runtime::middleware::loop_guard::LoopGuardMiddleware::with_defaults();
|
||||
chain.register(Arc::new(mw));
|
||||
}
|
||||
|
||||
// Token calibration middleware
|
||||
{
|
||||
use std::sync::Arc;
|
||||
let mw = zclaw_runtime::middleware::token_calibration::TokenCalibrationMiddleware::new();
|
||||
chain.register(Arc::new(mw));
|
||||
}
|
||||
|
||||
// Skill index middleware — inject lightweight index instead of full descriptions
|
||||
{
|
||||
use std::sync::Arc;
|
||||
let entries = self.skill_executor.list_skill_index();
|
||||
if !entries.is_empty() {
|
||||
let mw = zclaw_runtime::middleware::skill_index::SkillIndexMiddleware::new(entries);
|
||||
chain.register(Arc::new(mw));
|
||||
}
|
||||
}
|
||||
|
||||
// Guardrail middleware — safety rules for tool calls
|
||||
{
|
||||
use std::sync::Arc;
|
||||
let mw = zclaw_runtime::middleware::guardrail::GuardrailMiddleware::new(true)
|
||||
.with_builtin_rules();
|
||||
chain.register(Arc::new(mw));
|
||||
}
|
||||
|
||||
// Only return Some if we actually registered middleware
|
||||
if chain.is_empty() {
|
||||
None
|
||||
} else {
|
||||
tracing::info!("[Kernel] Middleware chain created with {} middlewares", chain.len());
|
||||
Some(chain)
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a system prompt with skill information injected
|
||||
async fn build_system_prompt_with_skills(&self, base_prompt: Option<&String>) -> String {
|
||||
// Get skill list asynchronously
|
||||
@@ -417,6 +505,11 @@ impl Kernel {
|
||||
loop_runner = loop_runner.with_path_validator(path_validator);
|
||||
}
|
||||
|
||||
// Inject middleware chain if available
|
||||
if let Some(chain) = self.create_middleware_chain() {
|
||||
loop_runner = loop_runner.with_middleware_chain(chain);
|
||||
}
|
||||
|
||||
// Build system prompt with skill information injected
|
||||
let system_prompt = self.build_system_prompt_with_skills(agent_config.system_prompt.as_ref()).await;
|
||||
let loop_runner = loop_runner.with_system_prompt(&system_prompt);
|
||||
@@ -501,6 +594,11 @@ impl Kernel {
|
||||
loop_runner = loop_runner.with_path_validator(path_validator);
|
||||
}
|
||||
|
||||
// Inject middleware chain if available
|
||||
if let Some(chain) = self.create_middleware_chain() {
|
||||
loop_runner = loop_runner.with_middleware_chain(chain);
|
||||
}
|
||||
|
||||
// Use external prompt if provided, otherwise build default
|
||||
let system_prompt = match system_prompt_override {
|
||||
Some(prompt) => prompt,
|
||||
|
||||
Reference in New Issue
Block a user