跳转到内容

Agno 可插拔上下文提供者协议

Agno 可插拔上下文提供者协议

学习目标

本章将分析 Agno 的上下文提供者系统:

  • ContextProvider 抽象基类的统一接口设计
  • 三种暴露模式(default/agent/tools)的适用场景
  • 10+ 内置提供者的功能概览
  • 健康检查(Status)协议

前置知识

建议先阅读:

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


项目实践

ContextProvider 抽象基类

Agno 将所有外部上下文源统一为 ContextProvider 协议:

# 伪代码:ContextProvider 协议
class ContextProvider(ABC):
def __init__(self, id: str, mode: ContextMode = ContextMode.default, model: Optional[Model] = None):
self.id = id
self.mode = mode
self.model = model
@abstractmethod
def query(self, question: str) -> Answer: ...
@abstractmethod
async def aquery(self, question: str) -> Answer: ...
@abstractmethod
def status(self) -> Status: ...
@abstractmethod
async def astatus(self) -> Status: ...
# 可选:写入支持(默认抛出 NotImplementedError)
async def aupdate(self, document: Document) -> None:
raise NotImplementedError(f"{self.id} is read-only")

关键设计

  • Answer 返回结构化的 results(Document 列表)和 text(自然语言摘要)。查询方可以选择使用结构化结果或直接使用文本
  • Status 提供健康检查能力(ok: bool + detail: str
  • 只读提供者继承 NotImplementedError,调用方通过 <name> is read-only 错误消息获得清晰反馈

三种暴露模式

ContextMode 控制 Provider 如何暴露给调用 Agent:

模式行为适用场景
defaultProvider 自己决定最佳暴露方式通用场景,让 Provider 自行优化
agent包装为子 Agent,调用方获得单一 query_<id> 工具Agent 需要自然语言查询能力,不需要了解 Provider 的底层细节
tools直接暴露 Provider 的底层工具(如 search_slack_messagesread_file调用方需要精细控制,自己决定查询策略

mode=agent 的包装逻辑

# 伪代码:将 Provider 包装为子 Agent
sub_agent = Agent(
name=f"{provider.id}_context",
tools=[provider.query_tool], # 暴露单一查询工具
system_message=provider.instructions(), # 注入使用指南
)
# 调用方 Agent 获得一个 query_<id> 工具
# 如:query_slack(question: str) -> str

mode=tools 的暴露逻辑

# 伪代码:直接暴露 Provider 的底层工具
tools = provider.get_tools() # 返回多个工具
# 如 SlackProvider 暴露:search_slack_messages, read_channel, get_thread_replies
# 调用方 Agent 可以直接编排这些工具

内置提供者概览

提供者数据源写入支持核心工具
WebContextProvider搜索引擎(Exa/Parallel)搜索网页、提取摘要
SlackContextProviderSlack 工作区搜索消息、读取频道、获取线程
GoogleDriveContextProviderGoogle Drive浏览文件、搜索文档
GmailContextProviderGmail搜索邮件、读取邮件
FileSystemContextProvider本地文件系统浏览目录、读取文件
DatabaseContextProviderSQL 数据库执行查询、列出表
WikiContextProvider知识库查询知识库条目
MCPContextProvider外部 MCP 服务器取决于服务器通过 MCP 协议暴露
CalendarContextProvider日历服务查询日程
WorkspaceContextProvider工作区文件管理工作区文件

健康检查协议

每个 Provider 实现 status() 返回健康状态:

# 伪代码:健康检查
@dataclass
class Status:
ok: bool # 是否可达
detail: str = "" # 详细信息(失败原因或连接状态)

Agent 可以在 run 前检查所有 Provider 的 status(),提前发现配置问题。

问题与规避

1. 只读 Provider 的写入错误

调用方尝试对只读 Provider(如 Web、Slack)执行写入操作,会收到 NotImplementedError

规避:Agent 的系统提示中包含 Provider 的 instructions(),指导哪些操作不可执行。但 Agent 可能仍然尝试写入——错误消息 <name> is read-only 提供了清晰的反馈。

2. 外部服务不可用

Web 搜索、Slack、Drive 等外部服务可能因网络或认证问题不可用。

规避status() 方法可以在 run 前检查。但对于 run 过程中的临时不可用,需要在工具调用层面处理(重试、降级)。

3. 三种模式的选择困惑

用户可能不确定该用 default/agent/tools 哪个模式。

规避

  • default 是最安全的默认选择
  • 如果需要自然语言查询能力(“帮我搜索…”),用 agent
  • 如果需要精细控制(先列出目录再读取特定文件),用 tools

设计取舍

为什么统一抽象而非各自为战?

Agno 选择将所有上下文源统一为 ContextProvider

  • 优势:统一的 query/status 接口,新增提供者只需实现协议
  • 代价:抽象层可能丢失某些提供者的特有功能(如 Slack 的线程回复)
  • 替代方案:每种上下文源独立 API——保留全部功能但使用更复杂

为什么 mode=agent 包装为子 Agent 而不是直接工具?

优势:子 Agent 有自己的系统提示和上下文理解,可以处理复杂查询(如”找到最近关于 X 的讨论,总结分歧”) 代价:子 Agent 调用增加一次额外的 LLM 调用 替代方案:直接暴露工具——更快但需要调用方 Agent 自己构建查询

参考来源