CopilotKit 工具渲染实战:用 useRenderTool 将后端 Agent 工具调用渲染为 React 组件
CopilotKit 工具渲染实战用 useRenderTool 将后端 Agent 工具调用渲染为 React 组件【免费下载链接】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在后端 Agent 工具调用的全流程中如何让聊天记录里的每一次工具调用都可视化CopilotKit 的 Tool Rendering 机制给出了答案前端通过useRenderTool为每个工具名注册独立的 React 渲染器接收args、result与status从而同时呈现进行中与已完成两种状态的调用结果。本文以showcase/integrations/ag2集成示例中的 tool-rendering 演示为核心从 API 源码、完整 Demo 实现、AG2 后端工具对应关系、e2e 测试四个层面讲透如何把天气、航班、股票、骰子等 Agent 工具调用渲染成品牌化的聊天卡片。一、核心概念后端工具调用 → 前端 React 组件原文档showcase/integrations/ag2/src/app/demos/tool-rendering/README.md点明了 Tool Rendering 的本质后端 Agent 工具调用Backend agent tool calls被渲染为聊天记录chat transcript中的 React 组件。前端使用useRenderTool按工具名注册渲染器渲染器接收args、result与statusUI 可以据此同时反映进行中in-flight与已完成completed的调用状态。这份 README 是挂在 demo 源码旁的开发者笔记正式的规范化描述canonical description记录在 showcase manifest 中。在 showcase/integrations/ag2/manifest.yaml 中该演示的条目为- id: tool-rendering name: Tool Rendering description: Backend agent tools rendered as UI components tags: - agent-capabilities route: /demos/tool-rendering highlight: - src/agents/agent.py - tools/get_weather.py - tools/query_data.py - tools/schedule_meeting.py - tools/search_flights.py - src/app/demos/tool-rendering/page.tsx - src/app/api/copilotkit/route.ts可以看出这是一个典型的Agent 能力展示agent-capabilities后端 AG2 Agent 通过工具get_weather、search_flights、get_stock_price、roll_d20等执行真实逻辑前端将这些执行过程与结果翻译成用户可读、可交互的 UI。二、useRenderTool 源码级剖析注册、去重与状态机useRenderTool的实现位于 packages/react-core/src/v2/hooks/use-render-tool.tsx。其核心 API 分两种形态通配符wildcard形态——注册name: *作为任何未精确匹配工具名的回退渲染器useRenderTool( { name: *, render: ({ name, status }) ( div{status complete ? ✓ : ⏳} {name}/div ), }, [], );具名named形态——按工具名精确注册parameters提供 Standard Schema V1 兼容的类型化 schemaZod、Valibot、ArkType 均可useRenderTool( { name: searchDocs, parameters: z.object({ query: z.string() }), render: ({ status, parameters, result }) { if (status inProgress) return divPreparing.../div; if (status executing) return divSearching {parameters.query}/div; return div{result}/div; }, }, [], );从源码可以看到几个关键行为use-render-tool.tsx按agentId:name去重注册条目写入 CopilotKit 的renderToolCalls注册表最新注册覆盖同名条目无清理卸载渲染器在组件卸载后仍保留在注册表中这样历史聊天中的旧工具调用依然能够被渲染——这是聊天场景的关键设计状态分支关联具名注册在底层会按ToolCallStatusInProgress/Executing/Complete分别转发render并将底层args重新暴露为类型化的parameters保证判别联合discriminated union在状态间保持关联deps 刷新第二个参数deps变化时重新注册可用于随选中 Agent 切换渲染器如[selectedAgentId]。render回调收到的 props 是一个按状态区分的判别联合见 use-render-tool.tsx状态status取值resultparameters可用性inProgressinProgressundefined部分参数Partialexecutingexecutingundefined完整参数completecompletestring完整参数这正好对应原文档所说的接收args、result和statusUI 可以反映 in-flight 和 completed 两种调用状态。三、完整 Demo 实战四个品牌化卡片 一个通配符回退tool-rendering 演示是展示仓库内三步进阶中最完整的一种形态每个有意思的后端工具都拥有专属品牌化 UI同时一个 catch-all 渲染器负责兜底所有漏网的工具调用。映射关系见 page.tsxget_weather → WeatherCard / (per-tool renderer) search_flights → FlightListCard / (per-tool renderer) get_stock_price → StockCard / (per-tool renderer) roll_d20 → D20Card / (per-tool renderer) * → CustomCatchallRenderer / (wildcard fallback)3.1 顶层挂载与聊天组件页面顶层用CopilotKit挂载运行时并通过agenttool-rendering指定后端 Agentexport default function ToolRenderingDemo() { return ( CopilotKit runtimeUrl/api/copilotkit agenttool-rendering div classNameflex justify-center items-center h-screen w-full div classNameh-full w-full max-w-4xl Chat / /div /div /CopilotKit ); }Chat组件内部完成所有渲染器注册并渲染CopilotChat agentIdtool-rendering classNameh-full rounded-2xl /。3.2 天气工具渲染器get_weather → WeatherCarduseRenderTool( { name: get_weather, parameters: z.object({ location: z.string() }), render: ({ parameters, result, status }) { const loading status ! complete; const parsed parseJsonResultWeatherResult(result); return ( WeatherCard loading{loading} location{parameters?.location ?? parsed.city ?? } temperature{parsed.temperature} humidity{parsed.humidity} windSpeed{parsed.wind_speed} conditions{parsed.conditions} / ); }, }, [], );这里有一个值得学习的实战细节参数与结果的双源合并。工具执行时parameters.location来自 Agent 的工具调用参数可用于先行展示地点完成后parsed.city来自后端返回结果可能是规范化后的城市名通过parameters?.location ?? parsed.city ?? 两者互补保证卡片在任何阶段都能显示正确的地名。WeatherCardweather-card.tsx是一个纯展示组件加载中显示Fetching weather...与省略号动画完成后显示温度°F、湿度百分比、风速mph与天气状况并通过conditionsEmoji辅助函数把sun/rain/cloud/snow等文本映射为对应图标。3.3 航班工具渲染器search_flights → FlightListCarduseRenderTool( { name: search_flights, parameters: z.object({ origin: z.string(), destination: z.string() }), render: ({ parameters, result, status }) { const loading status ! complete; const parsed parseJsonResultFlightSearchResult(result); return ( FlightListCard loading{loading} origin{parameters?.origin ?? parsed.origin ?? } destination{parameters?.destination ?? parsed.destination ?? } flights{parsed.flights ?? []} / ); }, }, [], );FlightListCardflight-list-card.tsx展示了更丰富的状态处理加载时渲染三个脉冲占位骨架Skeleton完成后渲染航班列表每行显示航空公司、航班号、起降时间与价格同时顶部徽章从searching…切换为N results。它还导出了Flight类型供上层做结果类型推断。3.4 股票工具渲染器get_stock_price → StockCarduseRenderTool( { name: get_stock_price, parameters: z.object({ ticker: z.string() }), render: ({ parameters, result, status }) { const loading status ! complete; const parsed parseJsonResultStockResult(result); return ( StockCard loading{loading} ticker{parameters?.ticker ?? parsed.ticker ?? } priceUsd{parsed.price_usd} changePct{parsed.change_pct} / ); }, }, [], );StockCardstock-card.tsx对涨跌幅做了视觉编码changePct 0用绿色text-[#189370]加前缀下跌用红色text-[#D14343]未返回时显示--。这是典型的结果驱动 UI——同一张卡片根据工具返回值改变含义。3.5 骰子工具渲染器roll_d20 → D20CarduseRenderTool( { name: roll_d20, parameters: z.object({ value: z.number().optional() }), render: ({ result, status }) { const loading status ! complete; const parsed parseJsonResultD20Result(result); const value typeof parsed.value number ? parsed.value : typeof parsed.result number ? parsed.result : undefined; return D20Card loading{loading} value{value} /; }, }, [], );roll_d20的渲染器展示了结果字段兼容处理后端返回可能使用value或result两种字段名渲染器对两者都做兜底解析见 d20-card.tsx。D20Card还带有一个有趣的细节当点数为 20自然暴击时卡片会加上ring-2 ring-[#85ECCE]高亮圈并显示critical!徽章——用最少的代码实现结果驱动的增强交互。3.6 通配符回退渲染器useDefaultRenderTool未被上述四个渲染器覆盖的任何工具调用会落入通过useDefaultRenderTool注册的通配符渲染器useDefaultRenderTool( { render: ({ name, parameters, status, result }) ( CustomCatchallRenderer name{name} parameters{parameters} status{status as CatchallToolStatus} result{result} / ), }, [], );CustomCatchallRenderercustom-catchall-renderer.tsx是一张通用工具卡顶部展示工具名与状态徽章主体区分为 Arguments参数美化 JSON 输出与 Result结果绿色主题高亮两个区块。它的状态徽章定义了完整的工具生命周期语义status徽章文案视觉语义inProgressstreaming橙色系参数流式传入中executingrunning紫色系工具执行中completedone绿色系结果已返回Result区块在非complete状态显示waiting for tool to finish…完成后才渲染解析后的 JSON。parseResult与safeStringify两个辅助函数分别处理结果可能是 JSON 字符串也可能是已解析对象以及任意值安全序列化两种边界情况。3.7 建议引导useSuggestions该演示还通过 suggestions.ts 中的useConfigureSuggestions预置了五条建议提示覆盖每个工具路径以及多工具链式调用场景useConfigureSuggestions({ suggestions: [ { title: Weather in SF, message: Whats the weather in San Francisco? }, { title: Find flights, message: Find flights from SFO to JFK. }, { title: Stock price, message: Whats the current price of AAPL? }, { title: Roll a d20, message: Roll a 20-sided die. }, { title: Chain tools, message: Chain a few tools in this single turn: get the weather in Tokyo, search flights from SFO to Tokyo, and roll a d20., }, ], available: always, });这既是演示入口也是测试入口e2e 测试正是通过点击这些建议药丸pill来驱动对应的工具调用路径。四、结果解析与类型安全parseJsonResult 的边界处理所有渲染器都依赖共享的 parse-json-result.ts。该工具处理了一个真实项目中的常见陷阱——工具结果的形状不确定export function parseJsonResultT(result: unknown): T { if (!result) return {} as T; try { return (typeof result string ? JSON.parse(result) : result) as T; } catch { return {} as T; } }当 Agent 以 JSON 字符串形式发出结果时执行JSON.parse当运行时已在上游解码为对象时直接返回结果缺失或解析失败时返回{}保证渲染器不会因空值崩溃。结合后端 Python 代码可以看到为什么会存在这种字符串化结果在 agent.py 中get_weather、search_flights等工具返回结果时会用--占位符等策略做序列化处理因此前端必须同时兼容字符串与对象两种形态。五、后端呼应AG2 Agent 的工具注册Tool Rendering 的前端只是渲染真正的逻辑在 AG2 后端 Agent。在 agent.py 中工具通过register_for_llm装饰器暴露给 LLMasync def get_weather(location): ... async def search_flights(...): ...Agent 的系统提示system prompt会明确告知 LLM 可用工具及其用途——包括使用get_weather查询任何城市的天气使用search_flights查询航班并渲染富 A2UI 卡片等。而运行时接线发生在 src/app/api/copilotkit/route.tstool-rendering与agentic_chat、tool-rendering-default-catchall、tool-rendering-custom-catchall等名称一起注册到同一个共享的agent.pyConversableAgent每个 demo 页面通过agenttool-rendering/agentIdtool-rendering指向同一个后端。也就是说前端渲染器的name必须与后端工具的注册名一一对应这是 Tool Rendering 正确工作的契约。六、测试验证e2e 断言与 QA 清单Tool Rendering 的可靠性由两层保障6.1 Playwright e2e 测试tests/e2e/tool-rendering.spec.ts 采用药丸驱动的六测试方案每个测试点击建议药丸后断言对应卡片Weather in SF pill断言weather-card可见且weather-city包含 San Francisco、weather-humidity包含 55%、weather-wind包含 10Find flights pill断言flights-card可见flight-origin为 SFO、flight-destination为 JFK且flight-row数量 ≥ 2来自确定性 fixtureStock price pill断言stock-card的 ticker 为 AAPL、价格为 $338.37、涨跌为 -2.96%Roll a d20 pill断言恰好出现5 张d20-card最后一张值为 20前四张均非 20确定性 fixture 依次返回[7, 14, 3, 19, 20]Chain tools pill一次对话内同时出现weather-card、flights-card、d20-card验证多工具链式调用在同一轮中的并发渲染。测试依赖showcase/aimock的确定性录制 fixture如d5-all.json把每个药丸提示词固定映射到确定性的工具调用序列这正是可复现 e2e的关键。各个卡片组件暴露的data-testidweather-card、flights-card、stock-card、d20-card、custom-catchall-card都是为了给测试提供稳定的锚点而设计的。6.2 人工 QA 清单qa/tool-rendering.md 提供了人工验收步骤覆盖聊天界面加载、建议按钮可见性、天气卡片各字段渲染、多城市查询不互相破坏、错误处理空消息与响应时限聊天 3 秒内加载、Agent 10 秒内响应、无控制台报错。七、三种变体从零渲染到全自定义的进阶路径tool-rendering 演示并非孤例manifest 与 PARITY 笔记显示它在showcase/integrations/ag2中有一个三步进阶系列值得对比学习变体渲染策略定位tool-rendering每工具品牌化渲染器 自定义 catch-all本文核心最完整的形态tool-rendering-default-catchall前端零自定义渲染器完全依赖 CopilotKit 内置默认 UI开箱即用见 manifest.yamltool-rendering-custom-catchall单一品牌化通配符渲染器覆盖所有工具调用用一张应用自设计的卡片统一表现tool-rendering-reasoning-chain在工具渲染基础上叠加推理链reasoning chain展示工具渲染 思维过程可视化三者分别对应逐步接入的每个阶段先用默认 UI 快速跑通再换成单一品牌卡片统一样式最后为高价值工具逐个定制专属 UI。八、实战总结与最佳实践回顾原文档与仓库实现Tool Rendering 的正确姿势可以归纳为前端工具名与后端严格对齐useRenderTool的name必须与 AG2 Agent 中register_for_llm的工具名一致这是渲染生效的契约用status驱动全生命周期 UIinProgress流式传参→executing骨架/加载动画→complete结果渲染不要让界面在工具执行期间显得卡死参数与结果双源合并parameters?.xxx ?? parsed.xxx ?? 模式保证执行中和完成后都有数据可展示结果解析容错统一走parseJsonResult兼容字符串与对象两种结果形态为测试预留锚点每个卡片暴露稳定的data-testid配合确定性 fixture 让 e2e 可复现catch-all 兜底通过useDefaultRenderTool或useRenderTool({ name: * })注册通配符渲染器确保任何未注册的新工具都不会白屏。通过 page.tsx 与 react-core 源码 的对照阅读你可以把这个模式直接迁移到自己的 AG2 CopilotKit 项目中让每一个后端工具调用都在聊天界面中活起来。【免费下载链接】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),仅供参考