跳转到内容

Pydantic 配置 Schema 设计

Pydantic 配置 Schema 设计

学习目标

理解 Nanobot 如何使用 Pydantic 实现 JSON 友好的 camelCase 配置格式与环境变量 snake_case 的统一。

前置知识

下文假设你已理解上述概念,直接聚焦 Nanobot 的具体实现。

项目实践

camelCase/snakeCase 双向兼容

基础模型通过 alias_generator 实现自动转换:

class Base(BaseModel):
model_config = ConfigDict(
alias_generator=to_camel, # snake_case → camelCase
populate_by_name=True, # 接受两种格式
)

这使得配置文件可以写为:

{
"agents": {
"defaults": {
"contextWindowTokens": 65536, // camelCase (JSON 推荐)
"model": "anthropic/claude-opus-4-5"
}
}
}

同时也接受 Python 风格的 context_window_tokens

环境变量自动映射

Config 继承 BaseSettings,支持环境变量覆盖:

class Config(BaseSettings):
model_config = ConfigDict(
env_prefix="NANOBOT_", # 前缀
env_nested_delimiter="__", # 嵌套分隔符
)

环境变量映射规则:

  • NANOBOT_AGENTS__DEFAULTS__MODEL=anthropic/claude-sonnet-4-6config.agents.defaults.model
  • NANOBOT_PROVIDERS__OPENROUTER__API_KEY=sk-or-v1-xxxconfig.providers.openrouter.api_key

延迟类型解析

ToolsConfig 的工具子配置存在循环导入问题(工具模块需要 from nanobot.config.schema import Base),采用延迟解析:

class ToolsConfig(Base):
web: WebToolsConfig = Field(
default_factory=lambda: _lazy_default("nanobot.agent.tools.web", "WebToolsConfig")
)
# ...
def _resolve_tool_config_refs():
"""运行时延迟导入并 rebuild model。"""
from nanobot.agent.tools.web import WebToolsConfig
# ...
ToolsConfig.model_rebuild()
Config.model_rebuild()

模型验证器

Config 使用 @model_validator 确保引用完整性:

@model_validator(mode="after")
def _validate_model_preset(self) -> "Config":
if "default" in self.model_presets:
raise ValueError("model_preset name 'default' is reserved")
name = self.agents.defaults.model_preset
if name and name not in self.model_presets:
raise ValueError(f"model_preset {name!r} not found")
# ...

智能 Provider 匹配

Config._match_provider() 实现了复杂的多级匹配逻辑:

  1. 强制指定 → 直接返回
  2. 模型前缀精确匹配 → anthropic/claudeanthropic
  3. 关键词模糊匹配 → 模型名包含 gptopenai
  4. 本地提供商 base URL 检测 → api_base 包含 11434ollama
  5. 有 API Key 的网关回退 → OpenRouter、AiHubMix 等

问题与规避

循环导入

问题:工具配置类在工具模块中定义,但工具模块导入 config.schema。

规避_resolve_tool_config_refs() 延迟到所有模块加载后调用,try/except ImportError 兼容首次导入失败。

设计取舍

为什么用 Pydantic 而非 configparser/YAML

原因:Pydantic 提供了 JSON 友好的序列化/反序列化、类型验证、环境变量映射、别名生成,一次性解决配置管理的所有痛点。

参考来源