Agno 数据库可插拔适配器模式
Agno 数据库可插拔适配器模式
学习目标
本章将分析 Agno 的数据库适配器架构:
- BaseDb / AsyncBaseDb 统一抽象接口
- 10+ 数据库后端的实现概览
- ComponentType 枚举的组件区分
- 数据库迁移脚本管理
项目实践
统一抽象接口
Agno 的数据库层围绕两个抽象基类构建:
# 伪代码:BaseDb 抽象class BaseDb(ABC): @abstractmethod def create_tables(self) -> None: ...
@abstractmethod def session_exists(self, session_id: str, user_id: Optional[str] = None) -> bool: ...
@abstractmethod def read_session(self, session_id: str, user_id: Optional[str] = None) -> Optional[Dict]: ...
@abstractmethod def upsert_session(self, session: Dict) -> None: ...
@abstractmethod def delete_session(self, session_id: str, user_id: Optional[str] = None) -> None: ...
# ... 更多抽象方法
class AsyncBaseDb(BaseDb): @abstractmethod async def acreate_tables(self) -> None: ...
@abstractmethod async def asession_exists(self, ...) -> bool: ...
# ... 所有方法的 async 版本关键设计:
AsyncBaseDb继承BaseDb,sync 方法仍然可用(调用同步版本)- 每个方法有明确的签名,实现类必须遵循
10+ 数据库后端
| 后端 | 目录 | 适用场景 |
|---|---|---|
| PostgreSQL | db/postgres/ | 生产环境,事务支持 |
| AsyncPostgreSQL | db/async_postgres/ | 异步生产环境 |
| SQLite | db/sqlite/ | 开发/测试环境 |
| JSON | db/json/ | 文件存储,无数据库 |
| In-Memory | db/in_memory/ | 测试,无持久化 |
| MongoDB | db/mongo/ | 文档数据库 |
| Redis | db/redis/ | 缓存 + 持久化 |
| DynamoDB | db/dynamo/ | AWS 托管 |
| Firestore | db/firestore/ | GCP 托管 |
| GCS JSON | db/gcs_json/ | Google Cloud Storage |
| SingleStore | db/singlestore/ | 分布式 SQL |
| SurrealDB | db/surrealdb/ | 多模型数据库 |
ComponentType 枚举
Agno 使用 ComponentType 区分不同组件的数据存储:
# 伪代码class ComponentType(str, Enum): AGENT = "agent" TEAM = "team" WORKFLOW = "workflow"同一数据库后端为 Agent、Team、Workflow 分别维护独立的 session 表/集合:
PostgreSQL: agent_sessions 表 team_sessions 表 workflow_sessions 表
MongoDB: agent_sessions 集合 team_sessions 集合 workflow_sessions 集合SessionType 区分
# 伪代码class SessionType(str, Enum): AGENT = "agent" TEAM = "team" WORKFLOW = "workflow"SessionType 与 ComponentType 配合,在数据库操作中标记会话类型。
数据库迁移
db/migrations/ 目录包含迁移脚本:
db/migrations/ 001_create_agent_sessions.py 002_create_team_sessions.py 003_create_workflow_sessions.py 004_add_metrics_column.py ...PostgreSQL 后端使用 Alembic 风格的迁移,其他后端可能有简化的迁移逻辑。
配置解析
db/utils.py 提供数据库配置解析:
# 伪代码def resolve_db_from_config(db_url: str) -> BaseDb: if db_url.startswith("postgresql"): return PostgresDb(db_url=db_url) elif db_url.startswith("sqlite"): return SqliteDb(db_url=db_url) elif db_url.startswith("mongodb"): return MongoDb(db_url=db_url) # ... 更多后端问题与规避
1. 抽象接口与具体实现的差距
BaseDb 定义的方法在某些数据库中无法完美映射(如 Redis 的键值存储不支持复杂查询)。
规避:适配器实现可能使用数据结构模拟(如在 Redis 中使用 Hash 结构模拟表)。对于不支持的操作,抛出 NotImplementedError。
2. 迁移脚本的后端差异
不同数据库的迁移语法不同(PostgreSQL 用 SQL,MongoDB 用 JavaScript)。
规避:每个后端实现自己的迁移逻辑。BaseDb.create_tables() 自动执行当前后端的迁移。
3. 性能差异
In-Memory 和 Redis 后端比 PostgreSQL 快很多,但功能受限。
规避:
- 开发用 SQLite/In-Memory
- 生产用 PostgreSQL/AsyncPostgreSQL
- 缓存层用 Redis
设计取舍
为什么支持这么多数据库后端?
优势:用户可以选择已有的基础设施,无需额外部署数据库 代价:维护成本高,每个后端都需要测试 替代方案:只支持 PostgreSQL 和 SQLite——覆盖大多数场景,但失去灵活性
为什么 AsyncBaseDb 继承 BaseDb?
优势:sync 方法在异步后端中仍然可用(通过 asyncio.to_thread 包装同步调用)
代价:AsyncBaseDb 需要实现两组方法
替代方案:两个完全独立的接口——更干净但不能混用 sync/async