8.1 WorkspaceScope 工作区范围
概述
`WorkspaceScope` 是 RAF 工作区管理的核心数据结构。它定义了 Agent 的**操作边界**——Agent 可以在哪些路径下自由读写文件、执行命令,在哪些路径下需要审批或被禁止。
// crates/core/src/workspace.rs
pub struct WorkspaceScope {
/// 规范化的根路径
pub root: PathBuf,
/// 可读名称,注入 system prompt
pub name: String,
/// 越界处理策略
pub policy: ScopePolicy,
/// 扩展属性——路径白名单、命令白名单、环境变量等
pub properties: HashMap<String, serde_json::Value>,
}
为什么需要工作区管理
安全问题
Agent 通常被赋予文件系统访问和命令执行能力。如果不加限制,恶意或错误的提示词可能导致:
- 读取敏感文件(`/etc/passwd`、`.env`、SSH 私钥) - 覆盖或删除关键系统文件 - 执行危险的系统命令(`rm -rf /`、`curl | sh`) - 在工作区外创建或修改文件
RAF 的方案
RAF 通过三层防护实现工作区安全:
Layer 1: WorkspaceScope 定义边界
↓
Layer 2: path_guard.rs 在工具执行时检测越界
↓
Layer 3: ScopePolicy + ApprovalRequiredTool 处理越界
graph TB
subgraph "工作区安全模型"
A[WorkspaceScope<br/>root + name + policy + properties]
B[ScopePolicy<br/>AllowAll / ApproveOutside / DenyOutside]
C[IScopeTool<br/>工具自动感知 scope]
D[path_guard<br/>resolve_safe / resolve_safe_new]
E[ApprovalRequiredTool<br/>跨范围审批包装]
end
A --> B
B -->|ApproveOutside| E
B -->|DenyOutside| D
C --> D
D -->|ScopeStatus::OutsideScope| E
E -->|触发审批| HITL[HITL 审批流程]
ScopePolicy 三种策略
`ScopePolicy` 是一个枚举,定义了三种越界处理策略:
pub enum ScopePolicy {
/// 开发模式——不作任何限制
AllowAll,
/// 生产模式——跨范围操作需人机协同审批
ApproveOutside,
/// 受限模式——禁止任何跨范围访问
DenyOutside,
}
策略对比
| 策略 | 工作区内操作 | 跨范围读操作 | 跨范围写/删除操作 | 适用场景 | |------|-------------|-------------|------------------|---------| | `AllowAll` | 直接执行 | 直接执行 | 直接执行 | 本地开发、受信任环境 | | `ApproveOutside` | 直接执行 | 需审批 | 需审批 | 生产环境、需要审计 | | `DenyOutside` | 直接执行 | 工具级拒绝 | 工具级拒绝 | 沙箱环境、CI/CD |
从配置字符串解析策略
`ScopePolicy::from_config_str` 将 YAML/JSON/TOML 中的策略字符串统一解析为 `ScopePolicy` 枚举值。未知值会回退为 `DenyOutside`(安全优先,fail closed),并记录 `ERROR` 级别日志。
/// 从配置字符串解析策略。未知值返回 `DenyOutside`(安全优先)。
///
/// 支持的别名:
/// allow_all / allow / read → AllowAll
/// approve_outside / approve / ask → ApproveOutside
/// deny_outside / deny / restrict → DenyOutside
pub fn from_config_str(s: &str) -> Self {
match s {
"allow_all" | "allow" | "read" => ScopePolicy::AllowAll,
"approve_outside" | "approve" | "ask" => ScopePolicy::ApproveOutside,
"deny_outside" | "deny" | "restrict" => ScopePolicy::DenyOutside,
_ => ScopePolicy::DenyOutside,
}
}
| 配置字符串 | 别名 | 解析结果 | |-----------|------|---------| | `"read"` | `"allow"`, `"allow_all"` | `ScopePolicy::AllowAll` | | `"approve"` | `"ask"`, `"approve_outside"` | `ScopePolicy::ApproveOutside` | | `"deny"` | `"restrict"`, `"deny_outside"` | `ScopePolicy::DenyOutside` | | 其他任何值 | — | `ScopePolicy::DenyOutside`(fail closed) |
AllowAll
不限制 Agent 的操作范围。工具在工作区内外的操作都直接执行。适用于开发环境和完全受信任的场景。
let scope = WorkspaceScope::new("/project", "dev-workspace")
.with_policy(ScopePolicy::AllowAll);
ApproveOutside
Agent 在工作区内的操作直接执行,工作区外的操作触发审批流程。这是推荐的**生产环境默认策略**。
let scope = WorkspaceScope::new("/var/app", "production-app")
.with_policy(ScopePolicy::ApproveOutside);
// 搭配 WorkspaceContextProvider,工具被自动包装为 ApprovalRequiredTool
let provider = WorkspaceContextProvider::new(Arc::new(scope))
.add_tool(ReadFile { scope: None })
.add_tool(WriteFile { scope: None });
DenyOutside
严格限制。Agent 只能在定义的工作区范围内操作,任何越界尝试在工具级别被拒绝。适用于高度受控的沙箱环境。
let scope = WorkspaceScope::new("/sandbox", "restricted-sandbox")
.with_policy(ScopePolicy::DenyOutside);
扩展属性
`WorkspaceScope.properties` 是一个 `HashMap<String, serde_json::Value>`,支持存储任意扩展属性:
let scope = WorkspaceScope::new("/project", "my-project")
.with_policy(ScopePolicy::ApproveOutside)
.with_property("allowed_commands", serde_json::json!(["ls", "cat", "grep"]))
.with_property("max_file_size_mb", serde_json::json!(10))
.with_property("environment", serde_json::json!({
"NODE_ENV": "production",
"PATH": "/usr/local/bin:/usr/bin"
}));
这些属性可以在 `RunCommand` 等工具中读取,用于实现命令白名单、文件大小限制等功能。
IScopeTool:工作区感知工具
`IScopeTool` 是一个 trait,扩展了 `ITool`,使工具能够接受 `WorkspaceScope` 的注入:
pub trait IScopeTool: ITool {
/// 使用指定工作区范围创建工具的新实例。
fn create_scoped(&self, scope: Arc<WorkspaceScope>) -> Arc<dyn ITool>;
}
为什么需要 IScopeTool
- **关注点分离**:工具不需要在构造函数中关心工作区范围,`WorkspaceContextProvider` 自动注入。 - **编译期保证**:通过 Rust 的 trait 系统,只有实现了 `IScopeTool` 的工具才能被工作区上下文提供器管理。 - **运行时多态**:返回 `Arc<dyn ITool>` 允许工具注入 scope 后仍可通过统一的 `ITool` 接口使用。
RAF 内置的 IScopeTool 实现
所有文件系统工具和 `RunCommand` 都实现了 `IScopeTool`:
| 工具 | scope 用途 | |------|-----------| | `ReadFile` | 读取路径安全校验 | | `WriteFile` | 写入路径安全校验 | | `EditFile` | 编辑路径安全校验 | | `ListFiles` | 列出目录安全校验 | | `InspectFile` | 文件检查安全校验 | | `MakeDirectory` | 创建目录安全校验 | | `RemovePath` | 删除路径安全校验 | | `MoveFile` | 移动路径安全校验(源 + 目标) | | `FindFiles` | 搜索路径安全校验 | | `SearchFile` | 内容搜索路径安全校验 | | `RunCommand` | 命令执行 + 路径上下文 |
配置示例
开发环境
let scope = WorkspaceScope::new(".", "local-dev")
.with_policy(ScopePolicy::AllowAll);
生产环境(单应用)
let scope = WorkspaceScope::new("/opt/myapp", "myapp-prod")
.with_policy(ScopePolicy::ApproveOutside)
.with_property("allowed_commands", serde_json::json!([
"ls", "cat", "head", "tail", "wc", "grep", "find",
"node", "npm", "git"
]));
沙箱环境
let scope = WorkspaceScope::new("/tmp/agent-sandbox-42", "sandbox-42")
.with_policy(ScopePolicy::DenyOutside)
.with_property("max_file_size_mb", 5)
.with_property("max_files", 100);
在 system prompt 中的表现
`WorkspaceContextProvider::build_instructions()` 将工作区信息注入 system prompt:
## 工作区
名称: my-project
根路径: /home/user/project
越界策略: 跨范围审批(工作区外的操作需用户审批后方可执行)
- 相对路径在工作区内解析
- 绝对路径若在工作区外,工具返回中 scope 字段会标明 outside_workspace
- 每个工具返回均包含 scope 字段以标明操作范围
这告知 LLM 其操作边界,帮助它做出合理的路径选择。