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(loop_guard): 为LoopGuard添加Clone派生
feat(capabilities): 实现CapabilityManager.validate()安全验证
fix(agentStore): 添加token用量追踪
chore: 删除未实现的Predictor/Lead HAND.toml文件
style(Credits): 移除假数据并标注开发中状态
refactor(Skills): 动态加载技能卡片
perf(configStore): 为定时任务添加localStorage降级
docs: 更新功能文档和版本变更记录
This commit is contained in:
iven
2026-03-27 07:56:53 +08:00
parent 0d4fa96b82
commit eed347e1a6
14 changed files with 724 additions and 476 deletions

View File

@@ -25,7 +25,7 @@ impl Default for LoopGuardConfig {
}
/// Loop guard state
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct LoopGuard {
config: LoopGuardConfig,
/// Hash of (tool_name, params) -> count

View File

@@ -1,6 +1,7 @@
//! Agent loop implementation
use std::sync::Arc;
use std::sync::Mutex;
use futures::StreamExt;
use tokio::sync::mpsc;
use zclaw_types::{AgentId, SessionId, Message, Result};
@@ -9,7 +10,7 @@ use crate::driver::{LlmDriver, CompletionRequest, ContentBlock};
use crate::stream::StreamChunk;
use crate::tool::{ToolRegistry, ToolContext, SkillExecutor};
use crate::tool::builtin::PathValidator;
use crate::loop_guard::LoopGuard;
use crate::loop_guard::{LoopGuard, LoopGuardResult};
use crate::growth::GrowthIntegration;
use crate::compaction;
use zclaw_memory::MemoryStore;
@@ -20,8 +21,7 @@ pub struct AgentLoop {
driver: Arc<dyn LlmDriver>,
tools: ToolRegistry,
memory: Arc<MemoryStore>,
#[allow(dead_code)] // Reserved for future rate limiting
loop_guard: LoopGuard,
loop_guard: Mutex<LoopGuard>,
model: String,
system_prompt: Option<String>,
max_tokens: u32,
@@ -46,7 +46,7 @@ impl AgentLoop {
driver,
tools,
memory,
loop_guard: LoopGuard::default(),
loop_guard: Mutex::new(LoopGuard::default()),
model: String::new(), // Must be set via with_model()
system_prompt: None,
max_tokens: 4096,
@@ -235,7 +235,28 @@ impl AgentLoop {
// Create tool context and execute all tools
let tool_context = self.create_tool_context(session_id.clone());
let mut circuit_breaker_triggered = false;
for (id, name, input) in tool_calls {
// Check loop guard before executing tool
let guard_result = self.loop_guard.lock().unwrap().check(&name, &input);
match guard_result {
LoopGuardResult::CircuitBreaker => {
tracing::warn!("[AgentLoop] Circuit breaker triggered by tool '{}'", name);
circuit_breaker_triggered = true;
break;
}
LoopGuardResult::Blocked => {
tracing::warn!("[AgentLoop] Tool '{}' blocked by loop guard", name);
let error_output = serde_json::json!({ "error": "工具调用被循环防护拦截" });
messages.push(Message::tool_result(id, zclaw_types::ToolId::new(&name), error_output, true));
continue;
}
LoopGuardResult::Warn => {
tracing::warn!("[AgentLoop] Tool '{}' triggered loop guard warning", name);
}
LoopGuardResult::Allowed => {}
}
let tool_result = match self.execute_tool(&name, input, &tool_context).await {
Ok(result) => result,
Err(e) => serde_json::json!({ "error": e.to_string() }),
@@ -251,6 +272,18 @@ impl AgentLoop {
}
// Continue the loop - LLM will process tool results and generate final response
// If circuit breaker was triggered, terminate immediately
if circuit_breaker_triggered {
let msg = "检测到工具调用循环,已自动终止";
self.memory.append_message(&session_id, &Message::assistant(msg)).await?;
break AgentLoopResult {
response: msg.to_string(),
input_tokens: total_input_tokens,
output_tokens: total_output_tokens,
iterations,
};
}
};
// Process conversation for memory extraction (post-conversation)
@@ -299,6 +332,7 @@ impl AgentLoop {
let memory = self.memory.clone();
let driver = self.driver.clone();
let tools = self.tools.clone();
let loop_guard_clone = self.loop_guard.lock().unwrap().clone();
let skill_executor = self.skill_executor.clone();
let path_validator = self.path_validator.clone();
let agent_id = self.agent_id.clone();
@@ -308,6 +342,7 @@ impl AgentLoop {
tokio::spawn(async move {
let mut messages = messages;
let loop_guard_clone = Mutex::new(loop_guard_clone);
let max_iterations = 10;
let mut iteration = 0;
let mut total_input_tokens = 0u32;
@@ -423,6 +458,26 @@ impl AgentLoop {
// Execute tools
for (id, name, input) in pending_tool_calls {
tracing::debug!("[AgentLoop] Executing tool: name={}, input={:?}", name, input);
// Check loop guard before executing tool
let guard_result = loop_guard_clone.lock().unwrap().check(&name, &input);
match guard_result {
LoopGuardResult::CircuitBreaker => {
let _ = tx.send(LoopEvent::Error("检测到工具调用循环,已自动终止".to_string())).await;
break 'outer;
}
LoopGuardResult::Blocked => {
tracing::warn!("[AgentLoop] Tool '{}' blocked by loop guard", name);
let error_output = serde_json::json!({ "error": "工具调用被循环防护拦截" });
let _ = tx.send(LoopEvent::ToolEnd { name: name.clone(), output: error_output.clone() }).await;
messages.push(Message::tool_result(id, zclaw_types::ToolId::new(&name), error_output, true));
continue;
}
LoopGuardResult::Warn => {
tracing::warn!("[AgentLoop] Tool '{}' triggered loop guard warning", name);
}
LoopGuardResult::Allowed => {}
}
let tool_context = ToolContext {
agent_id: agent_id.clone(),
working_directory: None,