
作者来自 Elastic Jonathan SimonElastic Agent Builder 的原生 A2A 端点让 Google 的 ADK 可以编排远程 agent而无需任何自定义 REST API。观看它如何在 “幸运星球” 中工作 —— 一个从头到尾构建的随机系外行星游戏。Agent Builder 现已正式发布GA。立即开始使用 Elastic Cloud Trial并查看 Agent Builder 文档。Elastic Agent Builder 提供原生的 Agent2AgentA2A端点任何兼容 A2A 的框架都可以编排你的 Elastic agent而无需编写任何自定义 REST 路由。为了证明这一点我们构建了Lucky Planet一个多人游戏其中 Google 的 Agent Development KitADK通过 A2A 驱动远程 Elastic agent从实时 NASA 数据集中随机选择系外行星并宣布获胜者。Elastic agent 负责所有数据检索retrieval和游戏逻辑。ADK 与 Flask 前端从未调用任何自定义 API。下面将介绍如何从零开始构建整个系统。前提条件为了跟随本文的步骤操作你需要准备以下内容在本地计算机上运行的文本编辑器。我们在本文示例说明中使用的是 Visual Studio Code。在本地计算机上运行的 Python 3.10 或更高版本。创建 Elasticsearch Serverless 项目要使用 Agent Builder 构建 agent我们需要一个 Elastic deployment 或 project因此首先来创建一个。Elastic Serverless 是最简单的入门方式。只需访问 Elastic Cloud 并注册账号。然后创建一个 Elasticsearch 项目。导入 CSV 文件以创建 Elasticsearch 索引首先我们需要一些关于行星的数据。NASA Exoplanet Archive 是获取银河系内太阳系外已发现行星数据的理想来源。在浏览器中查看 NASA 行星数据。将行星数据以文件名planets.csv保存到本地计算机。回到 Elastic Cloud在Getting Started页面中点击Add data然后选择Upload a file。将Index name指定为planets-raw。点击Select or drag and drop files链接然后选择之前保存的planets.csv文件。最后点击Import。使用 Agent Builder 与你的数据聊天从顶级导航菜单中选择Agents。Agent Builder 提供了一个默认 agent名为 “Elastic AI Agent”你可以使用它与你的数据进行对话。输入如下提示词我有哪些数据请提供详细信息。你应该会看到系统返回一个名为planets-raw的索引以及字段名称列表其中包括每个字段的数据类型和描述信息。请注意sy_dist字段的描述为“到恒星系统的距离很可能以秒差距 parsecs 为单位”。我们希望游戏中的行星距离使用光年light-years而不是秒差距parsecs。使用光年作为行星距离数据的单位更合适因为它是理解这些巨大距离最直观的方式之一。光年基于 “一年的时间长度” 和 “光速” 来定义。例如对于一颗距离 4.2 光年的行星如果你能够以光速旅行那么到达那里需要 4.2 年。此外使用更具描述性的字段名称会更好。我们可以通过创建一个新的索引来实现这一点。点击顶级导航菜单中的Developer Tools。复制以下_reindex请求。该请求会使用更新后的字段名称以及新的 “距地球光年距离light years distance from Earth” 字段来填充planets索引。由于该索引尚不存在因此会自动创建。POST _reindex { source: { index: planets-raw }, dest: { index: planets }, script: { source: ctx._id ctx._source.pl_name; ctx._source.planet_name ctx._source.remove(pl_name); if (ctx._source.sy_dist ! null) { ctx._source.light_years_distance_from_earth Math.round(ctx._source.sy_dist * 3.26156 * 100) / 100; } ctx._source.remove(sy_dist); } }将该请求粘贴到Developer Tools中然后点击Click to send request按钮来发送POST _reindex请求。你应该会看到一个200 OK的确认信息用来表明新的planets索引已经成功创建。很好我们已经在 Elastic 中准备好可用的数据了。现在让我们来构建一个可以使用这些数据的 agent。选择顶层导航中的Agents打开 Agent Builder。在 Elastic Agent Builder 中创建工具我们将通过创建一个 Model Context ProtocolMCP工具来开始构建 agent 的过程。Agent Builder 支持创建不同类型的工具这些工具可以执行索引搜索、运行 Elasticsearch Query Language (ES|QL) 查询、执行 workflows以及调用其他 MCP 工具。在 Agent Builder 中创建的每个工具都会以托管 MCP server 的形式暴露出来可以被任何外部 MCP client 调用。在 Agent Builder 中选择Tools。点击Manage all tools查看当前工具列表。点击 New tool。在Create Tool创建工具表单中将工具Type类型选择为 ES|QL并填写以下内容。在ES|QL Query文本区域中输入下面的查询用于从我们的planets索引中随机选择一颗行星FROM planets | EVAL rand_key hash(md5, CONCAT(planet_name, TO_STRING((NOW())))) | SORT rand_key ASC | KEEP planet_name, light_years_distance_from_earth, ra, dec | LIMIT 1在Tool ID工具 ID中填写get_random_planet在Description描述中填写Get a random planet.完成后的Create tool创建工具表单应该类似下图所示。点击Save保存来创建该工具。创建 agent 并为其分配工具Agent Builder 中创建 agent 非常简单。尤其是我们刚刚创建的 MCP 工具将由该 agent 使用同样托管在 Elastic 上因此可以很方便地将其分配给 agent。点击Manage agents。点击 New agent。在New Agent新建 Agent表单中输入以下信息。在Agent ID中输入lucky_planet_agent在Custom Instructions自定义指令文本区域中输入以下指令。如你所见我们不再使用基于 Query DSL 或 ES|QL 的确定性 REST 调用而是可以通过简单指令让 agent 自主完成数据访问通过 MCP并依赖其上下文理解与推理能力。该 agent 将负责自动适配多人游戏逻辑并在游戏结束时生成相关且有趣的结果总结。You are the game master for a game called Lucky Planet. Start the game when prompted. # Game Sequence: 1) At the beginning of each game youll say: Lets play Lucky Planet! Rolling the cosmic dice... 2) Select a planet for the Agent Planet using the get_random_planet tool. 3) Select a planet for the Player Planet using the get_random_planet tool. 4) Ensure each player has a unique planet. 5) Conclude game. # Game Conclusion: Display each game players planet along with its distance (formatted with commas) from Earth and announce the game winner. Use this exact output for the first lines of your Game Conclusion response: ----- Agent Planet: planet_name - light_years_distance_from_earth light years from Earth (ra: ra dec: dec) Player Planet: planet_name - light_years_distance_from_earth light years from Earth (ra: ra dec: dec) The winner is game winner! ----- # Multi-Player Option If the game is invoked with a prompt containing a list of players, use only those players to play the game by getting a random planet for each player and adapt the Game Conclusion response for multiple players. Sort the list of players ordered by closest planet to furthest. An example multi-player prompt would be: start game Socrates, Plato, and Aristotle.在Display name显示名称中输入以下文本Lucky Planet Agent在Display description显示描述中输入以下文本Agent that plays the game Lucky Planet.然后通过点击Tools工具标签将我们之前创建的自定义工具分配给该 agent。只选择我们之前创建的get_random_planet工具。然后点击Save and chat保存并聊天来保存该 agent并尝试使用它。只需输入 “play” 来开始Lucky Planet游戏。play game Gandalf, Frodo, Strider很棒游戏已经可以运行了。虽然Lucky Planet本身已经很有趣但如果能把玩家选中的行星在银河系Milky Way中的位置可视化出来会更酷。刚好有一个开源 Python 库可以做到这一点mw-plot。现在我们只需要一个基于 Python 的 agent 编排框架即可。由于我们是在 Agent Builder 中构建的 agent这意味着它可以被任何支持 A2A Protocol 的 agent 开发工具直接使用。事实证明用于构建 agentic A2A 应用的优秀开源工具包之一是由 Google 开发的 Agent Development Kit (ADK)。它支持 Python、Go、Java 和 TypeScript。在 Python 中开始使用 ADK现在让我们打开代码编辑器并运行一些代码。在你的本地计算机上打开 Visual Studio Code然后打开一个新的终端。在新打开的终端中运行以下命令从 GitHub 克隆 Lucky Planet 应用代码git clone https://github.com/jsimonweb/lucky-planet在终端中使用cd切换到lucky-planet目录cd lucky-planet在终端中输入以下命令用 Visual Studio Code 打开当前文件夹code -r .下面是克隆后的 lucky-planet 仓库在 Visual Studio Code 中的示意结构。该应用的基础架构是一个前端 HTML UI/templates/index.html通过 Python Flask 后端调用 ADK从而与 Elastic agent 进行请求与响应交互。当玩家输入玩家名称并点击开始游戏按钮后前端会向 Flask 的/api/ask路由发送一个POST请求该路由位于app.py文件中。app.route(/api/ask, methods[POST]) def ask(): 立即返回 agent 文本 —— 绘图生成是一个单独调用 try: players request.get_json(forceTrue).get(players, []) prompt play game , .join(players) if players else play game response_text asyncio.run(_send_a2a(prompt)) return jsonify({response: response_text})在这个路由内部会调用_send_a2a()方法并通过asyncio.run(_send_a2a(prompt))执行。这里 ADK 从 Elastic agent 收集到的响应会被存入response_text然后通过 Flask 的jsonify工具以 JSON 形式返回给前端。接下来我们来看_send_a2a()方法这是 ADK 与 Agent Builder agent 交互的核心位置# ── A2A 调用 ───────────────────────────────────────────────────────────────── async def _send_a2a(prompt: str) - str: auth {Authorization: fApiKey {ELASTIC_API_KEY}} async with httpx.AsyncClient(headersauth, timeout120.0) as http_client: resolver A2ACardResolver(httpx_clienthttp_client, base_urlELASTIC_AGENT_URL) card await resolver.get_agent_card( relative_card_path/lucky_planet_agent.json ) remote_agent RemoteA2aAgent( nameelastic_agent, agent_cardcard, httpx_clienthttp_client, ) session_service InMemorySessionService() async with Runner( agentremote_agent, app_nameAPP_NAME, session_servicesession_service, ) as runner: session await session_service.create_session( app_nameAPP_NAME, user_iduser, session_idstr(uuid.uuid4()), ) new_message genai_types.Content( roleuser, parts[genai_types.Part(textprompt)], ) final_text (no response) async for event in runner.run_async( user_iduser, session_idsession.id, new_messagenew_message, ): if event.is_final_response() and event.content and event.content.parts: texts [p.text for p in event.content.parts if p.text] if texts: final_text \n.join(texts) await remote_agent.cleanup() return final_text该方法首先创建一个 HTTP client并在请求头中注入 Elastic API key用于客户端授权。auth {Authorization: fApiKey {ELASTIC_API_KEY}} async with httpx.AsyncClient(headersauth, timeout120.0) as http_client:该方法随后使用这个 HTTP client 调用A2ACardResolver用于获取 Elastic agent 的 “card”。A2A agent card 是一个 JSON 清单manifest描述该 agent 能做什么、暴露哪些端点以及如何与它通信。A2ACardResolver在这里执行的是 A2A handshake握手过程其中包含 Elastic agent URL以及对应lucky_planet_agentAgent ID的lucky_planet_agent.json该 ID 就是我们在 Agent Builder 中创建 agent 时设置的。resolver A2ACardResolver( httpx_clienthttp_client, base_urlELASTIC_AGENT_URL )card await resolver.get_agent_card( relative_card_path/lucky_planet_agent.json )ADK 的RemoteA2aAgent会使用这个 agent card把远程 Elastic agent “包装”为一个看起来和本地 ADK agent 完全一致的对象。remote_agent RemoteA2aAgent( nameelastic_agent, agent_cardcard, httpx_clienthttp_client, )随后 ADK 会向 Elastic agent 发送消息本质上就是一个文本 prompt例如play game Lovelace, Shannon, TuringRunner 会流式返回 ADK events。应用只等待is_final_response()忽略中间的思考与 tool call 事件并收集最终的自然语言结果。new_message genai_types.Content( roleuser, parts[genai_types.Part(textprompt)] )async for event in runner.run_async(..., new_messagenew_message): if event.is_final_response() and event.content and event.content.parts: final_text \n.join(texts)最后ADK 关闭RemoteA2aAgent所使用的 HTTP client并释放资源。await remote_agent.cleanup()return final_textA2A 的作用是把远程 Elastic agent 变成一个可以用标准协议对话的对象。应用不需要调用自定义 REST endpoint例如POST /api/v1/run-game而是直接通过 A2A 通信因此接口变成了自然语言而不是函数签名。最终效果是一个游戏复杂的数据查询和游戏逻辑由 Elastic agent 负责而 ADK 负责通过 A2A 与 agent 通信、启动游戏并返回最终结果摘要同时前端再将结果与银河系Milky Way可视化结合展示。设置 agent URL 和 API Key 为环境变量为了让Lucky Planet应用连接到 Elastic Agent Builder agent需要设置两个环境变量A2A endpoint URL 和 Elastic API key。示例应用使用.env文件来存储这些配置。将env.example文件复制一份并命名为.env。返回 Agent Builder 界面获取应用与 Elastic 通信所需的两个值。在 Lucky Planet Agent 的自定义选项中选择Tools工具。点击屏幕右上角的Manage all tools管理所有工具。在 Tools library 页面顶部点击Manage MCP管理 MCP下拉菜单然后选择Copy MCP Server URL复制 MCP 服务器 URL。将MCP Server URL粘贴到.env文件中替换 占位符。Agent Builder 暴露两个端点/mcp用于向 MCP client 提供工具/a2a用于向 A2A client 提供 agent。这里我们使用/a2a。因此需要把 URL 末尾的 “mcp” 修改为 “a2a”因为 ADK 将通过 A2A 与运行在 Agent Builder 中的 agent 通信。修改后的 URL 应该类似如下https://luckyplanet-game-project-12345a.kb.us-central1.gcp.elastic.cloud/api/agent_builder/a2a接下来我们需要 API key。点击 Elastic Cloud 顶部导航中的Getting started入门。点击Copy API key button复制 API key 按钮来复制 API key。回到 Visual Studio Code将 API key 粘贴到.env文件中替换 占位符文本。你的.env文件应该类似如下所示运行应用要运行Lucky Planet应用请按以下步骤操作1. 创建 Python 虚拟环境python -m venv .venv2. 激活虚拟环境根据你的操作系统执行对应命令MacOS / Linuxsource .venv/bin/activateWindows.venv\Scripts\activate3. 安装依赖Lucky Planet应用依赖 ADK 进行 agent 编排因此需要先安装 ADK 以及所有 Python 依赖pip install -r requirements.txt4. 启动应用好了是时候戴上“安全头盔”运行应用了。使用以下命令启动python app.py运行python app.py命令后应该会输出一个 URL例如 http://127.0.0.1:5001你可以在浏览器中打开它来查看正在运行的Lucky Planet游戏。我们来试试看用你的数据上下文构建 AI 应用正如你在这篇博客中看到的Elastic Agent Builder 为你提供了快速构建基于自身数据上下文的 agent 所需的一切能力。这包括一个原生的 A2A endpoint可以直接被任何兼容框架编排使用。试用 Elastic Cloud构建具备数据上下文与检索能力的 agent用于解决你真正关心的问题。这份内容对你有多大帮助原文Elastic Agent Builder A2A: orchestrate agents with Google ADK - Elasticsearch Labs