借鉴 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 走旧路径
63 lines
2.2 KiB
Rust
63 lines
2.2 KiB
Rust
//! Skill index middleware — injects a lightweight skill index into the system prompt.
|
|
//!
|
|
//! Instead of embedding full skill descriptions (which can consume ~2000 tokens for 70+ skills),
|
|
//! this middleware injects only skill IDs and one-line triggers (~600 tokens). The LLM can then
|
|
//! call the `skill_load` tool on demand to retrieve full skill details when needed.
|
|
|
|
use async_trait::async_trait;
|
|
use zclaw_types::Result;
|
|
use crate::middleware::{AgentMiddleware, MiddlewareContext, MiddlewareDecision};
|
|
use crate::tool::{SkillIndexEntry, SkillExecutor};
|
|
use std::sync::Arc;
|
|
|
|
/// Middleware that injects a lightweight skill index into the system prompt.
|
|
///
|
|
/// The index format is compact:
|
|
/// ```text
|
|
/// ## Skills (index — use skill_load for details)
|
|
/// - finance-tracker: 财务分析、财报解读 [数据分析]
|
|
/// - senior-developer: 代码开发、架构设计 [开发工程]
|
|
/// ```
|
|
pub struct SkillIndexMiddleware {
|
|
/// Pre-built skill index entries, constructed at chain creation time.
|
|
entries: Vec<SkillIndexEntry>,
|
|
}
|
|
|
|
impl SkillIndexMiddleware {
|
|
pub fn new(entries: Vec<SkillIndexEntry>) -> Self {
|
|
Self { entries }
|
|
}
|
|
|
|
/// Build index entries from a skill executor that supports listing.
|
|
pub fn from_executor(executor: &Arc<dyn SkillExecutor>) -> Self {
|
|
Self {
|
|
entries: executor.list_skill_index(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl AgentMiddleware for SkillIndexMiddleware {
|
|
fn name(&self) -> &str { "skill_index" }
|
|
fn priority(&self) -> i32 { 200 }
|
|
|
|
async fn before_completion(&self, ctx: &mut MiddlewareContext) -> Result<MiddlewareDecision> {
|
|
if self.entries.is_empty() {
|
|
return Ok(MiddlewareDecision::Continue);
|
|
}
|
|
|
|
let mut index = String::from("\n\n## Skills (index — call skill_load for details)\n\n");
|
|
for entry in &self.entries {
|
|
let triggers = if entry.triggers.is_empty() {
|
|
String::new()
|
|
} else {
|
|
format!(" — {}", entry.triggers.join(", "))
|
|
};
|
|
index.push_str(&format!("- **{}**: {}{}\n", entry.id, entry.description, triggers));
|
|
}
|
|
|
|
ctx.system_prompt.push_str(&index);
|
|
Ok(MiddlewareDecision::Continue)
|
|
}
|
|
}
|