基于 A2UI 的 Orchestrator 示例:用 Angular Chat-Canvas 编排多个 A2A 智能体的完整实践
基于 A2UI 的 Orchestrator 示例用 Angular Chat-Canvas 编排多个 A2A 智能体的完整实践【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui本篇技术指南围绕 a2ui 仓库中samples/community/client/angular/projects/orchestrator这一示例应用展开讲解如何基于 A2AAgent-to-Agent与 A2UIAgent-to-UI协议在 Angular 应用中集成A2aChatCanvas聊天画布组件通过一个 Orchestrator 主智能体把用户请求路由给多个具备不同专业能力的子智能体并渲染这些智能体返回的 A2UI 界面图表、地图等。读完本文你将掌握该示例从环境准备、构建运行到源码级实现的完整脉络并能够据此搭建自己的多智能体编排前端。示例概览Chat-Canvas 与多智能体编排Orchestrator 是一个端到端的示例应用前端是一个 Angular 应用使用 Chat-Canvas 组件作为聊天界面后端是一个基于 Agent Development KitADK的 Orchestrator Agent它接收用户消息后根据上下文判断把任务委派给对应的子智能体子智能体各自拥有独立的 A2A 服务端点。用户在聊天窗口中的每一次交互——包括 A2UI 组件触发的userAction——都经由 Orchestrator 中转最终由创建了对应 UI 界面的子智能体完成处理。用一句话概括该示例的核心链路浏览器Angular Chat-Canvas→ Express 代理中间件 → A2A Client → Orchestrator AgentADK→ 通过transfer_to_agent路由 → 专业子智能体A2A 服务→ 返回 A2A 消息与 A2UI 界面 → Chat-Canvas 渲染示例自带两个自定义 A2UI 渲染组件支持下钻的饼图Chart和基于 Google Maps 的地图GoogleMap用于演示子智能体如何以 A2UI 数据驱动的形式向用户输出可视化结果。前置条件根据 示例 README 与后端 Orchestrator Agent 示例说明运行整套示例需要Node.js用于构建与运行 Angular 前端应用Google Maps API Key地图组件依赖 Google Maps JavaScript API需要到 Google 开发者平台申请密钥申请方式可参考 Google Maps JavaScript API 官方文档的 get-api-key 章节一个承载所有 A2A 服务的端点即运行中的 Orchestrator Agent 及其子智能体 A2A 服务其启动方式详见后端说明Python 3.9 或更高版本、UV包管理器用于运行基于 ADK 的 Orchestrator 智能体后端可访问的 LLM 与 API KeyOrchestrator 需要对每次请求做一次推理调用来决定路由目标。需要说明的是前端 README 中的第 3 条endpoint hosting all of the A2AService指向的说明文档位于仓库samples/community/agent/adk/orchestrator/README.md原文为相对链接../../../../agent/adk/orchestrator/README.md后文会详细展开该后端的运行方式。运行步骤详解1. 配置 Google Maps API Key编辑 src/environments/environment.ts该文件默认内容为export const environment { googleMapsApiKey: YOUR_API_KEY_HERE, };把YOUR_API_KEY_HERE替换为你在前置条件中申请的密钥。该值会被 src/app/app.ts 在应用初始化时动态拼接成 Google Maps JS SDK 的加载地址ngOnInit() { const script this.renderer2.createElement(script); script.src https://maps.googleapis.com/maps/api/js?key${environment.googleMapsApiKey}callbackinitMaplibrariesmarker; script.async true; script.defer true; this.renderer2.appendChild(this.document.body, script); }注意这里通过callbackinitMap与librariesmarkerAdvanced Marker 库加载脚本与地图组件中使用google.maps.marker.PinElement的能力对应见下文地图组件实现。2. 构建共享依赖在renderers/web_core目录下执行yarn buildweb_core是 A2UI 在 Web 平台的核心运行时库位于 renderers/web_coreAngular、React、Lit 等渲染器都依赖它。示例项目的 package.json 中声明了a2ui/angular、a2ui/web_core、a2ui/markdown-it三个依赖因此需要先构建共享依赖包。3. 安装本地依赖yarn install4. 运行所有智能体的 A2A 服务按照 samples/community/agent/adk/orchestrator/README.md 中的说明启动 Orchestrator Agent 及其子智能体具体步骤见下文后端ADK Orchestrator Agent小节。5. 启动应用yarn start orchestrator6. 打开浏览器访问 http://localhost:4200/ 即可看到 Chat-Canvas 聊天界面。前端应用源码解剖聊天画布集成应用根组件 src/app/app.ts 导入了A2aChatCanvas与ChatService并在模板 src/app/app.html 中直接使用main classmain a2a-chat-canvas [messageDecorator]demoMessageDecorator [emptyHistoryTemplate]welcomeMessage /a2a-chat-canvas /main两个关键绑定messageDecorator指向 src/message-decorator/demo-message-decorator.ts 导出的demoMessageDecorator。这是一个自定义消息装饰器工厂返回DemoMessageDecoratorComponent用于在消息渲染上叠加自定义样式/能力emptyHistoryTemplate无历史消息时的欢迎页展示智能体名称与两个建议快捷指令suggestion chipsList Chinese restaurants in New York餐厅列表与Show me charts图表展示点击后调用sendMessage把文本发送给ChatService。应用级配置把 A2A 服务、渲染目录与 Markdown 渲染器接进来src/app/app.config.ts 是应用功能装配的核心它通过configureChatCanvasFeatures组合三块能力export const appConfig: ApplicationConfig { providers: [ provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection(), provideRouter(routes), provideClientHydration(withEventReplay()), provideCharts(withDefaultRegisterables()), provideMarkdownRenderer(renderMarkdown), configureChatCanvasFeatures( usingA2aService(A2aServiceImpl), usingA2uiRenderers(DEMO_CATALOG), usingDefaultSanitizerMarkdownRenderer(), ), ], };usingA2aService(A2aServiceImpl)注册 A2A 消息通道的实现类usingA2uiRenderers(DEMO_CATALOG)注册本应用支持的自定义 A2UI 渲染目录Chart 与 GoogleMapprovideMarkdownRenderer(renderMarkdown)usingDefaultSanitizerMarkdownRenderer()配置基于a2ui/markdown-it的 Markdown 渲染管线provideCharts(withDefaultRegisterables())为 Chart.js 图表组件注册默认插件。A2A 服务的浏览器端实现src/services/a2a-service-impl.ts 实现了A2aService接口负责浏览器与本地 Express 代理之间的通信sendMessage(parts, signal)向/a2a发起POST请求把parts与contextId以 JSON 形式发送成功后从响应中提取并缓存contextId用于保持多轮会话上下文失败时抛出error字段对应的异常getAgentCard()向/a2a/agent-card发起GET请求获取后端智能体的 AgentCard在非浏览器环境如 SSR下直接返回空卡片避免服务端渲染时产生请求。async sendMessage(parts: Part[], signal?: AbortSignal): PromiseSendMessageSuccessResponse { const response await fetch(/a2a, { body: JSON.stringify({ parts: parts, contextId: this.contextId }), method: POST, signal, }); // ... }该实现还配套了单元测试 src/services/a2a-service-impl.spec.ts用于验证消息发送与 AgentCard 获取逻辑。Express 代理把浏览器请求转成 A2A 协议调用由于浏览器不能直接与智能体的 A2A 服务对话涉及跨域与协议差异示例在 src/server.ts 中内置了一个 Express 服务扮演协议翻译层。它的职责包括1. 接收聊天请求并构造 A2A 消息app.post(/a2a, (req, res) { // 收集原始请求体 → 解析 parts 与 contextId const sendParams: MessageSendParams { message: { messageId: uuidv4(), contextId, role: user, parts, kind: message, metadata: { a2uiClientCapabilities: { supportedCatalogIds: [ https://a2ui.org/specification/v0_8/standard_catalog_definition.json, ], }, }, }, }; });注意消息的metadata.a2uiClientCapabilities.supportedCatalogIds声明了客户端支持的目录为 A2UI v0.8 标准目录这告诉智能体可以安全地返回标准目录中的组件界面。2. 建立带 A2UI 扩展头的 A2A Clientasync function createOrGetClient() { client ?? await A2AClient.fromCardUrl( http://localhost:10002/.well-known/agent-card.json, { fetchImpl: fetchWithCustomHeader }, ); return client; } async function fetchWithCustomHeader(url, init) { const headers new Headers(init?.headers); headers.set(X-A2A-Extensions, https://a2ui.org/a2a-extension/a2ui/v0.8); return fetch(url, newInit); }A2AClient从 Orchestrator Agent 的 AgentCard 地址本示例硬编码为http://localhost:10002初始化并在每次请求中附加X-A2A-Extensions: https://a2ui.org/a2a-extension/a2ui/v0.8头向对端声明启用了 A2UI 扩展能力。3. 透传 AgentCardapp.get(/a2a/agent-card, async (req, res) { const response await fetchWithCustomHeader( http://localhost:10002/.well-known/agent-card.json, ); // ... res.json(card); });浏览器端getAgentCard()拉取到的 AgentCard 实际上是 Express 服务从 Orchestrator 处代理获取的。最终 Express 以isMainModule或pm_id判断进程角色默认监听PORT环境变量指定的端口默认 4000同时通过createNodeRequestHandler暴露给 Angular SSR 请求处理器。自定义 A2UI 渲染目录图表与地图示例在 src/a2ui-catalog/catalog.ts 中定义了DEMO_CATALOG把两个自定义组件注册为 A2UI 可渲染的目录项export const DEMO_CATALOG { Chart: { type: () import(./chart).then(r r.Chart), bindings: ({properties}) [ inputBinding(type, () (type in properties properties[type]) || undefined), inputBinding(title, () (title in properties properties[title]) || undefined), inputBinding(chartData, () (chartData in properties properties[chartData]) || undefined), ], }, GoogleMap: { type: () import(./google-map).then(r r.GoogleMap), bindings: ({properties}) [ inputBinding(zoom, () (zoom in properties properties[zoom]) || 8), inputBinding(center, () (center in properties properties[center]) || undefined), inputBinding(pins, () (pins in properties properties[pins]) || undefined), inputBinding(title, () (title in properties properties[title]) || undefined), ], }, } as Catalog;bindings负责把 A2UI 数据模型properties中的字段映射为 Angular 组件的input绑定实现数据驱动渲染智能体只需返回符合数据模型的 JSON前端即可动态渲染出对应的组件实例。Chart支持下钻的饼图src/a2ui-catalog/chart.ts 基于 Chart.js 与ng2-charts实现。它继承DynamicComponentTypes.CustomNode通过resolvePrimitive把数据路径解析成实际值并支持两级数据根视图读取chartData[0].label / .value直至数据耗尽最多 500 项下钻视图读取chartData[i].drillDown[j].label / .value生成子类目数据。交互上支持三种进入下钻的方式点击图例legend.onClick、点击扇区chartClick下钻后可通过左上角返回按钮restoreOriginalView()回到根视图标签以百分比形式展示datalabels插件。GoogleMap带自定义标记的地图src/a2ui-catalog/google-map.ts 基于angular/google-maps的google-map与map-advanced-marker实现核心能力从pins数据路径逐条解析地点最多 100 个 pin每个 pin 读取lat / lng / name / description / background / borderColor / glyphColor等字段其中lat、lng、name缺失时该地点被判定为无效并跳过源码注释中提示该校验逻辑未来应上移到guard.ts的 typed data model 中使用google.maps.marker.PinElement构造高级标记Advanced Marker支持背景色、边框色、字形颜色定制center缺省时回退到默认中心点洛杉矶lat: 34.0626, lng: -118.3759zoom缺省为 8组件在zoom解析成功时才渲染整个地图容器避免无意义加载。后端ADK Orchestrator Agent前端 README 链接的后端示例位于 samples/community/agent/adk/orchestrator/README.md它实现了一个调度中枢式的智能体基于ADK A2A 协议使用RemoteA2aAgent把子智能体声明为远程 A2A AgentRemoteA2aAgent会把 ADK 事件翻译为 A2A 消息发往子智能体各自的 A2A 服务并在请求头中附加X-A2A-Extensionshttps://a2ui.org/a2a-extension/a2ui/v0.8以启用 A2UI 扩展示例中为了简化检查将该扩展硬编码为启用状态每次请求做一次推理路由Orchestrator 对每条用户消息执行一次 LLM 推理判断应路由到哪个子智能体随后用 ADK 的transfer_to_agent把原始消息转交给目标子智能体。这一路由在后续调用包括 A2UIuserAction中都会执行README 也指出未来版本可通过before_model_callback将userAction编程式地路由回创建该界面的智能体从而省掉一次 Orchestrator 的 LLM 推理。运行后端示例后端示例的启动步骤为创建环境变量文件并填入 API Key不要提交.envcp .env.example .env # 编辑 .env填入真实的 API Key运行编排智能体与各子智能体chmod x ./run_demo.sh ./run_demo.sh尝试示例中的典型指令验证路由效果用户指令路由目标子智能体Check me in please前台front deskMy room needs cleaning客房服务housekeepingThe AC is broken维修maintenanceI want to order a burger to my room客房送餐room service从源码结构看这套酒店场景 专业子智能体的演示设计正好与前端示例中查询餐厅地图、展示图表饼图的快捷指令相呼应形成一整套可体验的多智能体 可视化交互闭环。安全注意事项重要后端示例 README 特别强调任何在你的直接控制之外的智能体都应被视为潜在不可信实体。具体风险包括提示注入恶意智能体可能在 AgentCard 的name、skills.description等字段中注入构造数据若未净化就直接拼入 LLM 提示词可能暴露应用于提示注入攻击伪造界面 / XSS / DoS从外部智能体接收的任何 UI 定义与数据流都应视为不可信输入。恶意智能体可能伪造合法界面诱骗用户钓鱼、通过属性值注入恶意脚本XSS或生成过度复杂的布局拖垮客户端DoS内嵌内容隔离如果应用支持 iframe / webview 之类的可选内嵌内容必须额外防范恶意外部站点。开发者有责任实现输入净化、内容安全策略CSP、对内嵌内容做严格隔离、安全处理凭据等防护措施未经验证的数据与未经沙箱化的渲染内容可能引入严重漏洞。这一安全准则同样适用于本文介绍的 Orchestrator 前端Express 代理层应作为可信边界对下游智能体返回的内容保持警惕。小结Orchestrator 示例为多智能体 A2UI 可视化提供了一条完整、可运行的参考链路。从前端看它示范了A2aChatCanvas的集成方式、configureChatCanvasFeatures的能力装配、A2A 服务的浏览器实现与 Express 协议代理以及如何通过DEMO_CATALOG注册数据驱动的自定义渲染组件从后端看它示范了基于 ADK 的编排智能体如何借助 A2A 协议与transfer_to_agent实现上下文感知的路由分发。无论你是想搭建企业级多智能体协作前端还是想为自有智能体扩展 A2UI 渲染能力这套示例都值得作为起点深入研读。【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考