CopilotKit + A2A + A2UI 实战:用 AG-UI 协议构建可动态渲染 UI 的餐厅预订 Agent

CopilotKit + A2A + A2UI 实战:用 AG-UI 协议构建可动态渲染 UI 的餐厅预订 Agent CopilotKit A2A A2UI 实战用 AG-UI 协议构建可动态渲染 UI 的餐厅预订 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本文以仓库中的 A2A A2UI Starter 模板 为蓝本完整讲解如何基于 CopilotKit 前端栈与 Google ADKA2A 协议搭建一个能边对话、边生成真实 UI的餐厅查找与预订 Agent。读完本文你将掌握 A2A/A2UI 协议的端到端协作链路、双服务器开发环境的启动与调试方式以及如何借助声明式 UI Schema 让 LLM 直接输出可渲染的组件树。模板概览一个餐厅搜索 在线订座的 AI 应用该 Starter 是一个现代 Next.js 全栈示例前端由 CopilotKit 提供聊天界面与 A2UI 渲染能力后端是一个运行于独立端口上的 A2AAgent-to-AgentAgent 服务。整体业务能力包括根据用户输入的美食类型cuisine与地点location查找餐厅并返回结果列表将结果渲染为带图片、评分、地址的卡片列表单列或双列布局在卡片上发起Book Now预订操作Agent 动态生成预订表单人数、日期时间、饮食需求提交表单后生成预订成功确认界面完成对话 → 表单 → 确认的完整闭环。模板的核心思想是大部分 UI 不再由前端手写而是由 Agent 以 A2UI 声明式 JSON 的形式动态下发。前端 app/page.tsx 中仅挂载了CopilotChat组件其余界面均由 Agent 生成。环境准备Prerequisites开始前需要准备以下环境依赖版本/说明Gemini API Key供 ADK/A2A Agent 调用大模型详见下文 API Key 一节Python3.12uvPython 依赖与虚拟环境管理工具Node.js20包管理器npm默认、pnpm、yarn、bun 任选其一Node.js 20 是运行 Next.js 16 前端与构建工具链的最低要求Python 3.12 与uv用于运行 Agent 侧的 ADK 框架与 A2A 服务端。快速开始一键启动双服务第一步安装依赖在模板根目录执行安装命令使用你偏好的包管理器# 使用 npm默认 npm install # 使用 pnpm pnpm install # 使用 yarn yarn install # 使用 bun bun install注意安装过程会自动完成 Python 环境的初始化postinstall钩子会调用install:agent即执行 scripts/setup-agent.sh 在agent/目录下运行uv sync。如果手动安装遇到问题可单独执行npm run install:agent第二步配置 Gemini API Key在agent文件夹内创建.env文件即 examples/integrations/a2a-a2ui/agent/.env写入以下内容GEMINI_API_KEYyour-gemini-api-key-hereAgent 服务启动时通过 agent/main.py 中的load_dotenv()加载该文件。源码中的校验逻辑是只要环境变量GOOGLE_GENAI_USE_VERTEXAI不等于TRUE就必须存在GEMINI_API_KEY否则服务直接报错退出——也就是说如果你改为使用 Vertex AI可以不用设置该 Key。此外Agent 默认使用LITELLM_MODEL环境变量指定的模型默认值为gemini/gemini-2.5-flash需要切换模型时可以通过该变量覆盖。第三步启动开发服务器# 使用 npm默认 npm run dev # 使用 pnpm pnpm dev # 使用 yarn yarn dev # 使用 bun bun run dev该命令通过concurrently同时启动两个服务见 package.json 中的dev脚本UI 服务next dev --turbopack即 Next.js 前端Agent 服务执行 scripts/run-agent.sh在agent/目录下运行uv run .通过 uvicorn 启动 A2A 服务器默认监听http://localhost:10002。启动完成后打开前端页面即可向聊天窗口提问例如Find me the top 5 chinese restaurants。可用脚本一览所有脚本均可搭配你选择的包管理器运行脚本说明dev同时启动 UI 与 Agent 两个开发服务器默认入口dev:debug以LOG_LEVELdebug启动开发服务器便于排查协议层问题dev:ui仅启动 Next.js UI 服务器dev:agent仅启动 A2A Agent 服务器build构建 Next.js 应用用于生产环境start启动生产服务器install:agent为 Agent 安装 Python 依赖uv sync架构拆解A2A 与 A2UI 如何协作要真正用好这个模板需要理解两条关键链路A2A 负责对话与任务A2UI 负责界面生成与更新。A2A 服务端ADK Agent 的暴露方式agent/main.py 将基于 Google ADK 构建的RestaurantAgent包装为标准的 A2A Starlette 应用通过AgentCapabilities(streamingTrue, extensions[get_a2ui_agent_extension()])声明支持流式输出并挂载 A2UI 扩展通过AgentSkill(idfind_restaurants, ...)声明查找餐厅技能供客户端在发现阶段识别挂载CORSMiddleware允许来自http://localhost:5173的跨域请求将agent/images/目录作为/static静态资源对外服务供卡片中的菜品图片引用。其中 A2UI 扩展定义在 a2ui_extension/src/a2ui/a2ui_extension.py它约定 MIME 类型application/jsona2ui通过create_a2ui_part()把 A2UI JSON 包装成 A2A 的DataPart随消息流下发。Agent 实现工具调用 动态 UI 输出agent/agent.py 中的RestaurantAgent是核心逻辑所在其指令AGENT_INSTRUCTION约定了三段式业务逻辑查找餐厅必须调用get_restaurants工具从用户输入中提取 cuisine、location 与数量count预订收到形如USER_WANTS_TO_BOOK...的查询时生成预订表单 UI确认收到User submitted a booking...的查询时生成确认 UI。其中 agent/tools.py 实现了get_restaurants(cuisine, location, count)工具它会读取 agent/restaurant_data.json 中的餐厅数据并利用ToolContext中的base_url把图片链接中的http://localhost:10002替换为 Agent 服务的实际地址保证前端能正确加载静态图片。前端CopilotRuntime 桥接 A2A 与 CopilotKitapp/api/copilotkit/[[...slug]]/route.tsx 是前后端之间的桥梁它实例化A2AClient(http://localhost:10002)并通过RuntimeA2AAgent继承自ag-ui/a2a的A2AAgent将 A2A Agent 包装为 CopilotKit Runtime 的默认 Agent随后用createCopilotEndpoint暴露为/api/copilotkit端点。前端 app/page.tsx 中的CopilotKitProvider通过runtimeUrl/api/copilotkit接入该端点并通过a2ui{{ theme }}与renderActivityMessages{activityRenderers}启用 A2UI 渲染。理解 A2UI 声明式 SchemaAgent 如何画出界面模板的关键在于agent/prompt_builder.py即 examples/integrations/a2a-a2ui/agent/prompt_builder.py。它向 LLM 提供了完整的 A2UI JSON SchemaA2UI_SCHEMA、针对餐厅场景的 UI 模板示例RESTAURANT_UI_EXAMPLES以及组装系统提示词的get_ui_prompt()。A2UI 消息的四类动作Schema 规定一条 A2UI 消息必须且只能包含以下四个动作之一动作作用beginRendering通知客户端开始渲染一个 UI Surface指定surfaceId、根组件root与样式font、primaryColorsurfaceUpdate用一组组件更新/新建某个 Surfacecomponents数组dataModelUpdate更新 Surface 对应的数据模型contents键值数组path为可选定位缺省或为/时整体替换deleteSurface按surfaceId删除某个 Surface组件树从 Text 到 Button 的完整组件体系surfaceUpdate.components中的每个组件都包含id组件唯一标识、可选的weight对应 CSSflex-grow仅当作为 Row/Column 的直接子级时允许设置与component组件类型及其属性。Schema 内置的组件类型包括基础展示Text支持h1~h5、caption、body等usageHint、Imagefit对应object-fitusageHint支持icon/avatar/smallFeature/header等、Icon、Video、AudioPlayer布局容器Row/Columnchildren可用explicitList固定子级或用template结合dataBinding动态生成、Listvertical/horizontal、Card、Tabs、Divider、Modal交互组件Button通过action.name与action.context派发客户端动作、CheckBox、TextFieldtextFieldType支持shortText/longText/number/date/obscured可配置validationRegexp、DateTimeInputenableDate/enableTime、MultipleChoice、Slider。所有字段的值既可以是字面量literalString/literalNumber/literalBoolean/literalArray也可以是数据模型路径引用path例如/items/0/name这使得同一套组件树可以复用动态数据。四种内置 UI 模板RESTAURANT_UI_EXAMPLES为 LLM 预置了四种可直接套用的 UI 模板并在提示词中约定了选择规则SINGLE_COLUMN_LIST_EXAMPLE单列列表餐厅数量 ≤ 5 时使用通过Listtemplate把/items数据绑定渲染为垂直卡片流TWO_COLUMN_LIST_EXAMPLE双列列表餐厅数量 5 时使用用Row排布多张卡片BOOKING_FORM_EXAMPLE预订表单收到预订意图时使用包含TextField人数、饮食需求与DateTimeInput日期时间等CONFIRMATION_EXAMPLE确认页预订提交后生成确认卡片展示订座详情。提示词中要求 LLM 的输出必须用分隔符---a2ui_JSON---拆成两部分前半部分是对话文本后半部分是符合 Schema 的 A2UI 消息 JSON 数组。如果你要复用到其他业务如机票预订只需替换示例模板get_ui_prompt()函数本身可以原样复用。服务端如何校验与纠错agent/agent.py 的stream()方法实现了生成 → 校验 → 重试的稳健机制将A2UI_SCHEMA解析后包装为数组校验器{type: array, items: single_message_schema}解析 LLM 输出剥离---a2ui_JSON---分隔符与可能的 json 代码围栏用json.loads检查 JSON 可解析性再用jsonschema.validate对照 Schema 校验校验失败时自动重试max_retries 1即最多 2 次尝试并携带失败原因重新提示 LLM 严格遵循 Schema重试耗尽仍未通过时降级返回文本错误消息避免前端收到损坏的 UI 数据。理解 A2UI 客户端事件从点击按钮到Agent 响应A2UI 的双向性体现在agent_executor.pyexamples/integrations/a2a-a2ui/agent/agent_executor.py中。它根据客户端是否声明了 A2UI 扩展try_activate_a2ui_extension选择使用UI 版 Agent还是纯文本 Agent同时解析消息中的DataPart若DataPart中包含userAction即客户端 UI 事件则按actionName分发book_restaurant→ 组装USER_WANTS_TO_BOOK: ...查询携带restaurantName、address、imageUrl上下文submit_booking→ 组装User submitted a booking for ...查询携带人数、时间、饮食需求否则回退到context.get_user_input()的纯文本输入。最终响应会根据任务类型设置不同的任务终态提交预订后为TaskState.completed其余场景为TaskState.input_required等待用户继续操作并调用create_a2ui_part()把每个 A2UI 消息封装为独立的DataPart下发给前端。常见问题排查TroubleshootingAgent 连接问题如果前端出现 Im having trouble connecting to my tools依次检查ADK Agent 是否运行在 10002 端口——该端口在 route.tsx 的new A2AClient(http://localhost:10002)与 agent/main.py 的默认端口参数中均有硬编码约定Gemini API Key 是否配置正确——确认 agent/.env 中的GEMINI_API_KEY已填写两个服务器是否都启动成功——用npm run dev同时观察ui与agent两个进程的输出。Python 依赖问题如果遇到 Python import 错误可进入agent目录手动同步依赖并启动cd agent uv sync uv run .模板文件的阅读路线图文件仓库相对路径作用examples/integrations/a2a-a2ui/README.md模板总览、快速开始与排查指南examples/integrations/a2a-a2ui/package.jsonnpm 脚本与前端依赖清单examples/integrations/a2a-a2ui/app/page.tsx前端聊天界面入口CopilotChat 挂载点examples/integrations/a2a-a2ui/app/api/copilotkit/[[...slug]]/route.tsxCopilotRuntime 与 A2A Agent 的桥接路由examples/integrations/a2a-a2ui/agent/agent.py餐厅 Agent 主体含 A2UI 校验与重试逻辑examples/integrations/a2a-a2ui/agent/prompt_builder.pyA2UI Schema、UI 模板示例与提示词组装examples/integrations/a2a-a2ui/agent/agent_executor.pyA2A 事件解析与 UI 客户端动作分发examples/integrations/a2a-a2ui/agent/tools.pyget_restaurants工具实现examples/integrations/a2a-a2ui/agent/main.pyA2A 服务器装配AgentCard、CORS、静态资源examples/integrations/a2a-a2ui/a2ui_extension/src/a2ui/a2ui_extension.pyA2UI 扩展定义与create_a2ui_part封装如何扩展到其他业务场景这个模板的设计目标是易于扩展Starter 定位。将其迁移到其他业务例如机票预订、酒店查询时核心步骤如下在 agent/tools.py 中新增领域工具并注册到 agent/agent.py 的LlmAgent(tools[...])在 agent/prompt_builder.py 中按新场景编写 UI 模板示例并更新get_ui_prompt()中的模板选择规则如需新的按钮动作如book_flight在 agent_executor.py 的userAction分发逻辑中增加对应分支保持A2UI_SCHEMA不变——它描述的是通用 A2UI 协议能力与具体业务解耦。模板整体遵循 MIT 许可证你可以自由提交 issue 与功能建议。本仓库中还有更多相关实现可以参考例如 examples/integrations/a2a-middleware、examples/integrations/agent-spec 等集成示例以及packages/a2ui-renderer中关于 A2UI 渲染器在前端的具体实现。【免费下载链接】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),仅供参考