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
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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user