ZCode 中的 AI 上下文使用可视化:Context 复合组件体系实战解析
【免费下载链接】ZCodeZ.ai's coding agent harness. Powerful, intelligent, extensible.项目地址: https://gitcode.com/gh_mirrors/zco/ZCode
导读
本文围绕 ZCode 开源仓库中 AI Elements 技能体系的核心组件Context展开,系统讲解如何在一个基于 AI SDK 的 React 应用中展示模型上下文窗口占用率、token 消耗明细(输入 / 输出 / 推理 / 缓存)与实时成本估算。通过阅读本文,你将掌握 Context 复合组件(compound component)的完整 API、React Context 数据流设计、基于Intl.NumberFormat的 token 智能格式化,以及基于tokenlens的成本计算原理,并看到该组件在 ZCode 产品界面中的真实集成方式。
Context是一个用于展示 AI 模型上下文窗口使用情况的复合组件系统:它通过交互式悬停卡片(hover card)呈现上下文窗口占用、token 消耗拆分与成本估算。组件源自 Vercel 的 ai-elements,配套的可运行示例见 .agents/skills/ai-elements/scripts/context.tsx。
安装与前置条件
在项目中使用 Context 组件前,请确认满足 AI Elements 技能体系的环境要求:
- Node.js 18 及以上;
- 一个安装了AI SDK的 React / Next.js 项目;
- 已安装shadcn/ui(若未安装,执行安装命令时会自动引入)。
安装 Context 组件最直接的方式是使用 AI Elements 专用 CLI(请根据项目的packageManager选择npx、pnpm dlx或bunx --bun作为包执行器):
npx ai-elements@latest add contextCLI 会把组件源码直接集成到项目的@/components/ai-elements/目录(或你在 shadcn 配置中指定的组件目录),组件以源码形式存在于项目代码库中,而非隐藏在第三方库内部,因此你可以像使用普通 React 组件一样导入使用,甚至直接打开文件查看实现或做定制修改。
在 ZCode 仓库中,该组件的本地实现已落地于 packages/ui/src/components/ai-elements/context.tsx,其依赖的tokenlens版本为^1.3.1(见 packages/ui/package.json)。
核心特性一览
从参考文档与源码实现可以归纳出 Context 组件的以下能力:
- 复合组件架构(Compound Component):由根组件与多个子组件灵活组合,按需拼装展示元素;
- 可视化进度指示:SVG 圆形进度环直观显示上下文使用百分比;
- Token 明细拆分:输入、输出、推理、缓存四类 token 分别展示;
- 实时成本估算:借助
tokenlens库按模型定价实时计算费用; - 智能格式化:token 数量自动按 K / M / B 缩写;
- 交互式悬停卡片:悬停触发详细信息的 HoverCard 弹层;
- Context Provider 模式:通过 React Context API 完成干净的数据流传递;
- TypeScript 支持:所有组件均带完整类型定义;
- 无障碍设计:正确使用 ARIA 属性与语义化 HTML;
- 主题自适应:进度指示使用
currentColor,随宿主主题自动适配。
Props 全表
以下完整继承自参考文档的 Props 定义,并结合源码 packages/ui/src/components/ai-elements/context.tsx 补充了说明。
<Context />— 根 Provider
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
maxTokens | number | - | 上下文窗口总大小(token 数),用于计算占用百分比;源码中formatUsagePercent会在maxTokens <= 0时返回 0,避免除零异常 |
usedTokens | number | - | 当前已使用的 token 数 |
usage | LanguageModelUsage | - | 来自 AI SDK 的详细 token 用量拆分(input、output、reasoning、cached input tokens),对应ContextSchema中的usage字段 |
modelId | ModelId(即string) | - | 模型标识符,用于成本计算,例如"openai:gpt-5" |
...props | ComponentProps<HoverCard> | - | 其余属性透传给底层 HoverCard 组件 |
根组件在源码中通过useMemo缓存{ maxTokens, modelId, usage, usedTokens }形成 context 值,并包裹HoverCard(closeDelay={0}、openDelay={0},即悬停零延迟开合)。任何子组件在Context之外使用时都会抛出"Context components must be used within Context"错误(见useContextValue的实现)。
<ContextTrigger />— 触发按钮
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 自定义触发元素;未提供时渲染默认按钮(带百分比进度环图标) |
loading | boolean | false | ZCode 扩展属性:为true时进度环图标替换为旋转的Loader2加载图标(对应额度自动重置进行中状态,源码注释注明为「正在重置」状态) |
...props | ComponentProps<Button> | - | 透传给默认按钮元素 |
默认触发按钮使用variant="ghost"、size="icon-md"样式,内部通过HoverCardTrigger asChild包装,以保证无障碍与聚焦行为。
<ContextContent />— 悬停卡片容器
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
className | string | - | 附加 CSS 类,通过cn合并 |
...props | ComponentProps<HoverCardContent> | - | 透传给 HoverCardContent 组件 |
源码中该容器固定宽度!w-64,采用rounded-lg、bg-tooltip背景与text-tooltip-foreground前景色,关闭默认阴影、圆环与外框(shadow-none ring-0 outline-0)。
<ContextContentHeader />— 头部
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 自定义头部内容;未提供时渲染百分比、token 数与进度条 |
action | React.ReactNode | - | ZCode 扩展属性:头部右上角可选的行动作区域 |
progressSegments | readonly { className?: string; id: string; percent: number }[] | - | ZCode 扩展属性:分段进度条配置,与 progress.tsx 的分段渲染能力配合 |
...props | ComponentProps<"div"> | - | 透传给头部 div 元素 |
默认头部渲染 "Context" 标题、分隔线、百分比(保留 1 位小数,如40.0%)、已用 / 总量的完整数字(如40,000 / 128,000),以及一个基于Progress组件的横向进度条(value={usedPercent * 100})。
<ContextContentBody />— 主体
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 主体内容,通常放置各 Usage 拆分组件 |
...props | ComponentProps<"div"> | - | 透传给 body div 元素 |
主体默认使用bg-menu背景与p-3内边距,与头部、底部形成视觉分区。
<ContextContentFooter />— 底部
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 自定义底部内容;未提供且传入modelId时渲染总成本 |
...props | ComponentProps<"div"> | - | 透传给 footer div 元素 |
底部通过border-t border-popover-border与bg-surface形成次级背景。默认渲染 "Total cost" 与格式化后的总费用;未提供modelId时成本为$0.00。
Usage 组件(ContextInputUsage/ContextOutputUsage/ContextReasoningUsage/ContextCacheUsage)
四个用量组件共享相同的 Props 约定:
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 自定义内容;未提供时渲染对应类型的 token 数与成本 |
className | string | - | 附加 CSS 类 |
...props | ComponentProps<"div"> | - | 透传给 div 元素 |
源码中的行为细节值得注意:
- 输入(Input):读取
usage.inputTokens,成本按{ input: inputTokens, output: 0 }计算; - 输出(Output):读取
usage.outputTokens,成本按{ input: 0, output: outputTokens }计算; - 推理(Reasoning):读取
usage.reasoningTokens,成本以{ reasoningTokens }独立计算(推理 token 通常有特殊定价); - 缓存(Cache):读取
usage.cachedInputTokens,成本以{ cacheReads: cacheTokens, input: 0, output: 0 }计算(缓存读取通常价格更低)。
每个组件在对应 token 数为 0 时返回null(不渲染),token 数未定义时显示占位符—;成本文本通过• $X.XX形式附在 token 数之后。
组件架构:复合组件 + React Context
Context 组件采用复合组件(compound component)模式,配合 React Context 在多个展示组件间共享数据,结构分层如下:
<Context>—— 根 Provider,持有全部上下文数据(usedTokens、maxTokens、usage、modelId);<ContextTrigger>—— 交互式触发元素(默认:带百分比进度环的按钮);<ContextContent>—— 悬停卡片内容容器;<ContextContentHeader>—— 头部,含进度可视化;<ContextContentBody>—— 主体,承载用量拆分;<ContextContentFooter>—— 底部,展示总成本;- Usage 组件—— Input / Output / Reasoning / Cache 四类独立用量展示。
数据流方面,根组件Context用useMemo构造 context 值并通过ContextContext.Provider下发,所有子组件通过useContextValue()取用;若脱离根组件使用则抛出运行时错误(参见 packages/ui/src/components/ai-elements/context.tsx 中createContext与useContextValue的实现)。底层交互依赖 shadcn/ui 风格的 hover-card.tsx(基于 Radix UI 的HoverCard,含 Portal、对齐与动画)与 progress.tsx(支持分段segments渲染)。
圆形进度环的实现细节
默认触发按钮中的图标是一个纯 SVG 环形进度条(源码ContextIcon):
- 常量定义:
ICON_RADIUS = 10、ICON_VIEWBOX = 24、ICON_CENTER = 12、ICON_STROKE_WIDTH = 4、PERCENT_MAX = 100; - 周长
circumference = 2π × 10,根据使用比例usedPercent计算dashOffset = circumference × (1 - usedPercent); - 底环
opacity="0.25"表示空余容量,进度弧opacity="0.7"且transform: rotate(-90deg)使进度从 12 点钟方向起始,strokeLinecap="round"提供圆角端点; - 颜色直接使用
currentColor,因此自动适配按钮前景色与宿主主题。
Token 智能格式化
组件对 token 数值的格式化遵循Intl.NumberFormat的 compact(紧凑)记法,按数量级自动缩写:
- < 1,000:显示精确数值,如
842; - 1,000+:追加 K 后缀,如
32K; - 1,000,000+:追加 M 后缀,如
1.5M; - 1,000,000,000+:追加 B 后缀,如
2.1B。
在 ZCode 实现中,该逻辑被封装为独立的formatCompactTokenNumber工具函数,位于 packages/ui/src/lib/tokenNumberFormat.ts。源码注释明确记录了本地化决策:token 数值应走当前 locale 的 compact 记法(中文环境显示万/亿,英文环境显示 K/M/B),避免此前「为修英文长单位而把所有 locale 强制成 K/M/B」的回归问题;而模型列表的容量 badge(formatModelContextWindowLabel)作为技术规格则固定使用en-US的 K/M/B 展示。数值非有限数(!Number.isFinite)时返回空字符串兜底。
成本计算原理
当传入modelId时,组件通过tokenlens库自动完成成本估算(packages/ui/src/components/ai-elements/context.tsx 中getUsage(...).costUSD?.totalUSD)。成本构成如下:
- 输入 token:按模型的输入定价计算;
- 输出 token:按模型的输出定价计算;
- 推理 token:针对支持推理(reasoning)的模型使用特殊定价;
- 缓存 token:按缓存输入读取(cache read)的优惠价计算;
- 总成本:各类 token 成本之和。
成本最终使用Intl.NumberFormat以USD 货币格式输出(如$0.42)。值得说明的是,页脚总成本仅以inputTokens与outputTokens两项调用getUsage,而各 Usage 组件的单项成本分别独立计算,推理与缓存成本在单项明细中体现。
完整可运行示例
参考文档指向的示例脚本 .agents/skills/ai-elements/scripts/context.tsx 演示了完整组合方式——以一个128,000token 的上下文窗口、已用40,000token、openai:gpt-5模型为数据,组合全部 7 类子组件:
"use client"; import { Context, ContextCacheUsage, ContextContent, ContextContentBody, ContextContentFooter, ContextContentHeader, ContextInputUsage, ContextOutputUsage, ContextReasoningUsage, ContextTrigger, } from "@/components/ai-elements/context"; const Example = () => ( <div className="flex items-center justify-center p-8"> <Context maxTokens={128_000} modelId="openai:gpt-5" usage={{ cachedInputTokens: 0, inputTokens: 32_000, outputTokens: 8000, reasoningTokens: 0, totalTokens: 40_000, }} usedTokens={40_000} > <ContextTrigger /> <ContextContent> <ContextContentHeader /> <ContextContentBody> <ContextInputUsage /> <ContextOutputUsage /> <ContextReasoningUsage /> <ContextCacheUsage /> </ContextContentBody> <ContextContentFooter /> </ContextContent> </Context> </div> ); export default Example;注意:usage的字段名必须与 AI SDK 的LanguageModelUsage类型一致(inputTokens、outputTokens、reasoningTokens、cachedInputTokens、totalTokens),其中推理与缓存 token 为 0 时,对应 Usage 组件会自动隐藏,不会产生空行。
在 ZCode 产品中的真实应用
Context 组件并非仅供示例使用,它在 ZCode 桌面端聊天输入工具栏中被实际集成:packages/ui/src/chat-input-toolbar/contextUsage.tsx导入了Context、ContextContentBody、ContextContent、ContextTrigger等组件,将 Context 窗口占用、Coding Plan 与 Start Plan 额度三段信息聚合在同一个上下文面板中展示(源码头注说明该文件是「context 面板聚合 Context windows、Coding Plan 和 Start Plan 三段紧耦合展示」)。
该集成还展示了两个扩展点:通过loading属性在额度自动重置期间替换进度环图标,以及通过progressSegments传入多段进度条配置(配合 progress.tsx 的分段能力)。这说明 Context 复合组件通过...props透传与可选子节点约定,可以低成本嵌入真实业务面板。
样式与主题集成
组件基于 Tailwind CSS 并遵循宿主设计系统(shadcn/ui 语义 token):
- 进度指示使用
currentColor,自动适配主题与前景色; - 悬停卡片宽度与内边距可定制(默认
!w-64+p-3); - 页脚使用次级背景(
bg-surface+ 顶部边框)形成视觉分隔; - 主体文本统一使用
text-ui-base字号,保持一致性; - 次级信息使用 muted 前景色(
text-foreground-subtle/text-muted-foreground)。
由于组件代码以源码形式进入你的项目,你可以直接修改 Tailwind 类名来调整外观,例如去掉圆角、更换背景 token 等,无需额外配置。
延伸阅读
- 组件完整实现:packages/ui/src/components/ai-elements/context.tsx
- 可运行示例:.agents/skills/ai-elements/scripts/context.tsx
- Token 格式化工具:packages/ui/src/lib/tokenNumberFormat.ts
- 底层 UI 基元:hover-card.tsx、progress.tsx
- 产品内集成案例:packages/ui/src/chat-input-toolbar/contextUsage.tsx
- 技能体系总览与安装说明:.agents/skills/ai-elements/SKILL.md
【免费下载链接】ZCodeZ.ai's coding agent harness. Powerful, intelligent, extensible.项目地址: https://gitcode.com/gh_mirrors/zco/ZCode
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考