跳转到内容

Agno Pydantic Dataclass 大规模参数设计

Agno Pydantic Dataclass 大规模参数设计

学习目标

本章将分析 Agno 的大规模参数数据模型设计:

  • @dataclass(init=False) 的设计动机
  • 手写 __init__ 处理 80+ 参数的模式
  • 向后兼容的废弃参数迁移策略
  • 字段即文档的注释风格

项目实践

@dataclass(init=False) 模式

Agno 的 AgentTeam 类使用 @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 Agent
model: Optional[Model] = None
# Fallback models tried when the primary model fails
fallback_models: Optional[List[Union[Model, str]]] = None
# If True, give the agent tools to update the session_state dynamically
enable_agentic_state: bool = False

为什么不用 docstring? dataclass 字段不支持 docstring。注释是最直接的文档方式,IDE 也能通过 hover 显示。

联合类型(Union)的广泛使用

# 伪代码:Union 类型的字段
tools: Optional[Union[List[Union[Toolkit, Callable, Function, Dict]], Callable[..., List]]] = None
knowledge: Optional[Union[KnowledgeProtocol, Callable[..., KnowledgeProtocol]]] = None
learning: 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() 工厂函数统一处理:

# 伪代码:字符串转 Model
self.model = model # 赋值原始值
# 在 run() 时解析
agent.model = get_model(agent.model)

设计取舍

为什么用 dataclass 而非 Pydantic BaseModel?

Agno 的 Agent 使用 @dataclass 而非 class Agent(BaseModel)

  • 优势dataclass 更轻量,没有 Pydantic 的验证开销;字段默认值直接声明
  • 代价:没有 Pydantic 的自动验证(需要在 __init__ 中手动处理)
  • 替代方案:Pydantic BaseModel——自动验证但更重,初始化更慢

为什么保留废弃参数而非直接移除?

优势:向后兼容——已有代码不会因为升级而报错 代价:签名膨胀,新开发者可能困惑 替代方案:直接移除——干净的签名但破坏已有代码

参考来源