1. 模块概述
本章涵盖两个基础设施模块:
- **Config 系统**(
nanobot/config/):基于 Pydantic v2 的配置管理,支持 JSON 配置文件、环境变量覆盖和 camelCase 别名 - **Security 系统**(
nanobot/security/):SSRF 防护、工作区访问控制和网络安全策略
1.1 Config 系统架构
graph TB
subgraph "配置来源"
JSON["~/.nanobot/config.json"]
ENV["环境变量覆盖"]
end
subgraph "Config Schema (Pydantic)"
CFG[Config]
AGT[AgentDefaults]
CH[ChannelsConfig]
PR[ProviderConfig]
TOOL[ToolsConfig]
DR[DreamConfig]
TR[TranscriptionConfig]
end
subgraph "配置消费"
AL[AgentLoop.from_config]
CM[ChannelManager]
PF[ProviderFactory]
end
JSON --> CFG
ENV --> CFG
CFG --> AGT
CFG --> CH
CFG --> PR
CFG --> TOOL
AGT --> AL
CH --> CM
PR --> PF
1.2 Security 系统架构
graph TB
subgraph "安全层"
SSRF[SSRF Guard]
WS[Workspace Access Control]
PTH[PTH File Guard]
end
subgraph "执行点"
WEB[Web Tools
web_fetch/web_search]
FS[FileSystem Tools
read_file/write_file]
SH[Shell Tools
exec]
end
SSRF -->|blocks private URLs| WEB
WS -->|restricts paths| FS
WS -->|restricts paths| SH
2. Config 系统详解
2.1 配置加载流程
sequenceDiagram
participant CLI as CLI Entry
participant Loader as Config Loader
participant Schema as Pydantic Schema
participant File as config.json
CLI->>Loader: load_config()
Loader->>File: 读取 ~/.nanobot/config.json
File-->>Loader: JSON 内容
Loader->>Schema: Config.model_validate(data)
Schema->>Schema: 验证 + camelCase 别名解析
Schema-->>Loader: Config 对象
Loader-->>CLI: Config 实例
2.2 核心配置结构
1 | class Config(BaseSettings): |
2.3 camelCase 别名支持
Config 系统使用 Pydantic 的 AliasChoices 同时支持 snake_case 和 camelCase:
1 | class ModelPresetConfig(Base): |
这允许用户在 config.json 中使用 JSON 惯例的 camelCase 键名。
3. Security 系统详解
3.1 SSRF 防护
AgentRunner._classify_violation 中的 SSRF 防护是硬安全边界:
1 | _SSRF_MARKERS = ( |
当工具(如 web_fetch)返回包含 SSRF 标记的错误时,AgentRunner 将其分类为 SSRF 违规并附加不可绕过的边界说明,阻止 LLM 尝试其他绕过方式。
3.2 工作区访问控制
WorkspaceScopeResolver(nanobot/security/workspace_access.py)管理文件和 Shell 工具的工作区访问权限:
- 默认模式:工具只能访问工作区路径
- 宽松模式(
restrict_to_workspace=False):工具可访问整个文件系统 - 项目工作区:可通过会话元数据指定不同的项目路径
3.3 工作区违规处理
1 | _WORKSPACE_VIOLATION_MARKERS = ( |
工作区违规与 SSRF 不同——它是可恢复的错误。AgentRunner 会给 LLM 机会重试(使用正确的路径),但如果重复违规,会升级提示强度。
4. 设计决策分析
4.1 为什么使用 Pydantic v2 而不是 dataclass?
决策:配置系统使用 Pydantic v2 (BaseSettings + Base)。
权衡:
- ✅ 自动验证和类型转换
- ✅ camelCase 别名支持(
AliasChoices) - ✅ 环境变量覆盖(
BaseSettings) - ✅ 嵌套模型验证
- ❌ 增加依赖(pydantic + pydantic-settings)
- ❌ 启动时验证开销
4.2 为什么 SSRF 是不可绕过的硬边界?
决策:SSRF 违规返回不可绕过的错误消息,阻止 LLM 的所有绕过尝试。
权衡:
- ✅ 防止 LLM 被诱导访问内网资源
- ✅ 安全默认,不依赖 LLM 的”判断”
- ❌ 可能阻止合法的内网访问(需要用户手动配置
ssrfWhitelist)
4.3 为什么工作区违规是可恢复的?
决策:工作区违规允许 LLM 重试。
权衡:
- ✅ LLM 可能只是使用了错误的相对路径,给机会纠正
- ✅ 不会因一次路径错误就终止整个 turn
- ❌ 恶意用户可能通过反复尝试探测文件系统结构(但受限在工作区外即被阻止)
5. 学习检查点
📝 本章小结
- Config 使用 Pydantic v2,支持 JSON 文件 + 环境变量 + camelCase 别名
- AgentDefaults 提供所有 Agent 行为的默认参数,可在 config.json 中覆盖
- SSRF 防护是硬安全边界,不可绕过,附带明确的拒绝说明
- 工作区访问控制是可恢复的错误,LLM 有重试机会
- camelCase 别名使 JSON 配置文件更符合前端惯例
🤔 思考题
为什么 SSRF 边界说明中要显式列出所有可能的绕过方式(curl, wget, encoded IPs…)?
参考答案
LLM 在遇到拒绝后往往会尝试”创造性”的绕过方式。如果不显式列出(
nanobot/nanobot/agent/runner.py#L1213-L1220),LLM 可能会逐一尝试这些方法——每次尝试都是一次新的工具调用,浪费 Token 和时间。一次性列举所有已知绕过方式可以提前阻断这个”尝试-拒绝”循环。WorkspaceScopeResolver的for_turn和for_message有什么区别?参考答案
for_turn在整个 turn 期间生效,决定工具执行时的路径限制。for_message在消息级别生效,允许同一 turn 内不同消息有不同的工作区(如子Agent 在独立的工作区运行)。这种分离支持了项目工作区功能——用户可以通过会话元数据临时切换工作区而无需重启 Agent。