运行防护与配置
本页覆盖运行时护栏、会话修复逻辑、安全相关配置以及依赖选择。
包含的主题
- 循环守卫
- 会话修复
- 安全配置
- 安全相关依赖
循环守卫
源码: librefang-runtime/src/loop_guard.rs
LoopGuard 追踪单次 Agent 循环执行中的工具调用,以检测 Agent 是否
陷入重复调用同一工具的状态。
配置
pub struct LoopGuardConfig {
pub warn_threshold: u32, // Default: 3
pub block_threshold: u32, // Default: 5
pub global_circuit_breaker: u32, // Default: 30
}
检测算法
- 对每次工具调用,计算
tool_name + "|" + serialized_params的 SHA-256 哈希。 - 在
HashMap<String, u32>中递增该哈希的计数。 - 递增
total_calls。 - 返回分级裁决:
pub fn check(&mut self, tool_name: &str, params: &serde_json::Value) -> LoopGuardVerdict {
self.total_calls += 1;
// Global circuit breaker
if self.total_calls > self.config.global_circuit_breaker {
return LoopGuardVerdict::CircuitBreak(/* ... */);
}
let hash = Self::compute_hash(tool_name, params);
let count = self.call_counts.entry(hash).or_insert(0);
*count += 1;
if *count >= self.config.block_threshold {
LoopGuardVerdict::Block(/* ... */)
} else if *count >= self.config.warn_threshold {
LoopGuardVerdict::Warn(/* ... */)
} else {
LoopGuardVerdict::Allow
}
}
裁决类型
| 裁决 | 含义 | 动作 |
|---|---|---|
Allow | 正常操作 | 运行工具 |
Warn(msg) | 同一调用重复 >= 3 次 | 运行,并在结果中附加警告 |
Block(msg) | 同一调用重复 >= 5 次 | 跳过执行,返回错误 |
CircuitBreak(msg) | 总调用次数 > 30 | 终止整个 Agent 循环 |
哈希计算
fn compute_hash(tool_name: &str, params: &serde_json::Value) -> String {
let mut hasher = Sha256::new();
hasher.update(tool_name.as_bytes());
hasher.update(b"|");
let params_str = serde_json::to_string(params).unwrap_or_default();
hasher.update(params_str.as_bytes());
hex::encode(hasher.finalize())
}
注意:serde_json::to_string 生成确定性输出(对象键已排序),确保
语义相同的参数产生相同的哈希值。
关键特性
使用不同参数的调用会被分别追踪。一个 Agent 使用 10 个不同查询调用
web_search 不会触发守卫,但使用 web_search({"query": "test"}) 调用
5 次将被阻止。
会话修复
源码: librefang-runtime/src/session_repair.rs
在将消息历史发送给 LLM 之前,本模块会验证并修复常见的结构性问题, 避免 API 错误。
三阶段修复
pub fn validate_and_repair(messages: &[Message]) -> Vec<Message>
阶段 1 -- 收集 ToolUse ID:
扫描所有消息中的 ContentBlock::ToolUse { id, .. } 块,将其 ID 收集
到 HashSet<String> 中。
阶段 2 -- 过滤孤立条目和空消息:
- 孤立的 ToolResult:
ContentBlock::ToolResult { tool_use_id, .. }块中tool_use_id不在 ToolUse ID 集合中的会被丢弃。 - 空消息: 文本为空或没有内容块的消息会被丢弃。
阶段 3 -- 合并连续的同角色消息:
Anthropic API 要求严格的角色交替(user、assistant、user、 assistant...)。如果两条连续消息具有相同的角色,它们会被合并为一条 包含组合内容块的消息。
每种修复的必要性
| 问题 | 原因 | 不修复的后果 |
|---|---|---|
| 孤立的 ToolResult | 压缩或截断删除了 ToolUse | API 错误:"tool_use_id not found" |
| 空消息 | 取消的生成、空的用户提交 | API 错误:内容为空 |
| 连续的同角色消息 | 手动编辑历史、会话修复本身 | API 错误:角色交替违规 |
内容合并
合并连续的同角色消息时,两条消息都被转换为块格式并拼接:
fn merge_content(dst: &mut MessageContent, src: MessageContent) {
let dst_blocks = content_to_blocks(std::mem::replace(dst, MessageContent::Text(String::new())));
let src_blocks = content_to_blocks(src);
let mut combined = dst_blocks;
combined.extend(src_blocks);
*dst = MessageContent::Blocks(combined);
}
安全配置
config.toml 参考
# API Authentication
api_key = "your-secret-api-key" # Empty = localhost-only mode
# OFP Wire Protocol
[network]
shared_secret = "your-pre-shared-key" # Required for OFP
# WASM Sandbox
[sandbox]
fuel_limit = 1000000 # CPU instruction budget per execution
timeout_secs = 30 # Wall-clock timeout per execution
max_memory_bytes = 16777216 # 16 MB max WASM memory
# Rate Limiting
# 500 tokens/minute/IP (not currently configurable via config.toml)
# Web Search SSRF Protection
[web]
# SSRF protection is always on and cannot be disabled
密钥相关环境变量
| 变量 | 用途 |
|---|---|
OPENAI_API_KEY | OpenAI 兼容驱动 |
ANTHROPIC_API_KEY | Anthropic 驱动 |
GEMINI_API_KEY 或 GOOGLE_API_KEY | Gemini 驱动 |
DEEPSEEK_API_KEY | DeepSeek 提供商 |
GROQ_API_KEY | Groq 提供商 |
BRAVE_API_KEY | Brave 网络搜索 |
TAVILY_API_KEY | Tavily 网络搜索 |
PERPLEXITY_API_KEY | Perplexity 网络搜索 |
所有环境变量中的 API 密钥在加载到驱动结构体时都会被包装为
Zeroizing<String>。
能力声明(Agent 清单)
能力在 Agent 的 TOML 清单中声明:
[agent]
name = "my-agent"
[[capabilities]]
type = "FileRead"
value = "/data/*"
[[capabilities]]
type = "NetConnect"
value = "*.openai.com:443"
[[capabilities]]
type = "ToolInvoke"
value = "web_search"
[[capabilities]]
type = "LlmMaxTokens"
value = 4096
循环守卫调优
LoopGuardConfig 的默认值:
| 参数 | 默认值 | 描述 |
|---|---|---|
warn_threshold | 3 | 相同调用多少次后发出警告 |
block_threshold | 5 | 相同调用多少次后阻止 |
global_circuit_breaker | 30 | 总调用多少次后熔断 |
子进程沙箱白名单
要将特定环境变量传递给子进程:
sandbox_command(&mut cmd, &["MY_CUSTOM_VAR".to_string()]);
只有明确列在 allowed_env_vars 中的变量(加上安全默认值)才会被
子进程继承。
安全相关依赖
| Crate | 用途 |
|---|---|
sha2 | SHA-256 哈希(审计追踪、循环守卫、SSRF、校验和) |
hmac | HMAC-SHA256 用于 OFP 认证 |
hex | 哈希和签名的十六进制编解码 |
subtle | 常数时间比较(ConstantTimeEq)用于 HMAC 验证 |
ed25519-dalek | Ed25519 签名/验证用于清单签名 |
rand | 密码学安全随机数生成器用于密钥生成(OsRng) |
zeroize | Zeroizing<T> 包装器用于自动密钥内存擦除 |
governor | GCRA 速率限制算法 |
wasmtime | WASM 沙箱,带燃料 + 纪元计量 |
uuid | OFP 握手的 nonce 生成 |
chrono | 审计条目的 ISO-8601 时间戳 |
reqwest | HTTP 客户端(在受 SSRF 保护的 host_net_fetch 中使用) |
为什么选择这些 Crate
- sha2/hmac: 属于 RustCrypto 项目,经过审计,在生产环境的 Rust 项目中广泛使用。
- ed25519-dalek: Rust 生态中事实上的 Ed25519 标准库,经过广泛审计。
- subtle: 提供常数时间操作以防止时序侧信道攻击。
- zeroize: RustCrypto 官方的密钥零化方案,与
Droptrait 集成。 - governor: 久经考验的 GCRA 实现,使用
DashMap支持的并发状态。