AG2 工具渲染(Tool Rendering)实战指南:从 useRenderTool 到 E2E 质量验证
2026/9/10 20:03:40 网站建设 项目流程

AG2 工具渲染(Tool Rendering)实战指南:从 useRenderTool 到 E2E 质量验证

【免费下载链接】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

导读

本文以仓库中 tool-rendering.md 质量验证清单为骨架,完整拆解 CopilotKit 仓库中 AG2 集成示例的"工具渲染"(Tool Rendering)能力:前端如何通过useRenderTool把后端 Agent 的工具调用实时渲染为品牌化 React 卡片,后端如何通过 AG-UI 协议被代理接入,以及如何用 Playwright E2E 测试把整条链路固化下来。读完本文,你将掌握 per-tool 渲染器 + 通配 catch-all 的完整注册方式、前后端数据契约的关键细节,以及一套可复制的 QA 验收方法。

一、功能定位:AG2 集成示例中的 Tool Rendering

showcase/integrations/ag2是 CopilotKit 与 AG2 中,"Tool Rendering" 被定义为:

Backend agent tools rendered as UI components(后端 Agent 工具渲染为 UI 组件)

即:后端 Agent 在执行工具时,前端聊天记录(chat transcript)中不再是干巴巴的 JSON,而是渲染为带有品牌设计的 React 组件。manifest 中高亮的关键文件覆盖了完整链路:

  • agent.py — AG2 后端 Agent 与工具定义;
  • tools/get_weather.py、tools/query_data.py、tools/search_flights.py、tools/schedule_meeting.py — 具体工具实现;
  • page.tsx — 前端渲染器注册与页面;
  • route.ts — CopilotKit 运行时与 AG-UI 协议代理。

示例应用将同一主题做成了"三阶段递进"(见 manifest 中三个独立 demo):tool-rendering(每个核心工具都有专属渲染器 + 通配兜底)、tool-rendering-default-catchall(前端零自定义渲染器,完全依赖 CopilotKit 内置默认 UI)、tool-rendering-custom-catchall(用useDefaultRenderTool注册一个统一品牌化兜底卡片)。本 QA 文档针对的是最完整的tool-rendering变体。

二、架构概览:前端、运行时与 AG2 后端如何连接

2.1 AG-UI 协议代理

前端 demo 页面挂载时指定了运行时地址与 Agent 名称:

<CopilotKit runtimeUrl="/api/copilotkit" agent="tool-rendering">

/api/copilotkit由 route.ts 处理:它通过@ag-ui/clientHttpAgent把请求转发到独立运行的 FastAPI 后端(默认http://localhost:8000,可通过AGENT_URL环境变量覆盖),并使用 AG-UI 协议通信。

const AGENT_URL = process.env.AGENT_URL || "http://localhost:8000"; function createAgent(path = "/") { return new HttpAgent({ url: `${AGENT_URL}${path}` }); }

route.ts 把tool-rendering注册在sharedAgentNames列表中(route.ts#L35-L56),与agentic_chattool-rendering-default-catchall等名称共享同一个agent.py中定义的ConversableAgent——也就是说,这一系列前端差异巨大的 demo 背后是同一份后端逻辑,差异全部发生在前端渲染层。

2.2 健康检查前提

QA 文档的前置条件强调两点:

  1. Demo 已部署且可访问;
  2. Agent 后端健康(检查/api/health)。

后端侧的真实健康探针在 entrypoint.sh:一个 watchdog 每 30 秒curl一次http://127.0.0.1:8000/health,连续 3 次失败即判定异常。因此前端验收前先确认该端点返回正常,是保证测试结果可解释的第一步。

三、前端渲染机制:useRenderTool 与 useDefaultRenderTool

3.1 per-tool 渲染器注册

demo 页面 为每个"有趣"的后端工具注册了专属渲染器,映射关系如下:

后端工具专属渲染器说明
get_weather<WeatherCard />天气卡片
search_flights<FlightListCard />航班列表卡片
get_stock_price<StockCard />股票行情卡片
roll_d20<D20Card />掷骰子卡片
其他一切工具<CustomCatchallRenderer />通配兜底

useRenderTool的典型注册方式(以天气工具为例):

useRenderTool( { name: "get_weather", parameters: z.object({ location: z.string(), }), render: ({ parameters, result, status }) => { const loading = status !== "complete"; const parsed = parseJsonResult<WeatherResult>(result); return ( <WeatherCard loading={loading} location={parameters?.location ?? parsed.city ?? ""} temperature={parsed.temperature} humidity={parsed.humidity} windSpeed={parsed.wind_speed} conditions={parsed.conditions} /> ); }, }, [], );

要点拆解:

  • nameparameters:声明该渲染器负责的工具名与参数 Schema(使用 zod 校验);
  • render回调:接收{ parameters, result, status }三个入参,其中result通常是后端返回的 JSON 字符串,需要先用parseJsonResult(位于 parse-json-result.ts)解析为对象;
  • status:驱动加载态。源码中status !== "complete"视为 loading,据此切换卡片内容。

3.2 通配兜底渲染器

任何未被具名渲染器认领的工具调用,都会落到通过useDefaultRenderTool注册的 CustomCatchallRenderer:

useDefaultRenderTool( { render: ({ name, parameters, status, result }) => ( <CustomCatchallRenderer name={name} parameters={parameters} status={status as CatchallToolStatus} result={result} /> ), }, [], );

兜底卡片展示工具名、状态徽章、格式化后的参数与结果 JSON。从源码看,CatchallToolStatus有三种状态(custom-catchall-renderer.tsx#L15):

状态值徽章文案含义
inProgressstreaming流式执行中
executingrunning正在执行
completedone已完成(此时才渲染结果 JSON)

这正好与 QA 文档"验证加载态→验证渲染结果"的验收顺序一一对应。

四、天气卡片:从后端工具到前端组件的完整数据契约

4.1 后端:为什么必须返回 JSON 字符串

QA 文档要求验证天气卡片的多个数据字段(城市名、温度、湿度、风速、体感温度、天气状况)。这些字段的后端来源是 agent.py 中的get_weather工具:

async def get_weather( location: Annotated[str, "City name to get weather for"], ) -> str: """Get current weather for a location.""" result = get_weather_impl(location) return json.dumps( { "city": result["city"], "temperature": result["temperature"], "feels_like": result["feels_like"], "humidity": result["humidity"], "wind_speed": result["wind_speed"], "conditions": result["conditions"], } )

源码注释明确记录了一个重要契约:工具必须返回 JSON 字符串而不是 dict——因为 autogen 对非字符串返回值会调用str()序列化,产生带单引号的 Python repr,前端JSON.parse无法解析,天气卡片就会渲染出--占位符。这是"工具渲染能否正常出数据"的关键细节,也是 QA 验证"所有数据字段已填充"背后的底层原因。query_datamanage_sales_todosschedule_meeting等工具采用了同样的模式。

4.2 前端:WeatherCard 组件与 testid

WeatherCard 是加载态与完成态的复合组件:

  • 加载态:显示城市名、"Fetching weather..." 文案与...占位符;
  • 完成态:渲染温度(华氏)、湿度百分比、风速(mph)、天气状况与对应 emoji。

组件上打了一组稳定的data-testid,供 QA 手工验收与 E2E 自动断言共用:

data-testid含义
weather-card天气卡片容器
weather-city城市名
weather-humidity湿度(如 55%)
weather-wind风速(如 10 mph)

天气图标由conditionsEmoji函数根据状况文本关键字映射(weather-card.tsx#L79-L87):sun/clear → sunrain/storm → raincloud → cloudsnow → snow

4.3 关于加载文案与主题色的说明

QA 文档预期加载态显示 "Retrieving weather..." 并带 spinner,且卡片背景色随天气状况变化(晴#667eea、雨#4A5568、多云#718096、雪#63B3ED)。对照当前源码:加载文案实际为 "Fetching weather..."(天气 emoji 位置为...),背景色为固定值#EDEDF5,未按状况动态换色。也就是说,QA 文档描述的是该变体的预期规格,而当前实现细节以源码为准——在做手工验收时,建议以实际渲染的文案与配色作为基准,同时把"加载态到完成态的切换是否清晰、信息是否齐全"作为核心判定标准,而不是逐字比对文案。

五、多城市天气查询:多卡片并存

QA 文档专门有一节验证"连续询问第二个城市后,第二张卡片应正常渲染且不破坏第一张"。这与前端的实现方式直接相关:每次工具调用都会挂载一张独立的卡片。这个设计在 d20-card.tsx 的注释中写得很明确:

Each tool call mounts its own card so e2e tests can count them.(每次工具调用挂载自己的卡片,以便 E2E 测试计数。)

因此验收要点是:

  • 先问 San Francisco,再问第二个城市;
  • 断言页面上出现两张weather-card,且各自weather-city显示正确的城市名;
  • 第一张卡片的内容不被第二张覆盖。

E2E 侧同样验证了这一行为:d20的测试用cards.count()精确断言"恰好 5 张卡片、最后一张结果是 20"(见 tool-rendering.spec.ts#L113-L143),说明"每次调用独立成卡"是可计数、可断言的关键设计。

六、建议按钮(Suggestion Pills)

QA 文档要求验证三个天气建议按钮可见并可点击填入输入框:Weather in San FranciscoWeather in New YorkWeather in Tokyo

这些建议按钮由useSuggestions()(demo 页面中的 hooks,按钮 DOM 使用data-testid="copilot-suggestion")驱动。需要说明的是:当前仓库的 E2E 测试所固化的建议集合是另一套五颗 pill——Weather in SFFind flightsStock priceRoll a d20Chain tools(tool-rendering.spec.ts#L26-L39),覆盖了天气、航班、股票、掷骰子与多工具链式调用五条路径。手工验收时可灵活处理:只要建议按钮可见、点击后能填充输入框或直接发送消息,即视为通过;若需要严格对齐 QA 文档,可将建议文案调整为目标城市(如 SF / New York / Tokyo)。

七、错误处理:空消息与无控制台报错

QA 文档的第三大块验收聚焦健壮性:

  1. 发送空消息应被优雅处理(不崩溃、不产生无效请求);
  2. 正常使用过程中无 console 错误

这条检查背后的意义在于:工具渲染链路横跨前端渲染器、CopilotRuntime 代理、AG2 后端三层,任何一层的异常都可能在浏览器控制台暴露。建议在 DevTools Console 保持开启的状态下完成 3.1—3.3 的全部步骤,把"无报错"作为贯穿始终的观察项。

八、从手工 QA 到 Playwright E2E:把验收固化为自动化

QA 文档的每一项手工检查,几乎都能在 tool-rendering.spec.ts 中找到对应的自动化断言。该测试文件头部注明其与 QA 文档的对应关系(QA reference: qa/tool-rendering.md),并把 5 颗建议 pill 映射为 6 个测试用例:

测试用例对应 QA 步骤关键断言(testid + 确定性 fixture 值)
页面加载与 5 颗建议 pill建议按钮检查copilot-suggestion× 5 可见
Weather in SF天气卡片渲染weather-card可见;weather-city含 "San Francisco";weather-humidity含 "55%";weather-wind含 "10"
Find flights多卡片渲染flights-card可见;flight-origin含 "SFO";flight-destination含 "JFK";flight-row≥ 2 行
Stock price股票卡片渲染stock-card可见;stock-ticker= "AAPL";stock-price含 "$338.37";stock-change含 "-2.96%"
Roll a d20多卡片渲染恰好 5 张d20-card;最后一张d20-value= "20",前四张非 20
Chain tools多工具链式调用一轮对话中同时出现weather-cardflights-cardd20-card

测试中使用的确定性数据来自 Aimock fixture(showcase/aimock/d5-all.json),每条 pill 提示词都被固定映射到确定的工具调用序列——这正是"渲染结果可重复断言"的前提。测试还设置了两个超时阈值:建议按钮等待 15 秒、工具调用等待 60 秒(tool-rendering.spec.ts#L15-L16),与 QA 文档"聊天 3 秒内加载、Agent 10 秒内响应"的预期共同构成性能验收基线。

九、验收标准总结(Expected Results)

综合 QA 文档的最终判定标准,一个"通过"的工具渲染验收应同时满足:

  • 性能:聊天界面 3 秒内加载完成;Agent 10 秒内给出响应;
  • 功能:天气卡片渲染出全部数据字段(城市、摄氏/华氏温度、湿度、风速、体感温度、状况图标);
  • 视觉一致性:天气图标与状况文本匹配(sun/rain/cloud/snow);
  • 健壮性:无 UI 错误、无布局破坏、空消息被优雅处理。

十、延伸阅读

  • 后端 Agent 与工具定义:agent.py
  • 前端渲染器注册:page.tsx
  • 天气卡片组件:weather-card.tsx
  • 通配兜底渲染器:custom-catchall-renderer.tsx
  • E2E 自动化验收:tool-rendering.spec.ts
  • 运行时代理与 Agent 注册:route.ts
  • Demo 目录与路由清单:manifest.yaml

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询