ADK 集成 AntigravityAgent:把 Google Antigravity SDK 代理封装为原生 ADK Agent 节点
2026/9/13 17:10:54 网站建设 项目流程

ADK 集成 AntigravityAgent:把 Google Antigravity SDK 代理封装为原生 ADK Agent 节点

【免费下载链接】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

AntigravityAgent是 ADK(Agent Development Kit)在labs模块中提供的一个BaseAgent子类,它把一个预配置的google.antigravity.AgentConfig(如LocalAgentConfig)包装成标准的 ADK Agent 节点:每一轮对话被委托给 Antigravity SDK 的运行器执行,其轨迹步骤(模型文本、工具调用、工具响应)被流式转换为标准 ADKEvent并写入会话记录。阅读本文后,你将掌握如何用 Antigravity SDK 的本地工作区工具与策略来驱动 ADK 应用,如何通过mode='single_turn'将其作为子代理嵌套进 ADK 父代理,以及如何把 ADKsub_agents反向桥接为 Antigravity 的客户端工具。

为什么需要 AntigravityAgent

Antigravity SDK 擅长在本地工作区中执行任务——它自带工作区(workspace)管理、文件类工具与安全策略(policy),例如限制模型只能在指定目录内读写。但 Antigravity SDK 本身不提供 ADK 那样的代理编排(多代理、工作流)、会话存储与 Web UI 能力。

AntigravityAgent解决了这个组合问题:它让 ADK 应用直接"借用" Antigravity SDK 的本地工作区工具与策略,同时保留 ADK 的编排、会话与 UI 能力。官方指南的原话是:它将 Antigravity SDK 的运行循环(harness)与 ADK 的会话/事件体系缝合在一起(见 官方指南 与 模块文档字符串)。

从源码结构看,该集成位于labs(实验性实验室模块)之下,与labs/openai并列,属于 ADK 提供的非核心、实验性质的集成能力,由以下几个文件组成:

  • 入口与主类:AntigravityAgent主实现
  • 事件转换器:把 Antigravity SDK 的Step翻译为 ADKEvent
  • 子代理工具桥接:把 ADK 子代理包装为 Antigravity 的客户端工具
  • 工具结果捕获:通过钩子(hook)缓冲客户端工具的调用结果
  • 包入口:依赖检查与AntigravityAgent导出

需要特别说明的是:使用本模块前必须先安装google-antigravity__init__.py在导入时会import google.antigravity,若缺失会抛出ImportError并提示运行pip install "google-adk[antigravity]"(见 包入口)。

快速开始:包装一个 Antigravity SDK 代理

官方指南给出了最精简的启动代码。第一步先配置 Antigravity SDK 侧的代理,第二步将其包装为 ADK 根代理:

from google.adk.labs.antigravity import AntigravityAgent from google.antigravity import LocalAgentConfig from google.antigravity.hooks import policy # 1. 配置 Antigravity SDK 代理。 # 多轮对话必须设置 save_dir,这样临时目录才能在多轮之间被保留。 sdk_config = LocalAgentConfig( system_instructions="You are a helpful local environment assistant.", workspaces=["./sandbox"], policies=[*policy.workspace_only(["./sandbox"])], save_dir="./trajectories", ) # 2. 将 Antigravity SDK 配置包装为独立的 ADK 根代理。 root_agent = AntigravityAgent( name="antigravity_assistant", description="Runs an Antigravity SDK agent inside ADK.", config=sdk_config, )

对这段代码的几个要点说明:

  • workspacespoliciespolicy.workspace_only(["./sandbox"])是一个 workspace 限定策略,允许模型在./sandbox内使用内置文件工具进行创建与编辑,同时把写入限制在该目录内。这是 Antigravity SDK 本地代理的核心安全机制。
  • save_dir的多轮语义:见下文"配置选项"一节的详细解释。
  • namedescriptionname是 ADK 会话中该代理的标识;description会在该代理被作为子代理/工具暴露给模型时被读取。

完成包装后,root_agent就是一个标准的 ADKBaseAgent,可以直接交给 ADK 的Runner运行,像使用任何其他 ADK 代理一样进行多轮对话、事件监听与 UI 调试。

仓库中有一个完整可运行的真实示例——Game Developer Agent:它把上述模式落地为一个"网页游戏开发者代理",system_instructions要求模型把每个游戏写成单个自包含 HTML 文件(内联 CSS/JavaScript,无外部依赖),并在game_repo工作区内分步增量构建,完成后说明如何游玩。示例还演示了工程细节:用os.makedirs(..., exist_ok=True)预创建工作区与轨迹目录,再传给LocalAgentConfig

工作原理:每轮重建 SDK 代理,用会话 ID 维持连续性

官方指南明确描述了AntigravityAgent的核心运行时模型:

  1. 每轮都构建并进入一个全新的 Antigravity SDKAgent
  2. 从 ADK 会话状态中读取并恢复会话 ID(conversation ID);
  3. 把最新用户输入发送进去,并把流式返回的每个Step转换为标准 ADKEvent(覆盖模型文本响应、函数调用、函数响应三类);
  4. 轮与轮之间不保持任何打开的资源:SDKAgent实例在退出时被关闭,下一轮重新连接。连续性完全来自会话 ID——包装器把它读写到 ADK 会话状态中。

对应到源码实现(主类):

  • 会话 ID 的存储键为_antigravity_conversation_id_前缀 + 代理名(_conversation_id_state_key,见 L317-L320)。这意味着同一个 ADK 会话中的两个AntigravityAgent实例各自维护独立的 Antigravity 对话,互不干扰。
  • 会话 ID 的持久化通过事件携带的EventActions(state_delta=...)完成(_conversation_id_event,见 L322-L336)。源码注释特别强调:它必须是独立事件而非合并进模型事件,因为部分事件(partial event)不会被追加到会话中,而state_delta正是在会话落盘时才被应用。
  • 恢复会话时,配置会被设置为SessionContinuationMode.CREATE_OR_RESUME(见 L360-L367):这是唯一能在底层存储已不存在时优雅降级为"新建会话"的模式,否则默认模式下"找不到存储"会直接抛硬错误。
  • 连接 SDK 代理使用了异步上下文管理器async with;若__aenter__期间被取消,会显式调用__aexit__以避免孤儿化 harness 子进程(见 L369-L376)。
  • 会话 ID 的写入时机经过精心设计:即使某轮步骤不产生任何用户可见事件(例如 compact 压缩步骤),只要对话有历史,就仍要记录 ID,否则下一轮会"孤儿化"这段对话(见 L403-L432)。

Step 到 Event 的映射规则

转换逻辑被独立抽取到 _event_converter.py,以保证映射规则可读、可单独测试。核心入口是convert_step_to_events(L334-L381),每收到一个Step按以下顺序产出事件:

AntigravityStep内容产出的 ADK 事件
思考增量thinking_deltapartial=True的模型思考事件(仅 SSE 流式模式)
文本增量content_deltapartial=True的模型文本事件(仅 SSE 流式模式)
完整模型文本响应一个author=代理名的最终模型文本事件
模型发起的工具调用author=代理名function_call事件
工具执行完成/出错author=工具名function_response事件

几个值得注意的实现细节(均可从源码确认):

  • 最终文本只在is_complete_response时发出:Antigravity SDK 会在响应增长过程中反复重播累积的content,若每次都发会造成同一消息被记录多次,因此只在完整响应时用最终累积文本生成事件(L99-L131)。
  • SSE 流式模式下才生成增量事件_run_turn会检查run_config.streaming_mode == StreamingMode.SSE(L452-L454);非流式模式只产出最终事件。
  • 工具调用 ID 去重:当 SDK 省略调用 ID 时,会合成{step_index}-{name}形式的稳定 ID(_build_tool_call_id,L48-L50),并用seen_tool_calls/seen_tool_results集合对跨步骤重播的调用与结果去重。
  • final_model_text用于把事件中的用户可见文本读出来,它过滤掉 partial、思考(thought)与函数部件,多段文本以换行拼接(L384-L412)。
  • 已知边界:SDK 在回合取消时发出的SYSTEM_MESSAGE步骤目前被丢弃,尚未映射为 ADK 事件(见 _event_converter.py 顶部 TODO)。

配置选项

官方指南给出了一张配置参数表:

选项类型默认值说明
configAgentConfig(必填)描述 Antigravity SDK 代理的google.antigravity.AgentConfig
modeLiteral['single_turn'] \| NoneNone作为子代理使用时的组合模式。

两个参数在源码中的定义与细节如下(L161-L178):

config:Antigravity SDK 代理的全部定义

config定义了 Antigravity SDK 的指令(system_instructions)、工作区(workspaces)与策略(policies)。源码中它被标记为Field(exclude=True)——即从序列化中排除,因为它持有运行期接线(例如可调用工具),不是 JSON 可序列化的。

当使用LocalAgentConfig时,多轮连续对话必须提供save_dir。原因(指南与源码双重印证,见 L186-L202):本地配置在每次连接时会新建一个临时目录,如果没有save_dir每一轮写入的位置都不是下一轮会去查找的位置,即"每轮都是全新对话"。源码会在构造时检查并打印警告日志(_warn_if_local_without_save_dir):若检测到本地配置且未设置save_dir且非单轮模式,会警告"该代理不会跨轮记住任何东西"。注意它检查的是基类BaseLocalAgentConfig(而非默认子类),因为临时目录的创建逻辑在基类里,任何子类缺save_dir都会"失忆"(L265-L271)。

另外,config在每轮运行前会被深拷贝model_copy(deep=True),见 L296-L299):一方面避免修改调用方的配置对象,另一方面因为 SDKAgentAsyncExitStack是单次使用的,不能复用同一配置。

mode:子代理组合模式

mode控制AntigravityAgent如何被嵌套在 ADK 父代理之下:

  • mode=None(默认):作为独立的 ADK 根代理运行。
  • mode='single_turn':允许该代理拥有 ADK 父代理。此时父LlmAgent会把它暴露为一个内联工具,工具签名接收一个request字符串;父代理负责组合请求,会话历史不会被转发,每次单轮调用都是独立对话,前后不继承任何内容。

源码中modefrozen=True的字段,因为"收养检查"(adoption guard)只在构造时执行一次。该守卫在__setattr__中实现(L240-L248):如果尝试给AntigravityAgent设置非空的parent_agentmode != 'single_turn',会抛出ValueError,提示文案为:

AntigravityAgent may only be an ADK sub-agent when it sets mode='single_turn'... Otherwise it must run as an ADK root agent.

这与官方指南"Limitations"一节的第一条限制完全对应:AntigravityAgent 运行的是自包含的 Antigravity SDK 对话,因此除非设置mode='single_turn',否则它必须是 ADK 根代理

高级应用:给 AntigravityAgent 挂 ADK 子代理

官方指南的进阶场景是:AntigravityAgent可以拥有 ADKsub_agents。每个 ADK 子代理会被桥接到 Antigravity SDK 配置上,变成一个以子代理名字命名的客户端工具(client-side tool)。该工具接收一个request字符串,因此每个子代理必须提供非空的description——这是 Antigravity SDK 模型在选择是否调用时唯一能读到的信息。子代理在隔离环境中运行,只返回其最终文本;父会话记录一次工具调用和携带该最终文本的function_response

from google.adk.agents.llm_agent import Agent def get_current_time(city: str) -> dict: return {"status": "success", "report": f"The time in {city} is 12:00 PM."} time_agent = Agent( name="time_assistant", description=( "Returns the current time. Always call this for time-related queries." ), instruction="Answer time questions by calling get_current_time.", tools=[get_current_time], ) root_agent = AntigravityAgent( name="antigravity_assistant", description="Runs an Antigravity SDK agent inside ADK.", config=sdk_config, sub_agents=[time_agent], )

底层机制:客户端工具桥接

桥接实现在 _sub_agent_tools.py 的make_sub_agent_tool(L50-L122):

  • 子代理的名字成为工具名(__name__),子代理的description成为工具描述(__doc__),两者都是模型读取的信息来源。
  • 每次调用该工具时,会用Runner+ 全新的InMemorySessionService以隔离会话运行子代理一次,返回其最后一个用户可见文本;若没有文本则回退到最后一个错误消息,再不行返回''(绝不返回None)。
  • 这与 ADK 自带的AgentTool非常接近,区别是不返回代码执行输出与可执行代码;且子代理抛出的异常会原样传播给调用方(AgentTool则会把失败包装成错误字符串)。
  • 工具的Runnerfinally中显式关闭,以保证子代理的 MCP 会话、工具集等资源被正确释放——否则遗留的 MCP 会话会在后续出现 "Attempted to exit cancel scope in a different task" 之类的错误。
  • 导入Runner被刻意延迟到函数内部(L71),因为runners会拉入大半个 ADK,在 labs 模块顶层立即导入容易形成循环依赖。

构造期校验:名字与描述

_validate_sub_agents(L204-L238)中,构造时会校验三种冲突并抛出ValueError

  1. 子代理缺少description——它作为工具被暴露,描述是模型唯一决策依据;
  2. 子代理名字与config.tools中已有工具重名——它们会被追加到同一个config.tools,harness 按名字注册工具,重名者会被拒绝;
  3. 两个子代理重名——原因同上。

注意该校验在构造后(例如model_copy或后续修改sub_agents)还会被_build_sdk_config再次调用,防止绕过model_post_init

客户端工具的结果捕获

一个关键设计点是:客户端工具(ADK 子代理)的执行结果永远不会出现在轨迹(trajectory)里——其终止Steptool_calls为空,且Step本身没有结果字段。结果只能通过工具钩子到达。因此 _tool_result_capture.py 实现了两个钩子类,共用一个ToolResultBuffer

  • ToolResultCapturePostToolCallHook):成功路径,在post_tool_call时把ToolResultid存入缓冲;
  • ToolErrorCaptureOnToolErrorHook):失败路径,在on_tool_error时把ToolExecutionError包装为_FailedToolResult存入缓冲。两个钩子互斥——每次调用恰好触发其一(见 _tool_result_capture.py 模块文档)。

缓冲只在存在sub_agents时才注册(因为post_tool_call钩子每次成功调用都会带来一次阻塞往返),且只在 AntigravityAgent 拥有子代理时为真(L355-L359)。回合结束后,_run_turn会调用drain_tool_results把已捕获但尚未答复的调用配对成function_response事件,再清空缓冲(L470-L484)。

运行模式与运行时约束

作为工作流节点(Node)运行

AntigravityAgent同时实现了节点运行路径_run_impl(L486-L521):它把node_input转成用户内容,收集整轮最后一段模型文本作为output产出,从而可以作为 ADK 工作流(workflow)中的节点使用,支持node_input注入与output输出。这从源码结构看是复用run_async主路径的实现。

静默丢失恢复(silent-drop detection)

针对"要求恢复某会话但底层已不存在"的情况,源码对本地连接实现了检测:恢复后若对话历史为空,判定为静默新建了会话,此时会清除存储的 ID 并抛出RuntimeError,提示"已清除存储的 ID,下一轮将开始新对话,但本会话之前的回合不可恢复"(L394-L402)。该检测被限定在本地配置类型内,因为远程后端在陈旧 ID 上静默新建会话的行为无法在不依赖 SDK 支持的前提下被可靠区分(L434-L445)。

已知限制

官方指南明确列出的限制如下(并可与源码互证):

  1. 嵌套限制AntigravityAgent运行的是自包含的 Antigravity SDK 对话,因此除非设置mode='single_turn',否则它必须是 ADK 根代理。这条限制仅适用于该代理被放在 ADK 父代理之下的场景;它自己的 ADKsub_agents是被桥接为客户端工具的,永远不需要设置mode
  2. 子代理的 root 解析AntigravityAgent的 ADK 子代理的root_agent仍指向最外层 ADK 代理树。在三层结构中(LlmAgentAntigravityAgent(mode='single_turn')→ 子代理),中间的代理设置mode='single_turn'是因为它有 ADK 父代理,而不是因为它有子代理;此时 ADK 的 transfer 工具会被声明给子代理的模型。建议让AntigravityAgent的子代理保持"叶节点"形态,或者在这些子代理上设置disallow_transfer_to_parentdisallow_transfer_to_peers
  3. 并发限制:同一 ADK 会话的两轮并发运行是未定义行为,因为两者会打开同一个已存储的会话。

相关示例与进一步阅读

  • Game Developer Agent:一个独立的 Antigravity SDK 代理,在工作区内把浏览器游戏写成自包含 HTML。它同时演示了工作区目录预创建、workspace 限定策略与save_dir的标准用法,是理解本文全部概念的完整落地样例。
  • 模块 README:简短索引,指向本文档。
  • AntigravityAgent 单元测试:覆盖会话 ID 持久化、单轮模式、子代理校验与节点运行路径等行为,是理解边界条件的补充材料。
  • 事件转换器测试、子代理工具测试、工具结果捕获测试:分别验证 Step→Event 映射、子代理工具包装与钩子缓冲逻辑。

需要提醒的是:labs模块属于实验性集成,其 API 面向的是"把另一套 Agent 运行时嵌入 ADK"这一具体需求;生产使用前请确认你的 ADK 版本与google-antigravity包的兼容性,并严格按pip install "google-adk[antigravity]"安装依赖。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询