Google ADK(adk-python)Integrations 集成架构指南:可选依赖、懒加载与子包贡献规范
【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python
本篇技术指南围绕 src/google/adk/integrations/README.md 展开,系统讲解 ADK 中"集成(Integrations)"目录的定位、设计原则与扩展方式:什么代码应该放进integrations/、如何用可选依赖(extras)保持核心框架轻量、如何通过懒加载给出友好的错误提示,以及如何为外部服务(Agent Registry、BigQuery、Slack、Redis 等)编写自包含的集成子包。读完本文,你将掌握在 adk-python 中新增或使用一个集成模块的完整路径,并能在源码层面理解其背后的依赖管理与加载机制。
Integrations 目录的定位:可扩展的"插槽式"架构
ADK(Agent Development Kit)采用 code-first 的方式构建、评估与部署 AI Agent。随着生态的扩展,Agent 往往需要连接外部工具与服务(Agent Registry、BigQuery、ApiHub、Slack、Redis……)。如果把这些第三方依赖直接打进核心包,会让"轻量的核心"变得臃肿,也会给只使用基础能力的用户带来不必要的安装负担。
为此,adk-python 在 src/google/adk/integrations/ 下划出了一块专门的"集成区":所有与外部系统对接的模块都应放入该目录的子包中。这一集中化管理带来两个直接好处:
- 可发现性:开发者可以在一处浏览、查找、复用所有官方集成;
- 可贡献性:第三方集成有明确、统一的落位与规范,社区贡献者更容易参与。
从源码结构看,当前仓库的integrations/下已经存在 21 个集成子包,覆盖 Google Cloud 服务与第三方生态:
| 集成子包 | 主要职责 |
|---|---|
agent_identity | 基于 Google Cloud 的身份凭证与 AuthProvider 方案 |
agent_registry | 对接 Google Cloud Agent Registry,注册/发现 Agent 与 MCP Server |
api_registry | 对接 API Registry,管理 API 元数据 |
bigquery | BigQuery 查询、元数据、搜索与数据洞察工具集 |
cloud_run | Cloud Run 部署相关能力 |
crewai | 与 CrewAI 工具生态的互操作 |
daytona/e2b | 远程沙箱环境(DaytonaEnvironment / E2BEnvironment) |
eventarc | Eventarc 事件发布与消息工具 |
firestore/gcs/redis | 持久化存储:Firestore、GCS、Redis 会话服务 |
langchain | 与 LangChain 工具/结构的互操作 |
livekit | LiveKit 实时音视频媒体接入 |
model_armor | Google Cloud Model Armor 安全防护集成 |
oci | Oracle Cloud Infrastructure 生成式 AI(OCIGenAILlm) |
parameter_manager/secret_manager | GCP 参数与密钥管理 |
skill_registry | GCP Skill Registry |
slack | SlackRunner:将 Agent 部署到 Slack(Socket Mode) |
vmaas | 漏洞管理相关集成 |
以上清单依据 src/google/adk/integrations/ 目录实测列出,可作为"什么代码属于这里"的具体参照。
什么属于 Integrations 目录?
原文档给出了两条判断标准,实践中可进一步细化:
- 连接外部服务的代码:凡是把 ADK 与其它服务、API 或工具连接起来的模块(例如 agent_registry/agent_registry.py 中直接对
https://agentregistry.googleapis.com/v1发起 REST 请求的AgentRegistry客户端),都天然属于此目录。 - 依赖第三方库的模块:模块若依赖未包含在 ADK 核心依赖中的第三方库(例如 Slack 集成依赖
slack-bolt、Redis 集成依赖redis、Agent Registry 依赖a2a-sdk),也必须放在这里,而不能进入核心包。
反过来,与外部系统无关的通用能力(如 Agent 核心逻辑、Runner、会话服务基类、事件模型等)应留在src/google/adk/下对应的核心子包中(如agents/、runners.py、sessions/),不要把通用代码放进integrations/。
贡献指南详解:五个必须遵守的规范
原文档给出的贡献指南是集成开发的核心约束,以下结合仓库源码逐条展开。
1. 自包含包:每个集成一个独立子目录
每个集成应独立存在于自己的子目录中,例如integrations/my_service/。这样做的目的是保证集成的边界清晰:它可以独立安装、独立测试、独立演进,不会与其它集成产生隐式耦合。
仓库中的 integrations/slack/、integrations/redis/、integrations/bigquery/ 等都是这一模式的实例:每个目录内部都有自己的__init__.py与实现文件,且大多附带独立的 README(如 integrations/slack/README.md、integrations/redis/README.md)。
2. 内部结构自由:不强制遵循核心框架的组织模式
集成子包内部可以自由选择自己的代码结构与设计模式,无需严格照搬核心 ADK 框架的组织方式。例如:
- integrations/redis/ 内部采用
_config.py+_redis_session_service.py的下划线私有模块命名,用 Pydantic 配置类承载连接参数; - integrations/agent_registry/ 则用单个
agent_registry.py文件承载一个功能完整的高层客户端类; - integrations/bigquery/ 拆分为
client.py、config.py、query_tool.py、metadata_tool.py、search_tool.py等多个工具文件。
可见,只要保持"子包自包含",内部是采用扁平单文件、工具拆分、还是配置与实现分离,完全由集成作者根据服务复杂度自行决定。
3. 依赖可选化:extras 是核心原则
为了保持 ADK 核心轻量,集成所需依赖必须是可选的,并通过pyproject.toml中的optional-dependencies(即 pip 的 extras)定义。extra 的名称应与集成目录名一致,用户通过pip install "google-adk[my_service]"安装。
查看仓库根目录的 pyproject.toml,可以看到大量与集成目录一一对应的 extras 定义,例如:
optional-dependencies.slack:slack-bolt>=1.22与aiohttp!=3.14.2(Socket Mode 适配器依赖 aiohttp);optional-dependencies.redis:redis>=4.2(注释说明 4.2 是redis.asyncio落地的版本);optional-dependencies.a2a:a2a-sdk[http-server]>=0.3.4,<2(Agent Registry 与远程 A2A Agent 需要);optional-dependencies.livekit:livekit、livekit-api与pillow(后者用于对入站视频帧做 JPEG 编码);optional-dependencies.oci:oci>=2.126;optional-dependencies.bigquery-analytics:google-cloud-bigquery、google-cloud-storage与pyarrow(注释说明 pyarrow 体积约占该 extra 安装体积的三分之一,因此单独拆分);optional-dependencies.gcp、mcp、tools、extensions等聚合类 extra。
此外optional-dependencies.all是"解锁所有运行时特性"的集合(pyproject.toml中明确排除了 benchmark、community、dev、docs、test 这类服务于构建/测试/文档自身的 extra)。需要一次装齐所有集成能力时可以使用:
pip install "google-adk[all]"注意:
all包含crewai[tools]这类带 Python 版本条件(仅 3.11~3.12)的依赖,实际安装时 pip 会按解释器版本自动过滤。
4. 懒加载:捕获 ModuleNotFoundError 并给出可操作的错误提示
集成代码必须实现懒加载。如果用户未安装对应 extras 就使用该集成,应当捕获ModuleNotFoundError并抛出带有正确安装命令的描述性错误。
这一规范在 integrations/slack/slack_runner.py 中有教科书级的实现:
try: from slack_bolt.adapter.socket_mode.aiohttp import AsyncSocketModeHandler from slack_bolt.app.async_app import AsyncApp except ImportError as e: raise ImportError( "slack_bolt is not installed. Please install it with " '`pip install "google-adk[slack]"`.' ) from e同样的模式也出现在 integrations/agent_registry/agent_registry.py 中,其错误信息为:
raise ImportError( "AgentRegistry requires the 'a2a-sdk' package. " "Please install it using 'pip install google-adk[a2a]'." ) from e这里有两个值得借鉴的细节:
- 使用
from e保留原始异常链(raise ... from e),方便调试时追溯根因; - 错误信息直接给出精确的安装命令
pip install "google-adk[<extra>]",把用户引导到正确的操作上,而不是抛出一个模糊的ModuleNotFoundError: No module named 'slack_bolt'。
这种"延迟 import + 友好报错"的组合,让核心包可以安全地不依赖任何第三方库,同时保证用户在误用时第一时间得到解决方案。
5. 文档:每个集成都要有清晰的 setup / configuration / usage 说明
每个集成都应提供清晰文档,包含安装、配置与使用示例。仓库中的集成子包大多遵循这一要求,其中:
- integrations/slack/README.md 覆盖了前置安装、Slack App 的 Socket Mode/权限/事件订阅配置步骤,以及一段可运行的
SlackRunner接入代码; - integrations/redis/README.md 则提供了配置参数表、三种连接方式示例(URI、独立连接参数、预配置客户端)、Redis key 结构说明与直接调用
RedisSessionService的完整代码。
写作文档时建议沿用这一结构:先讲"装什么"(extras 安装命令),再讲"外部服务怎么配"(如 Slack App 权限、GCP IAM),最后给"最小可用示例"。
实战一:把 Agent 部署到 Slack(以 SlackRunner 为例)
作为"使用一个集成"的完整示例,integrations/slack/README.md 展示了如何把 ADK Agent 通过 Socket Mode 部署到 Slack。整体分四步。
第一步:安装带 Slack 支持的 ADK
pip install "google-adk[slack]"第二步:在 Slack API Dashboard 配置 App
- 打开Socket Mode,启用后生成App-Level Token(以
xapp-开头),并确保其具有connections:write权限; - 在OAuth & Permissions中添加 Bot Token Scopes:
app_mentions:read(接收 @提及)、chat:write(发送消息)、im:history(响应私信),按需添加groups:history(私密频道)与channels:history(公开频道); - 在Event Subscriptions中启用事件订阅,添加 bot 事件
app_mention与message.im; - 将 App 安装到工作区,获得Bot User OAuth Token(以
xoxb-开头)。
第三步:初始化并启动 SlackRunner
import asyncio import os from google.adk.runners import Runner from google.adk.integrations.slack import SlackRunner from slack_bolt.app.async_app import AsyncApp async def main(): # 1. 初始化 ADK Runner(传入你的 agent) # runner = Runner(agent=my_agent, session_service=my_session_service) # 2. 用 Bot Token 初始化 Slack AsyncApp slack_app = AsyncApp(token=os.environ["SLACK_BOT_TOKEN"]) # 3. 初始化 SlackRunner slack_runner = SlackRunner(runner=runner, slack_app=slack_app) # 4. 用 App Token 以 Socket Mode 启动 await slack_runner.start(app_token=os.environ["SLACK_APP_TOKEN"]) if __name__ == "__main__": asyncio.run(main())第四步:理解内部的会话与消息处理机制
从 integrations/slack/slack_runner.py 的源码可以看到几个关键设计:
- 防循环:
message事件处理器会跳过带bot_id/bot_profile的消息,避免 Agent 与自己对话造成死循环; - 触发条件:仅当
channel_type == "im"(私信)或消息处于线程中(thread_ts存在)时才交由_handle_message处理,与 README 中"订阅app_mention与message.im"的配置相呼应; - 会话 ID 规则(与 README 的 Session Management 小节一致):
- 私信:直接以
channel_id作为会话 ID; - 线程:以
f"{channel_id}-{thread_ts}"作为会话 ID,维持线程上下文; - App 提及且不在线程中:以消息时间戳
ts作为thread_ts,从而开启一个新线程会话;
- 私信:直接以
- 流式体验:先发送
_Thinking..._占位消息,随后在runner.run_async(...)的流式事件中,用chat_update把占位消息原地替换为 Agent 输出(thinking_ts置空后再用say发后续内容),结束后若没有文本输出则chat_delete删除占位消息; - 错误兜底:
run_async抛出异常时,把"Sorry, I encountered an error: ..."写入占位消息或直接回复,同时logger.exception记录堆栈。
实战二:用 Redis 做持久化会话(以 RedisSessionService 为例)
另一个典型的"配置项丰富"的集成是 Redis 会话服务,其完整文档见 integrations/redis/README.md。它实现了BaseSessionService,为 Agent 提供基于 Redis 的持久化会话存储。
安装:
pip install google-adk redis最小接入:
from google.adk.agents import Agent from google.adk.integrations.redis import RedisSessionService from google.adk.integrations.redis import RedisSessionServiceConfig from google.adk.runners import Runner config = RedisSessionServiceConfig( uri="redis://localhost:6379/0", ttl_seconds=86400 * 7, # 7 天 ) session_service = RedisSessionService(config=config) agent = Agent( name="assistant", instructions="You are a helpful AI assistant.", ) runner = Runner( app_name="my_app", agent=agent, session_service=session_service, )配置参数表(来自 README,字段默认值可直接照用):
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
uri | Optional[str] | None | Redis 连接 URI(如redis://[:password@]host:port/db,SSL 用rediss://)。设置后优先于独立连接字段。 |
host | Optional[str] | "localhost" | Redis 主机名。 |
port | Optional[int] | 6379 | Redis 端口。 |
password | Optional[str] | None | Redis 认证密码。 |
ssl | bool | False | 是否启用 SSL/TLS。 |
db | int | 0 | Redis 数据库索引。 |
ttl_seconds | int | 604800(7 天) | 会话 key 的过期时间;设为0或负数则禁用过期。 |
key_prefix | str | "adk:session:" | 会话服务创建的所有 Redis key 的前缀。 |
Redis key 结构与状态作用域(README 中的核心设计):
| Key 模式 | 数据 | 说明 |
|---|---|---|
{key_prefix}{app_name}:{user_id}:{session_id} | JSON(Session) | 会话本体:session ID、state、events 列表与最后更新时间,按ttl_seconds过期。 |
{key_prefix}user_state:{app_name}:{user_id} | JSON(dict) | user:前缀的用户级状态,跨会话共享,按ttl_seconds过期。 |
{key_prefix}app_state:{app_name} | JSON(dict) | app:前缀的应用级状态,跨用户、跨会话共享,按ttl_seconds过期。 |
状态同步规则:事件追加user:<key>增量时同步到用户级状态 key;追加app:<key>时同步到应用级状态 key;创建会话或更新事件时,三个作用域的状态会合并进会话状态。
直接以服务方式使用(绕过 Runner 单独管理会话):
from google.adk.integrations.redis import RedisSessionService from google.adk.sessions.base_session_service import GetSessionConfig session_service = RedisSessionService() # 创建会话(state 中混合了 user: 作用域与普通作用域键) session = await session_service.create_session( app_name="my_app", user_id="user_123", state={"user:theme": "dark", "topic": "weather"}, ) # 取最近 10 条事件 retrieved = await session_service.get_session( app_name="my_app", user_id="user_123", session_id=session.id, config=GetSessionConfig(num_recent_events=10), ) # 列出该用户所有会话 response = await session_service.list_sessions( app_name="my_app", user_id="user_123", ) # 删除会话 await session_service.delete_session( app_name="my_app", user_id="user_123", session_id=session.id, )从 Agent Registry 集成看"高层封装"模式
并非所有集成都只是"挂一个工具",integrations/agent_registry/agent_registry.py 展示了一种更高层的封装思路:AgentRegistry客户端不仅封装了 REST 调用,还提供把注册资源转换为可直接使用的 ADK 组件的方法:
get_mcp_toolset(mcp_server_name, ...):根据注册的 MCP Server 元数据,自动解析连接 URI、自动从 IAM bindings 解析认证方案(GcpAuthProviderScheme),返回一个配置好的McpToolset;get_remote_a2a_agent(agent_name, ...):把注册的 A2A Agent 解析为RemoteA2aAgent(优先使用注册卡card,否则依据 URI/协议绑定手工构造 agent card);list_agents/search_agents/list_mcp_servers/search_mcp_servers/list_endpoints等资源管理方法。
值得注意的实现细节:AgentRegistrySingleMcpToolset(同文件 agent_registry.py)在McpToolset.get_tools()返回的每个工具上注入GCP_MCP_SERVER_DESTINATION_ID这一custom_metadata键,用于在google.adk.telemetry.tracing的execute_toolspan 中标记 MCP 目标——这体现了"集成不仅能调用外部服务,还能与 ADK 的遥测体系深度协作"。
该集成同样遵循懒加载规范,未安装a2a-sdk时会抛出包含pip install google-adk[a2a]的明确错误。同时它还展示了 mTLS 支持:通过GOOGLE_API_USE_MTLS_ENDPOINT(auto/always/never)与GOOGLE_API_USE_CLIENT_CERTIFICATE环境变量决定是否走agentregistry.mtls.googleapis.com端点。
从"使用集成"到"编写集成":落地检查清单
综合原文档与仓库实践,无论使用还是贡献集成,都可以用下面的清单快速核对:
- 归属:代码是否在连接外部服务或依赖第三方库?若是,放入
src/google/adk/integrations/<name>/,而不是核心包; - 自包含:是否所有文件都收敛在
integrations/<name>/一个目录内? - extras 一致:
pyproject.toml中是否定义了与目录同名的optional-dependencies.<name>?用户能否用pip install "google-adk[<name>]"安装? - 懒加载:模块顶部是否用
try/except ImportError包裹第三方 import,并在 except 分支抛出带安装命令的ImportError ... from e? - 文档:是否提供独立的 README,覆盖安装命令、外部服务配置步骤、配置参数与可运行示例?
- 回归测试:是否补充了对应单元测试(可参考 tests/unittests/integrations/ 下按集成分组的测试组织方式)?
总结
ADK 的 Integrations 目录是官方为"可扩展性"预留的标准化插槽:它以"自包含子包 + 可选 extras 依赖 + 懒加载友好报错 + 独立文档"四条规则,在核心轻量与生态丰富之间取得了平衡。对使用者而言,pip install "google-adk[slack]"、google-adk[redis]这类命令就是接入能力的入口;对贡献者而言,integrations/<name>/目录加上一份 README 即是新集成的完整交付形态。本文所引用的 integrations/README.md、pyproject.toml 以及 Slack、Redis、Agent Registry 三个子包源码,共同构成了理解 ADK 集成机制的最小但完整的证据集。
【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考