ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Langfuse chart-view-prototype 深度解析:用「设计即构建」的方式打磨 v4 事件的「任意视图皆图表」体验

Langfuse chart-view-prototype 深度解析:用「设计即构建」的方式打磨 v4 事件的「任意视图皆图表」体验 Langfuse chart-view-prototype 深度解析用「设计即构建」的方式打磨 v4 事件的「任意视图皆图表」体验【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse本指南以 chart-view-prototype 的 README 为骨架系统拆解 Langfuse 前端团队如何通过一个「可丢弃的设计原型」throwaway design prototype阶段 EXP-CHART-PROTOTYPE / phase 0预先打磨 v4 events/traces 视图的「table → chart」可视化体验。读完本文你将理解原型的数据层如何用纯函数模拟未来服务端聚合接口、两种配置 UI 形态内联条 vs 侧边面板的设计取舍以及原型如何为后续 tRPCevents.aggregate接真数据铺路。一、原型的定位为什么要在接真实数据前先做这个按 README 的界定web/src/features/chart-view-prototype/是一个状态明确的可丢弃设计原型它只跑在 Storybook 里、只使用 mock 数据当前应用的任何代码都还没有 import 它。它的存在意义是「design by building」——在把功能接入 v4 真实读路径之前先用代码把「any view is a chart」的完整交互体验建出来供团队README 中提到的 Nikita拍板方向。这一阶段设计phase 0的产物不是可上线的功能而是一份可运行、可点击、可对比的设计决议这正是它与传统静态设计稿的本质区别。从源码结构看目录清单原型由三部分组成web/src/features/chart-view-prototype/ ├── types.ts # 配置规格 ChartViewConfig mock 事件行 PrototypeEvent ├── vocab.ts # 指标/维度/聚合/图表类型词汇表重导出生产词汇 ├── lib/ │ ├── aggregate.ts # 纯函数数据层aggregateEvents(events, config) → DataPoint[] │ ├── aggregate.clienttest.ts # 聚合器与时间分桶的单元测试 │ └── fixtures.ts # 确定性 mock 事件生成器 命名场景 ├── components/ │ ├── ChartViewPrototype.tsx # 根组件持有 mode config 状态渲染两种 take │ └── MockEventsTable.tsx # 代表性的「表格」一侧 └── ChartViewPrototype.stories.tsx # 真正的交付物Storybook stories二、核心体验设计一次「table → chart」的就地可视化原型要还原的核心体验是在 v4 events 视图中把表格翻转成图表并在原位配置可视化配置项是四元组metric指标对什么做聚合如 count、latency、totalCost、totalTokensaggregation聚合方式sum / avg / min / max / p50 / p95 / p99 / countbreakdown分组维度按 model、name、level、type、environment 拆分或不做拆分chart-type图表类型折线、面积、柱状、横向排行、饼图、大数字。同时提供Ask AI入口用自然语言描述需求由 AI 直接产出这份配置从而改变图表配置方式。整个配置模型刻意设计成「扁平的、枚举密集的」结构——正如 chart-view 的 types.ts 注释所说这样既便宜地放进 URL又方便 LLM 生成即 phase 2 的「Ask AI → chart」当前只覆盖 happy path1 个指标 × 1 个维度 × 1 种标准图表类型。Take A 与 Take B两种配置承载形态README 明确要求同时交付两种 UX 形态做对比Take形态特征Take A — inline bar紧凑的、常驻的配置条位于画布上方密集、快速像是表格的「活扩展」改动配置零跳转Take B — side panel最大化画布 可折叠的侧边配置面板更干净、更有引导性给图表留出更大空间两个 take 共用同一个 table↔chart 切换开关、同一个纯聚合器、同一个 mock 的「Ask AI → chart」入口差异只在于配置 UI 的呈现方式见 ChartViewPrototype.stories.tsx 的注释。从 ChartViewPrototype.tsx 的实现可以看到两种 take 的分流逻辑mode table时渲染 MockEventsTable否则按affordance属性决定渲染内联条InlineTake原型内自建还是侧边面板——后者直接复用生产组件 ChartViewPanel且 README 的组件注释明确记录了团队最终选择的方向Take B侧边面板被 Nikita 采纳为共享的生产形态Take A 仅作为设计记录保留在原型中。三、运行方式在 Storybook 里体验这套设计README 给出的运行方式非常简单在仓库根目录执行pnpm --filter web run storybook启动后进入Charts / Chart View Prototype分组即可查看全部 stories。原型的故事集ChartViewPrototype.stories.tsx本身就是设计交付物包含TakeA_InlineBar旗舰场景默认配置是「按模型拆分的、随时间变化的事件计数折线图」TakeB_SidePanel同样体验但配置收入可折叠面板TableView熟悉的 events 表格起始态右上角翻转开关切到图表AskAI针对带 error-spike 的数据集尝试「errors over time by level」这类自然语言问法LatencyP95ByModel、CostByModelRanked、EventsByLevelPie预设配置直接打开对应图表EmptyState空数据状态Comparison两种 take 上下堆叠、直接并排对比。stories 的 args 支持affordanceinline/panel和initialModetable/chart两个切换方便在 Storybook 画布里快速对比。原型内部还通过initialConfig支持注入预设配置见 stories 中的PRESETS。四、Owner map每个文件各司其职README 给出了清晰的职责划分表这里完整继承并补充源码依据文件职责源码要点types.ts配置规格ChartViewConfig mock 事件行PrototypeEventChartViewConfig从生产 chart-view/types.ts 重导出PrototypeEvent是原型的 v4 事件行替身字段名镜像 observations 视图声明startTime、type、name、model、level、environment、latencyMs、totalCost、totalTokensvocab.ts指标/维度/聚合/图表类型词汇表真实 widget 词汇表的忠实子集 取值器 配置强制转换纯逻辑、无 React第一行即export * from /src/features/chart-view/vocab确保 harness 与真实视图共用同一份事实来源另定义 mock 专用的METRIC_EXTRACTORS/DIMENSION_EXTRACTORSlib/aggregate.ts数据层。纯函数aggregateEvents(events, config) → DataPoint[]镜像未来 v4 聚合端点的返回形状内部按图表类型分派NUMBER→ 单点聚合时间序列 → 时间分桶 × 系列分类 → 分组聚合后按指标降序排序含floorToGranularity时间分桶与线性插值分位数lib/fixtures.ts确定性种子化mock 事件生成器 命名场景基于 mulberry32 微型 PRNG时间窗锚定固定时刻2026-06-25T18:00:00Z输出default/errorSpike/sparse/empty四个场景components/ChartViewPrototype.tsx根组件持有modeconfig状态渲染两种 take 之一其下全部为纯展示状态用useStatecoerceConfig归一化patchConfig用useCallback保持稳定data用useMemo派生components/ChartCanvas.tsxReact.memo图表渲染边界派生数据并渲染chart-library原型实际复用生产 chart-view/components/ChartCanvas.tsxcomponents/ConfigControls.tsx共享、纯展示的配置选择器指标/聚合/分组/粒度/图表类型原型直接复用生产 chart-view/components/ConfigControls.tsx 中的MetricSelect、AggregationSelect、BreakdownSelect、GranularitySelect、ChartTypePickercomponents/MockEventsTable.tsx切换开关中代表「表格」的一侧刻意做成轻量、纯展示的表格非完整虚拟化DataTable只显示前 14 行让切换「诚实」而不重造轮子components/ViewModeToggle.tsxtable↔chart 切换开关复用生产 chart-view/components/ViewModeToggle.tsxChartViewPrototype.stories.tsxstories实际交付物见上文第三节五、数据层原理纯函数模拟未来聚合端点原型最关键的抽象在 lib/aggregate.tsaggregateEvents(events, config)是一个纯函数输入 mock 事件数组 配置输出chart-library的DataPoint[]。它的设计目标是镜像生产环境dashboard.executeQuery对 observations 视图的返回形状这样 Storybook 里渲染的视图组件与真实EventsChartView完全一致只差数据来源见 aggregate.ts 注释。五.1 取值器extractormock 世界的「列映射」vocab.ts 中METRIC_EXTRACTORS定义了每个指标如何从PrototypeEvent取出数值export const METRIC_EXTRACTORS: RecordMetricKey, ((e: PrototypeEvent) number) | null { count: null, // null 行数count 度量 latency: (e) e.latencyMs, totalCost: (e) e.totalCost, totalTokens: (e) e.totalTokens, }; export const DIMENSION_EXTRACTORS: RecordDimensionKey, ((e: PrototypeEvent) string) | null { none: null, // null 不做分组 model: (e) e.model ?? unknown, name: (e) e.name, level: (e) e.level, type: (e) e.type, environment: (e) e.environment, };生产环境从 ClickHouse 的 observations 视图读取这些字段harness 则在客户端取值null的语义分别是「行数度量」和「不分组」。五.2 聚合分派三种图表家族的三种算法aggregateEvents按图表类型走三条路径aggregate.tsNUMBER大数字忽略分组全量聚合成单个DataPointtime_dimension与dimension均为undefined时间序列折线/面积/柱状先按timeGranularity把startTime用floorToGranularity分桶aggregate.tsUTC 下向下取整到分钟/小时/天再按系列二次分组最后按时间桶排序输出保证时间轴有序分类横向排行/饼图按分组维度聚合后按指标值降序排序b.metric - a.metric得到「排名」语义。底层aggregate函数aggregate.ts实现了全部聚合算子count直接返回行数sum/avg/min/max遍历取值并过滤非有限数p50/p95/p99走线性插值分位数算法percentile排序后按rank (p/100) * (n-1)在相邻两个值之间插值与常见统计库的分位数口径一致。五.3 测试验证聚合器的行为契约aggregate.clienttest.ts 用 Vitest 锁定了上述行为关键断言包括空事件集返回空数组count 指标按「时间桶 × 分组系列」计数gpt-4o 在 10:00 桶计 2claude-opus-4 在 11:00 计 1时间桶无论输入顺序如何都按时间序输出组内avg计算正确100/200/300 → 200线性插值 p95 精确到95.05对 1..100 序列rank 0.95 × 99 94.05分类结果按指标降序排名NUMBER图表类型产出单一聚合点floorToGranularity对 minute/hour/day 的向下取整如2026-06-25T10:45:12.345Z按 hour 取整为10:00:00.000Z。这套测试让「mock 数据层」本身也有行为契约为 phase 1 替换成真实 tRPC 调用提供了等价的参照基准。六、Mock 数据种子化的确定性事件流lib/fixtures.ts 的目标是确定性stories 和聚合器测试跨运行完全稳定不依赖Math.random和墙钟时间。实现手段是mulberry32 微型 PRNGfixtures.ts32 位种子驱动的确定性伪随机数生成器固定时间窗锚点WINDOW_END Date.parse(2026-06-25T18:00:00.000Z)加权随机采样weightedPick让模型/环境出现频率符合真实分布。生成器刻意模拟真实 trace 流的形态fixtures.ts5 个模型规格gpt-4o、gpt-4o-mini、claude-opus-4、claude-haiku-4、gemini-2.5-pro各带权重、基础延迟、延迟散布与每 token 成本5 个典型操作名generate-answer、summarize、classify-intent、embed-docs、rerank-results80% 为GENERATION其余为SPAN/EVENT级别以DEFAULT为主、薄尾分布 WARNING/ERROR/DEBUG成本随 token 数线性变化延迟随模型变化且非生成事件延迟和成本都显著更低可选errorSpike开关把 ERROR 集中到最近 4 小时窗口让「errors over time」在图上呈现真实的凸起。导出的四个命名场景SCENARIOS对应不同故事default640 条、24h、errorSpike种子 7ERROR 聚集、sparse28 条、6h、empty空数组。七、架构原则frontend-large-feature-architecture 的实践样本README 明确标注原型遵循frontend-large-feature-architecture三条原则在源码中逐一对得上纯派生Pure derivation所有数据变换集中在lib/aggregate.ts组件纯展示、只渲染派生数据。「同样的 events config → 同样的图表」可预测、可测试。这体现在 ChartViewPrototype.tsx 的useMemo(() aggregateEvents(events, config), [events, config])。单向数据流One-way data flow根组件持有modeconfig子组件只接收值 稳定的memoizedonChange回调除回调外没有任何向上回传的通道。patchConfig用useCallback包装且依赖为空数组ChartViewPrototype.tsx保证子组件引用稳定。渲染边界Render boundariesChartCanvas和选择器都做了React.memo单次配置变更不会触发重新聚合或无关 UI 重渲染。MockEventsTable同样React.memo化MockEventsTable.tsx。当图表类型切换时isTimeSeries计算结果用于禁用/启用粒度选择器粒度选择只在时间序列图表下有意义。配置的强制归一化coercionvocab.ts 的coerceConfig是配置安全的最后防线把配置的每个字段钳制到已知枚举成员URL 参数不可信并在指标改变时把聚合方式重置为该指标支持的默认聚合。视图组件和 URL 状态都经过它往返因此两者都不可能产出非法查询。根组件初始化和每次patchConfig都调用它const [config, setConfig] useStateChartViewConfig(() coerceConfig({ ...DEFAULT_CONFIG, ...initialConfig }), ); const patchConfig useCallback( (patch: PartialChartViewConfig) setConfig((prev) coerceConfig({ ...prev, ...patch })), [], );默认配置DEFAULT_CONFIG是「count 指标 × count 聚合 × 按 model 分组 × 折线时间序列 × 小时粒度」即旗舰 story 的打开状态。八、与生产 feature 的共享词汇表是唯一事实来源原型并非从零造轮子。它重导出并复用生产 features/chart-view 的能力类型chart-view/types.ts 定义了ViewMode、MetricKey、DimensionKey、AggregationFn、TimeGranularity和扁平枚举密集的ChartViewConfig词汇表chart-view/vocab.ts 中METRICS的每个指标都携带真实查询标识measurecount/latency/totalCost/totalTokens和单位millisecond/USDDIMENSIONS的每个维度都携带 observations 视图字段名如providedModelName、level、environment使后续构建聚合查询是「直接映射」图表类型则从DashboardWidgetChartType位于 packages/shared/src/db 的langfuse/shared/src/db导入中选取折线/面积/柱状时间序列、横向排行、饼图、大数字六种刻意排除透视表与表格侧重叠和直方图happy path 外配置语义describeConfig把配置渲染成人类可读的句子既是图表副标题也是 Ask-AI 流程中「我为你生成了什么」的确认文案——例如Count of events by model over timecount 指标省略聚合前缀NUMBER图表不声称分组见 vocab.ts。原型之所以能做到「harness 与真实视图共用同一份事实来源」核心在于 prototype/vocab.ts 的第一行export * from /src/features/chart-view/vocab。九、接入路径后续阶段的既定计划README 明确指出原型文件夹尚未包含生产接入代码并给出清晰的后续路线Phase 1把 lib/aggregate.ts 的客户端聚合函数替换为tRPCevents.aggregate调用返回同样的DataPoint[]把modeconfig移入URL 状态实现可逆的切换挂载进features/events/components/EventsTable.tsx并以 v4 读路径做特性门控gated。后续的「Ask AI → chart」phase 2 方向则建立在配置模型的 LLM 友好性之上扁平、枚举密集的ChartViewConfig让自然语言到配置的转换对 LLM 而言成本极低。对应地生产侧 EventsChartView.tsx 与 lib/buildChartQuery.ts、lib/chartConfigToWidget.ts 已经搭起了从配置到查询、到 widget 的桥原型正是这套生产代码在接真实数据前的「设计验证床」。十、小结一次可运行的 UI 设计决议chart-view-prototype 的价值在于把「表格视图内嵌可配置图表」这一体验从想法变成了可点击、可对比、可测试的 Storybook 原型纯函数数据层aggregateEvents提前锁定了未来聚合端点的数据契约种子化 fixtures 保证了演示与测试的确定性而 Take A / Take B 两种配置形态则把交互方向的取舍摆到桌面上供决策。对于想理解 Langfuse 前端如何「design by building」、以及 v4 chart view 背后配置模型与数据流的开发者这个原型文件夹连同其测试、stories 和生产 chart-view 模块是一份完整可循的参考路径。【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表