Agno Pydantic Dataclass 大规模参数设计
Agno Pydantic Dataclass 大规模参数设计
学习目标
本章将分析 Agno 的大规模参数数据模型设计:
@dataclass(init=False)的设计动机- 手写
__init__处理 80+ 参数的模式 - 向后兼容的废弃参数迁移策略
- 字段即文档的注释风格
项目实践
@dataclass(init=False) 模式
Agno 的 Agent 和 Team 类使用 @dataclass(init=False):
# 伪代码:Agent 类定义@dataclass(init=False)class Agent: # Agent settings model: Optional[Model] = None fallback_models: Optional[List[Union[Model, str]]] = None fallback_config: Optional[FallbackConfig] = None name: Optional[str] = None id: Optional[str] = None
# User settings user_id: Optional[str] = None
# Session settings session_id: Optional[str] = None session_state: Optional[Dict[str, Any]] = None # ... 80+ fields total为什么 init=False?
- 字段声明即文档:每个字段有 1-2 行注释,说明用途
- 手写
__init__可以处理复杂逻辑(废弃参数迁移、参数验证、默认值处理) - 保持 dataclass 的
__repr__、__eq__等自动生成方法
手写 init 处理
# 伪代码:手写 __init__def __init__( self, *, model: Optional[Union[Model, str]] = None, fallback_config: Optional[FallbackConfig] = None, fallback_models: Optional[List[Union[Model, str]]] = None, # ... 80+ parameters # Deprecated params — kept for backward compatibility search_session_history: Optional[bool] = None, num_history_sessions: Optional[int] = None,): self.model = model if fallback_config is not None: if fallback_models: log_warning("Both fallback_config and fallback_models provided. Using fallback_config.") self.fallback_config = fallback_config # ... 字段赋值
# 废弃参数迁移 if search_session_history is not None: log_warning("search_session_history is deprecated. Use search_past_sessions.") self.search_past_sessions = search_session_history关键设计:
- 全部参数使用关键字参数(
*后声明),禁止位置参数调用 - 废弃参数保留在签名中,但不引发错误——只发 warning 并迁移
log_warning提醒用户迁移,不破坏已有代码
字段即文档
每个 dataclass 字段有 1-2 行注释:
# 示例:字段注释# Model for this Agentmodel: Optional[Model] = None# Fallback models tried when the primary model failsfallback_models: Optional[List[Union[Model, str]]] = None# If True, give the agent tools to update the session_state dynamicallyenable_agentic_state: bool = False为什么不用 docstring? dataclass 字段不支持 docstring。注释是最直接的文档方式,IDE 也能通过 hover 显示。
联合类型(Union)的广泛使用
# 伪代码:Union 类型的字段tools: Optional[Union[List[Union[Toolkit, Callable, Function, Dict]], Callable[..., List]]] = Noneknowledge: Optional[Union[KnowledgeProtocol, Callable[..., KnowledgeProtocol]]] = Nonelearning: Optional[Union[bool, LearningMachine]] = None优势:一个字段支持多种输入方式,降低使用门槛
代价:__init__ 中需要类型判断和处理逻辑
问题与规避
1. 80+ 参数的可维护性
随着功能增加,__init__ 参数列表越来越长。
规避:
- 使用
# --- Section Name ---注释分组(Agent settings / Session settings / Memory 等) - 关键字参数强制调用,避免位置参数的顺序依赖
- Pydantic 的类型检查在静态分析时发现问题
2. 废弃参数的签名膨胀
保留废弃参数会导致签名中有大量历史参数。
规避:
- 废弃参数放在签名最后,有明确的
# Deprecated params注释 - 使用
log_warning而非DeprecationWarning(更明显) - 计划在下一个大版本移除
3. Union 类型的运行时处理
model: Optional[Union[Model, str]] 接受字符串 ID 或 Model 实例,__init__ 需要转换。
规避:使用 get_model() 工厂函数统一处理:
# 伪代码:字符串转 Modelself.model = model # 赋值原始值# 在 run() 时解析agent.model = get_model(agent.model)设计取舍
为什么用 dataclass 而非 Pydantic BaseModel?
Agno 的 Agent 使用 @dataclass 而非 class Agent(BaseModel):
- 优势:
dataclass更轻量,没有 Pydantic 的验证开销;字段默认值直接声明 - 代价:没有 Pydantic 的自动验证(需要在
__init__中手动处理) - 替代方案:Pydantic BaseModel——自动验证但更重,初始化更慢
为什么保留废弃参数而非直接移除?
优势:向后兼容——已有代码不会因为升级而报错 代价:签名膨胀,新开发者可能困惑 替代方案:直接移除——干净的签名但破坏已有代码