资讯动态

在 CopilotKit 中构建 A2A + A2UI 餐厅预订 Agent:基于 Google ADK 的服务端实现与安全实践

发布时间:2026/9/10 7:15:20 来源:尧图企业网站定制
在 CopilotKit 中构建 A2A A2UI 餐厅预订 Agent基于 Google ADK 的服务端实现与安全实践【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit本指南基于 CopilotKit 仓库中的 A2A A2UI 集成示例深入讲解如何用 Google Agent Development KitADK配合 A2AAgent-to-Agent协议将一个餐厅搜索与桌位预订Agent 以 A2A Server 的形式托管并通过 A2UI 消息让前端动态渲染富交互界面。读完本文你将掌握该示例的完整运行方法、源码级实现原理提示词构建、JSON Schema 校验、UI 事件回传、扩展协商以及在生产环境中必须遵循的不可信 Agent 安全准则。示例概览一个用 A2UI 驱动界面的 A2A 服务端 Agent该示例位于 examples/integrations/a2a-a2ui其中agent/子目录是一个独立的 Python 服务端 Agent 程序它的定位是使用 Google ADK 构建 LLM AgentLlmAgent负责理解用户意图、调用工具、生成响应通过 A2A 协议将该 Agent 暴露为标准的 A2A Server供任何符合 A2A 规范的客户端发现与调用借助 A2UIAgent to UI扩展在响应中携带声明式的 UI JSON如组件树、数据模型更新让前端把列表卡片、预订表单、确认卡片等界面直接渲染出来而不是让 Agent 输出纯文本。从整体架构看这是一个典型的三段式链路CopilotKit 前端Next.js→A2A 协议请求→ADK Agent A2UI 扩展响应。前端与 Agent 之间通过 A2A 的 AgentCard、Message/Part 等标准概念通信而 UI 内容则通过 A2UI 的DataPartMIME 类型application/jsona2ui随消息返回。环境准备原文档列出的前置条件如下本节结合仓库的依赖声明做进一步核对Python 3.9 或更高版本。需要注意的是当前仓库中该 Agent 的 pyproject.toml 声明的requires-python 3.13实际运行请以仓库为准若使用仓库锁定的环境建议直接采用 Python 3.13。**UV。可访问的 LLM 与 API Key。默认通过环境变量GEMINI_API_KEY提供详见下文启动参数与配置。核心依赖来自 pyproject.toml包括依赖包版本要求作用a2a-sdk0.3.0A2A 协议的服务端 SDK提供 A2AStarletteApplication、AgentCard 等google-adk1.8.0Google Agent Development Kit构建 LLM Agent 与 Runnergoogle-genai1.27.0Gemini 模型调用客户端litellm-统一模型网关用于LiteLlm模型封装a2uiworkspace 内版本A2UI 扩展辅助库create_a2ui_part等jsonschema4.0.0对 LLM 输出的 A2UI JSON 做 Schema 校验click8.1.8命令行参数解析host/portpython-dotenv1.1.0加载.env环境文件运行示例三步启动 A2A Server原文档给出了三步运行方法这里补充仓库中的实际路径对应关系步骤 1进入示例目录原文档中的a2a_samples/a2ui_restaurant_finder对应本仓库中的 agent 目录cd examples/integrations/a2a-a2ui/agent步骤 2创建环境文件写入 API Keyecho GEMINI_API_KEYyour_api_key_here .env启动代码在 agent/main.py 开头调用load_dotenv()加载该文件。启动前会做一次校验MissingAPIKeyError当GOOGLE_GENAI_USE_VERTEXAI未设置为TRUE时GEMINI_API_KEY必须存在否则进程会打印错误并以退出码 1 结束。步骤 3运行 Agent Serveruv run .uv run .会依据main.py 的入口启动 uvicorn默认绑定localhost:10002。启动完成后该 A2A Server 会通过AgentCard广播自身能力nameRestaurant Agent、version1.0.0、支持 streaming、声明 A2UI 扩展注册一个名为find_restaurants的AgentSkillAgentSkill定义在 agent/main.py描述根据菜系、位置帮助查找餐厅并带有示例查询挂载InMemoryTaskStore管理任务状态DefaultRequestHandler处理 A2A 请求通过 CORS 中间件允许http://localhost:5173前端开发服务器跨域访问将agent/images/目录挂载为/static静态资源供 UI 中的餐厅图片引用如http://localhost:10002/static/shrimpchowmein.jpeg。如果不想手敲命令仓库还提供了封装脚本 run-agent.sh以及 Windows 的run-agent.bat它们内部就是切换到 agent 目录后执行uv run .。源码拆解Agent 是如何工作的1. 核心 Agent 与 Runneragent.pyagent/agent.py 定义了RestaurantAgent类核心要点如下双模式设计构造函数接收use_ui参数。为True时使用指令 UI 提示词构建 Agent能生成 A2UI JSON为False时仅用纯文本提示词。这是为了让不支持 A2UI 扩展的客户端也能获得文本回复。模型配置通过LiteLlm(modelLITELLM_MODEL)封装模型默认值为gemini/gemini-2.5-flash可用环境变量LITELLM_MODEL覆盖。工具注册将 tools.py 中的get_restaurants函数直接挂载为tools[get_restaurants]。运行时使用Runner组合InMemoryArtifactService、InMemorySessionService、InMemoryMemoryService等内存态服务stream()方法按 session_id 复用会话并把base_url写入会话状态供工具读取。系统指令AGENT_INSTRUCTION明确约束了 LLM 的执行逻辑查找餐厅必须先调用get_restaurants工具从用户查询中提取 cuisine、location 和数量count例如top 5 chinese places中的 5拿到数据后严格按照prompt_builder.py中合适的 UI 示例生成最终 A2UI JSON预订桌位当收到USER_WANTS_TO_BOOK...形式的查询时使用预订表单模板生成 UI并把查询中的详情填入dataModelUpdate.contents确认预订当收到User submitted a booking...形式的查询时使用确认模板生成 UI 并填入最终预订信息。2. 提示词与 A2UI Schemaprompt_builder.pyagent/prompt_builder.py 是整个示例的UI 心脏包含三块内容A2UI JSON SchemaA2UI_SCHEMA完整的 JSON Schema 定义了 A2UI 消息必须且只能包含以下四种 action 之一Action作用必填字段beginRendering通知客户端开始渲染一个 surface根组件 样式root、surfaceIdstyles可选含font与primaryColor后者要求#RRGGBB十六进制格式surfaceUpdate用一组新组件更新 surfacesurfaceId、components数组每项含id与componentcomponent有且仅有一个组件类型键dataModelUpdate更新 surface 的数据模型path省略或为/时整体替换surfaceId、contents每项含key与唯一的类型化value*字段deleteSurface删除指定的 surfacesurfaceIdSchema 中还完整定义了受支持的组件类型Text、Image、Icon、Video、AudioPlayer、Row、Column、List、Card、Tabs、Divider、Modal、Button、CheckBox、TextField、DateTimeInput、MultipleChoice、Slider。其中布局类组件Row/Column/List通过children.explicitList或children.templatecomponentIddataBinding动态生成子项组织组件树文本、图片、图标等属性值既可传literalString字面量也可传path引用数据模型中的值例如/items/0/name这是 A2UI 数据与视图解耦的关键设计。UI 模板示例RESTAURANT_UI_EXAMPLES提供四种可直接复用的 A2UI 消息序列SINGLE_COLUMN_LIST_EXAMPLE单列列表模板餐厅数量 ≤ 5 时使用用Listtemplate数据绑定渲染卡片每张卡片含图片、名称、评分、详情、链接与Book Now按钮TWO_COLUMN_LIST_EXAMPLE双列卡片模板餐厅数量 5 时使用用Row并排两张卡片BOOKING_FORM_EXAMPLE预订表单模板包含人数输入TextField、日期时间DateTimeInput、饮食要求TextField与Submit Reservation按钮CONFIRMATION_EXAMPLE预订确认卡片模板展示餐厅图片、预订详情、饮食要求与期待您的光临文案。模板选择规则被写进提示词≤5 家用单列、5 家用双列、USER_WANTS_TO_BOOK用预订表单、User submitted a booking用确认卡片。注意模板是.format(base_urlbase_url)字符串模板base_url在运行时由 get_ui_prompt() 注入用于把示例中的占位符替换为真实服务地址。提示词组装get_ui_prompt/get_text_promptget_ui_prompt把输出规则、UI 模板规则、格式化后的示例与完整 A2UI Schema 拼成一个大的系统提示词并要求 LLM 的最终输出必须用---a2ui_JSON---分隔符拆成两部分前半部分为对话文本后半部分为符合 Schema 的 A2UI JSON 数组get_text_prompt则是纯文本模式的对应版本。这也解释了为何整个流程对输出格式如此依赖——JSON 是被解析、校验、再分片发送的。3. UI 校验与重试机制agent.py stream为了确保 LLM 产出的 UI JSON 可被前端安全解析agent.py 的stream()方法实现了完整的校验-重试闭环将单条消息 Schema 包装为{type: array, items: single_message_schema}因为提示词要求返回消息列表若开启 UI 模式且 Schema 加载失败直接返回错误消息每次尝试时先检查响应中是否包含---a2ui_JSON---分隔符然后剥离可能的 json 代码块围栏依次执行json.loads解析和jsonschema.validate校验捕获ValueError、JSONDecodeError、ValidationError校验失败则最多重试一次max_retries 1总计 2 次尝试重试提示词会带上失败原因并强调必须生成严格符合 Schema 的合法响应若全部重试耗尽向客户端返回文本形式的兜底错误信息。这一机制是生产级生成式 UI 的关键实践任何由 LLM 生成的声明式 UI 都必须经过 Schema 校验再交给前端渲染否则格式错误的 JSON 可能破坏前端解析。4. 执行器与 UI 事件回传agent_executor.pyagent/agent_executor.py 中的RestaurantAgentExecutor继承 A2A SDK 的AgentExecutor承担请求分发职责按扩展协商选 Agent通过try_activate_a2ui_extension(context)检查客户端请求中是否声明了 A2UI 扩展 URI是则使用ui_agent否则使用text_agent解析 UI 事件遍历消息中的DataPart识别userAction载荷将按钮点击转换成内部查询。例如book_restaurant→USER_WANTS_TO_BOOK: {restaurantName}, Address: {address}, ImageURL: {imageUrl}submit_booking→User submitted a booking for {restaurantName} for {partySize} people at {reservationTime} with dietary requirements: {dietary}...组装最终 Part把 Agent 的最终输出按---a2ui_JSON---拆分文本部分包装为TextPartJSON 列表中的每条 A2UI 消息通过create_a2ui_part()包装成带application/jsona2uiMIME 元数据的DataPart后发送任务状态机预订提交submit_booking完成时置为completed其余场景置为input_required等待前端继续交互。5. 工具与数据tools.py / restaurant_data.jsonagent/tools.py 定义了唯一的工具函数get_restaurants(cuisine, location, tool_context, count5)当查询涉及 new york 或 ny 时读取同目录的 restaurant_data.json66 行数据包含纽约多家餐厅的 name、detail、imageUrl、rating、infoLink、address会从tool_context.state读取base_url把数据中的http://localhost:10002替换为会话实际地址确保前端能正确加载/static图片按count切片返回指定数量的餐厅最终以 JSON 字符串形式交回 LLM。6. A2UI 扩展定义a2ui_extension.pya2ui_extension/src/a2ui/a2ui_extension.py 是 A2UI 与 A2A 的桥接层定义了扩展 URIhttps://a2ui.org/a2a-extension/a2ui/v0.8与 MIME 类型application/jsona2uicreate_a2ui_part(a2ui_data)把 A2UI 消息包装成带 MIME 元数据的DataPartget_a2ui_agent_extension()生成声明在 AgentCardcapabilities.extensions中的扩展描述可选acceptsInlineCustomCatalog参数try_activate_a2ui_extension(context)客户端请求 A2UI 扩展时将其标记为已激活并返回True供执行器决策。安全警示把外部 Agent 视为不可信实体这是原文档中特别强调、必须完整保留的核心实践指导。示例代码仅用于演示 A2A 协议的机制在构建生产应用时必须把任何不受你直接控制的 Agent 视为潜在的不可信实体所有来自外部 Agent 的数据都应视为不可信输入包括但不限于其 AgentCard、messages、artifacts 与 task statuses警惕提示注入Prompt Injection恶意 Agent 可以在 AgentCard 的字段如description、name、skills.description中携带精心构造的数据。如果未经净化就把这些数据拼进发给 LLM 的提示词就可能让你的应用暴露在提示注入攻击之下必须做输入校验与净化在使用这些数据前必须进行验证和清洗否则会引入安全漏洞开发者责任需要自行落实适当的安全措施例如输入校验、凭证的安全处理以保护系统和用户。在本文的示例中Schema 校验本身就是一种输入净化手段——它保证了进入前端的 A2UI 数据结构严格符合契约但这并不替代对 AgentCard、技能描述等文本字段的信任边界设计。常见问题与排查结合 examples/integrations/a2a-a2ui/README.md 的说明运行中常见问题如下提示 Im having trouble connecting to my tools请确认 A2A Agent 正常运行在端口 10002、GEMINI_API_KEY已正确设置、UI 与 Agent 两个服务都已启动Python 依赖导入报错进入 agent 目录执行uv sync同步依赖再uv run .启动无需自建前端时也可以只跑 Agent通过 A2A 协议客户端或其他支持 A2UI 的前端直接访问http://localhost:10002的 AgentCard 与任务接口。小结这个示例完整展示了现代 Agent 前端化的一条可落地路径用 ADK 构建工具型 Agent用 A2A 标准化服务发现与消息交换用 A2UI 让 Agent 直接画出可交互界面。无论是 ≤5 单列、5 双列的列表规则还是---a2ui_JSON---分隔符 JSON Schema 校验 重试的容错管线抑或book_restaurant/submit_booking的 UI 事件回传闭环都可以作为你自建生成式 UI Agent的直接参考。最后请牢记演示代码可以优雅生产系统必须对 Agent 的每一项输入保持警惕。【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价