ADK Python 实战:将远程 A2A 代理作为根代理(A2A Root Sample 全解析) 📅 发布时间:2026/9/13 13:23:15 👁 浏览次数: ADK Python 实战将远程 A2A 代理作为根代理A2A Root Sample 全解析【免费下载链接】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本文以 Agent Development KitADKPython官方示例a2a_root为核心完整讲解如何**把一个远程 Agent-to-AgentA2A服务作为主代理root agent**来驱动整个会话远端用 uvicorn 直接启动一个基于to_a2a()转换的 A2A 服务本地用RemoteA2aAgent以 Agent Card 方式接入。读完本文你将掌握「标准 ADK 代理 → A2A 服务 → 远程根代理」这条分布式部署链路的完整落地方法并能基于仓库源码理解其底层机制。示例定位与整体架构a2a_root示例位于 contributing/samples/a2a/a2a_root其核心思路是主代理本身并不包含业务逻辑而是一个远程 A2A 代理的代理proxy。整个示例由两个进程组成角色文件职责根代理Root Agentcontributing/samples/a2a/a2a_root/agent.py一个RemoteA2aAgent通过 A2A 协议连接远端服务充当本地入口远程 Hello World 代理contributing/samples/a2a/a2a_root/remote_a2a/hello_world/agent.py真正执行掷骰子、素数检查等逻辑的代理运行在独立服务器上原文档给出的架构示意如下┌─────────────────┐ ┌────────────────────┐ │ Root Agent │───▶│ Remote Hello │ │ (RemoteA2aAgent)│ │ World Agent │ │ (localhost:8000)│ │ (localhost:8001) │ └─────────────────┘ └────────────────────┘本地根代理由adk web服务承载监听localhost:8000远程 A2A 代理由 uvicorn 承载监听localhost:8001两者之间通过 A2A 协议通信根代理先读取远端的 Agent Card机器可读的代理能力描述再把用户任务以 JSON-RPC 形式投递过去。这种「远程代理即主代理」的模式展示了 A2A 架构在分布式代理部署上的灵活性你可以把一个团队开发的代理部署为独立 Web 服务然后在任意位置以极小配置量复用它。四大关键特性原文档将本示例的价值提炼为四点逐一展开1. 远程 A2A 作为根代理根代理是RemoteA2aAgent它不运行本地 LLM 推理而是把请求转发给远程 A2A 服务。这演示了如何用远程代理替代本地代理作为主代理是 A2A 分布式部署能力最直观的体现。关于RemoteA2aAgent的客户端机制详见后文「RemoteA2aAgent 客户端机制」一节。2. Uvicorn 服务器部署远程代理使用 uvicorn轻量级 ASGI 服务器启动而不是 ADK CLI。这给出了一种不依赖 ADK 命令行、把 A2A 代理暴露为独立 Web 服务的简洁部署路径uvicorn contributing.samples.a2a.a2a_root.remote_a2a.hello_world.agent:a2a_app \ --host localhost --port 80013. 代理业务能力远程代理具备四类能力覆盖了函数工具、异步工具、状态管理与并行调用的典型用法掷骰子Dice Rolling可配置骰子面数的roll_die工具素数检查Prime Number Checking批量判断数字是否为素数的check_prime异步工具状态管理State Management在工具上下文中维护滚动历史记录tool_context.state[rolls]并行工具执行Parallel Tool Execution代理被指令引导为「一次请求、同一轮内」并行调用多个工具。4. 极简部署模式远程侧使用to_a2a()工具函数把一个标准 ADK 代理转换为 A2A 服务无需额外配置即可完成远程代理的部署。to_a2a的完整参数与底层原理见后文专节。环境准备与启动步骤前置条件按原文档需要依次启动两个服务第一步启动远程 A2A 代理服务器终端一# Start the remote agent using uvicorn uvicorn contributing.samples.a2a.a2a_root.remote_a2a.hello_world.agent:a2a_app --host localhost --port 8001其中a2a_app是模块内通过to_a2a(root_agent, port8001)创建的 Starlette 应用详见源码 remote_a2a/hello_world/agent.py。注意此处模块路径必须以仓库根目录为基准且大小写精确--port 8001必须与根代理 Agent Card 中记录的端口一致。第二步运行主代理另开一个终端# In a separate terminal, run the adk web server adk web contributing/samples/a2aadk web会以 Web UI 方式启动本地根代理指定目录为contributing/samples/a2a即包含所有 A2A 示例的父目录根代理读取 agent.py 中定义的RemoteA2aAgent。交互示例两个服务就绪后即可与根代理对话原文档示例完整保留简单掷骰子User: Roll a 6-sided die Bot: I rolled a 4 for you.素数检查User: Is 7 a prime number? Bot: Yes, 7 is a prime number.组合操作掷骰子 素数判断User: Roll a 10-sided die and check if its prime Bot: I rolled an 8 for you. Bot: 8 is not a prime number.多次掷骰并筛选素数User: Roll a die 3 times and check which results are prime Bot: I rolled a 3 for you. Bot: I rolled a 7 for you. Bot: I rolled a 4 for you. Bot: 3, 7 are prime numbers.最后一次交互展示了状态管理的能力roll_die把每次结果追加到工具上下文状态中check_prime可以基于历史滚动结果批量判断。代码结构深度解析根代理agent.pyRemoteA2aAgent 的极简用法contributing/samples/a2a/a2a_root/agent.py 全文件只有一次构造调用from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH from google.adk.agents.remote_a2a_agent import RemoteA2aAgent root_agent RemoteA2aAgent( namehello_world_agent, description( Helpful assistant that can roll dice and check if numbers are prime. ), agent_cardfhttp://localhost:8001/{AGENT_CARD_WELL_KNOWN_PATH}, )关键点agent_card参数指向远端 well-known 端点AGENT_CARD_WELL_KNOWN_PATH由 src/google/adk/agents/remote_a2a_agent.py 导出优先从a2a.utils.constants导入旧版 a2a-sdk 兜底为/.well-known/agent.json。根代理正是通过这个 URL 拉取远端代理的「名片」。description会被父代理用于构建转移指令从源码看若传入的 Agent Card 本身带描述而description为空构造器会从卡片自动填充描述见RemoteA2aAgent.__init__对AgentCard类型的处理而通过网络拉取的卡片描述会被截断到 1024 字符并作为「不可信引用文本」处理_adopted_card_description防止远端注入恶意指令。远程 Hello World 代理remote_a2a/hello_world/agent.pycontributing/samples/a2a/a2a_root/remote_a2a/hello_world/agent.py 是业务逻辑所在包含四个核心构件roll_die(sides: int, tool_context: ToolContext) - int有状态函数工具。除返回random.randint(1, sides)外还把每次结果追加到tool_context.state[rolls]列表为后续「基于历史掷骰结果判断素数」提供状态支撑def roll_die(sides: int, tool_context: ToolContext) - int: result random.randint(1, sides) if not rolls in tool_context.state: tool_context.state[rolls] [] tool_context.state[rolls] tool_context.state[rolls] [result] return resultcheck_prime(nums: list[int]) - str异步函数工具。采用试除法遍历 2 到sqrt(n)判断批量数字是否为素数返回人类可读的汇总字符串无素数时返回No prime numbers found.。root_agent带详尽指令的主代理。指令文本是关键工程细节——它明确要求掷骰子必须调用roll_die工具并传整数禁止传字符串、禁止自行掷骰必须先等roll_die返回再调用check_prime回答时必须包含roll_die的结果检查素数时禁止依赖历史记录中的旧结果。这种「指令即协议」的做法是让 LLM 稳定走完「掷骰 → 判断 → 汇报」多步工具链的保障。此外该代理还做了两处实用配置generate_content_config关闭了HARM_CATEGORY_DANGEROUS_CONTENT安全阈值源码注释说明是为了避免掷骰子被误判为危险内容被注释掉的plannerBuiltInPlanner(...)展示了可选升级路径若需思考过程可见可启用内置规划器。a2a_app通过to_a2a(root_agent, port8001)创建的 Starlette 应用供 uvicorn 加载。to_a2a把 ADK 代理变成 A2A 服务to_a2a定义于 src/google/adk/a2a/utils/agent_to_a2a.py官方指南见 docs/guides/a2a/utils/agent_to_a2a/index.md。它接受一个BaseAgent或Workflow返回一个会说 A2A 协议JSON-RPC over HTTP的 Starlette 应用启动后暴露两条路由一条是接收任务的 JSON-RPC 端点另一条是发布代理能力描述的 well-known Agent Card 端点如http://localhost:8001/.well-known/agent-card.json。官方指南给出的最小示例与本示例的远程代理写法一致from google.adk import Agent from google.adk.a2a.utils.agent_to_a2a import to_a2a root_agent Agent(namehello_world_agent, tools[roll_die], ...) a2a_app to_a2a(root_agent, port8001) # 然后: uvicorn my_module:a2a_app --host localhost --port 8001参数速查表来自官方指南选项类型默认值说明agentBaseAgent \| Workflow必填要服务的单元位置参数其余均为关键字参数hoststrlocalhost写入 Agent Card 广告 RPC URL 的主机名不执行绑定portint8000写入 Agent Card 广告 RPC URL 的端口不执行绑定protocolstrhttp写入广告 URL 的协议rpc_pathstr两条路由的挂载路径前缀首尾斜杠会被去除agent_cardAgentCard \| str \| NoneNone预构建的卡片或卡片 JSON 文件路径push_config_storePushNotificationConfigStore \| NoneNone推送通知配置存储task_storeTaskStore \| NoneNoneA2A 任务状态存储runnerRunner \| NoneNone预构建 Runner替代内存版默认实现lifespanCallable[[Starlette], AbstractAsyncContextManager[None]] \| NoneNone自定义启动/关闭逻辑agent_executor_factoryCallable[[Runner], A2aAgentExecutor] \| NoneNone给定 Runner 构建执行器两个必须理解的底层行为port要写两遍且这不是笔误。to_a2a本身从不打开 socket——它只是把protocol://host:port拼进 Agent Card真正监听端口的是 uvicorn。因此示例中to_a2a(..., port8001)与uvicorn ... --port 8001必须一致否则服务器正常启动、但所有读取卡片的客户端都会连错地方。源码中rpc_url f{protocol}://{host}:{port}{prefix}/仅用于构建卡片与AgentCardBuilder印证了这一设计。调用to_a2a时几乎不做实事。真正的装配发生在 ASGI 服务器启动Starlette lifespan阶段解析Runner默认由InMemorySessionService、InMemoryArtifactService、InMemoryMemoryService、InMemoryCredentialService四个内存服务构建、构建A2aAgentExecutor、解析TaskStore与PushNotificationConfigStore、调用AgentCardBuilder.build()从代理派生卡片每个进程只构建一次启动后运行期改动的工具列表不会反映到卡片上最后挂载路由。因此卡片构建失败会表现为 uvicorn 的启动错误而非 import 时的异常。部署进阶与限制官方指南要点持久化默认全部使用内存实现进程退出即丢失一切。可通过runner传入带DatabaseSessionService的 Runner、task_storeDatabaseTaskStore(engine...)持久化并自行用lifespan释放引擎资源。自定义卡片自动构建的卡片不携带 provider、安全方案版本固定为0.0.1需要用AgentCardBuilder自行构建后以agent_card传入。实验性警告to_a2a标注了a2a_experimental每次调用都会发出UserWarning可用环境变量ADK_SUPPRESS_A2A_EXPERIMENTAL_FEATURE_WARNINGS1关闭。A2A 协议本身并非实验性实验的是 ADK 对该协议的实现。流式输出默认关闭自动构建的卡片capabilities为空streaming: falsemessage/stream请求会被拒绝如需流式必须自行构建AgentCapabilities(streamingTrue)的卡片传入。一个应用一个代理每次调用to_a2a都返回全新 Starlette 应用同一进程服务多个代理应挂载多个应用而非对同一应用重复调用。RemoteA2aAgent 客户端机制RemoteA2aAgent位于 src/google/adk/agents/remote_a2a_agent.py是to_a2a的客户端另一半。它支持三种方式指定远端代理直接传入AgentCard对象传入 Agent Card JSON 的 URL本示例采用此方式指向 well-known 端点传入本地 Agent Card JSON 文件路径。其主要职责包括Agent Card 解析与校验URL 方式通过A2ACardResolver拉取卡片文件方式从 JSON 解析。解析失败统一抛出AgentCardResolutionError。值得注意的安全设计是_validate_card_rpc_targets凡是从网络拉取的卡片其提供的所有 RPC 端点必须为 https 且与卡片来源同源仅 loopback 主机如 localhost允许明文 http——这正是本示例能在本地用http://localhost:8001跑通的原因。消息双向转换通过GenAIPartToA2APartConverter/A2APartToGenAIPartConverter在 ADK 事件Event与 A2A 消息/任务之间互转支持任务状态流式更新与产物事件。会话状态管理跨请求维护会话对无状态远端代理可通过full_history_when_statelessTrue让每次请求携带全部历史事件task 模式下默认开启。可配置项timeout默认 600 秒、httpx_client/a2a_client_factoryHTTP 与 A2A 客户端定制、auth_scheme/auth_credential/credential_key向远端代理附加鉴权头凭据按调用缓存、modetask作为父LlmAgent的任务子代理运行要求远端通过finish_task工具显式宣告完成。此外RemoteA2aAgent会对转发内容做「消毒」剥离携带凭据的 part_without_credential_parts、将本地request_input/request_confirmation等人机交互函数调用扁平化为纯文本再转发避免把人类输入答案或 OAuth 凭据泄漏给远端。故障排查原文档给出的排查要点如下连接问题Connection Issues确认 uvicorn 服务器在 8001 端口运行检查是否有防火墙拦截 localhost 连接核对根代理配置中的 Agent Card URL查看 uvicorn 日志中是否有启动错误。代理无响应Agent Not Responding检查 uvicorn 服务器日志中的报错确保代理指令清晰无歧义本示例中多步工具链高度依赖指令约束指令含糊会导致工具调用顺序错乱确认 A2A 应用配置的端口正确。Uvicorn 问题Uvicorn Issues确保模块路径拼写正确contributing.samples.a2a.a2a_root.remote_a2a.hello_world.agent:a2a_app确认所有依赖均已安装包括 ADK、a2a-sdk、uvicorn、starlette。结合前文源码分析还可以补充两条若根代理报AgentCardResolutionError多半是 8001 端口未启动或卡片 URL 拼错若出现「服务器正常但客户端连不上」优先核对to_a2a(port...)与uvicorn --port ...两处端口是否一致。延伸阅读to_a2a官方指南docs/guides/a2a/utils/agent_to_a2a/index.mdAgent Card 构建docs/guides/a2a/utils/agent_card_builder/index.mdA2A 执行器与事件转换docs/guides/a2a/executor/a2a_agent_executor/index.md对比示例a2a_basic 展示了另一条部署路径——通过adk api_server --a2a配合手写agent.json卡片提供服务其中不涉及to_a2a调用仓库内其他 A2A 示例如 a2a_human_in_loop、a2a_auth、a2a_state_forwarding可继续探索 A2A 的人机协同、鉴权与状态转发能力。【免费下载链接】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),仅供参考