搭建自动化 Web 页面性能检测系统 —— AI 篇
我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。
本文作者:琉易
这一篇是系列文章:
页面性能对于用户体验、用户留存有着重要影响,当页面加载时间过长时,往往会伴随着一部分用户的流失,也会带来一些用户差评。性能的优劣往往是同类产品中胜出的影响因素,也是一个网站口碑的重要评判标准。
系统架构图(简略)
本篇重点讲解 AI 分析模块的设计与实践。
AI 分析模块的设计与实现
输入与输出
- 输入:Lighthouse 检查产生的 JSON 数据
- 由于每次检测产生的 JSON 数据较大,一般有 350KB ~ 500KB,而大模型往往是根据输入 Tokens 进行计费,且并不是单纯的按量计费,类似于生活中常见的阶梯计费;另外模型支持的输入也有限,一般为 32k Tokens 或 64k Tokens。所以我们将 JSON 数据传输给大模型前需要进行精简。
- 保留检测结果中的关键数据,如:环境数据(environment)、每一项检测指标的详细结果(audits)、检测时的配置参数(configSettings)、汇总各类指标的最终得分(categories)。
- 输出:自然语言优化建议列表
- 如:建议将图片资源启用 lazy-load
- 如:减小某个图片文件的大小以减少传输时间
核心组成
- JSON 清洗与摘要
- Prompt 定义
- openai 接口集成
- 流式处理
Prompt 设计要点
构建一个高质量的 Prompt 是成功的关键,以下是一个例子:
你是一个网页性能优化专家。我将提供一个通过 Google Lighthouse 生成的 JSON 报告,请你根据报告中的内容:
1. 每个关键指标给出两三条优化建议,需要结合 json 中的实际数据进行举例。
2. 回答的内容使用 markdown 格式。
3. 专业名词需要使用中文。
实际测试中 Kimi 的 moonshot-v1-auto
模型回答更快,百炼平台的模型输入输出 Tokens 限制更宽泛,但是输出速度略慢;百炼平台的免费额度更多,OpenAI 费用较高且部署后会有访问的问题。
关键技术点
Lighthouse 报告数据结构解析
JSON 数据清洗与摘要是大模型调用能否成功的关键,清洗后的结果是 Prompt 的数据来源,如果内容较多可能会超出模型输入 Tokens 的限制从而导致调用失败。
- audits 中会包含各种指标近百种,我们可以删除一些内容较多但对分析用处不大的数据,如:offscreen-images、screenshot-thumbnails 等。
- Lighthouse 生成的 JSON 数据会直接保存瀑布图等图片的 Base64 格式数据,这些图片数据占用 Tokens 明显。
经过清洗,尽量将输入的 Tokens 控制在 100k 以内。
流式输出
openai 是一个 npm 包,通过这个 npm 包可以快速的对接各种大模型的 API 调用服务。
// 流式输出
const stream = await client.chat.completions.create({
model: 'moonshot-v1-auto',
messages: [
{
role: 'system',
content: `你是一个网页性能优化专家。我将提供一个通过 Google Lighthouse 生成的 JSON 报告,请你根据报告中的内容:
1. 每个关键指标给出两三条优化建议,需要结合 json 中的实际数据进行举例。
2. 回答的内容使用 markdown 格式。
3. 专业名词需要使用中文。`,
},
{ role: 'user', content: jsonData },
],
temperature: 0.3,
stream: true,
});
// 当启用流式输出模式(stream=True),SDK 返回的内容也发生了变化,我们不再直接访问返回值中的 choice
// 而是通过 for 循环逐个访问返回值中每个单独的块(chunk)
for await (const chunk of stream) {
if (abortSignal?.aborted) {
console.log(`${taskIdLogStr}任务中止, kimi chat`);
break;
}
// 在这里,每个 chunk 的结构都与之前的 completion 相似,但 message 字段被替换成了 delta 字段
const delta = chunk.choices[0].delta;
if (delta.content) {
onData(delta.content);
}
}
@ApiOperation({ summary: '分析检测结果' })
@HttpCode(HttpStatus.OK)
@Post('reportChat')
@RawResponse()
async reportChat(@Body() query: ReportChatReqDto, @Res() res: Response) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
const abortController = new AbortController();
// 👇 监听客户端断开连接(如 Abort)
res.on('close', () => {
abortController.abort();
});
try {
await this.AIService.reportChat(
query,
(content: string) => {
res.write(`data: ${JSON.stringify({ content })}\n\n`);
},
abortController.signal
);
res.write(`data: [DONE]\n\n`);
} catch (error) {
res.write(`data: [ERROR] ${error.message || 'stream error'}\n\n`);
abortController.abort();
} finally {
res.end();
}
}
非流式输出
// 非流式输出
const completion = await client.chat.completions.create({
model: 'moonshot-v1-auto',
messages: [
{
role: 'system',
content: `你是一个网页性能评分分析专家。我将提供一个产品的性能评分数据,帮我分析得分趋势和较大的得分变化。回答的内容不要带格式符号,尤其是 **。`,
},
{ role: 'user', content: jsonData },
],
temperature: 0.3,
});
return completion.choices[0].message.content;
实现的功能点
检测报告的智能分析与建议
由于保存的是 html 文件,我们可以通过正则将 html 文件中的 JSON 数据提取出来,用于后续的清洗与分析。
结合清洗后的 JSON 数据给出优化建议。
数据周报的趋势分析
将过去一周的分数给到大模型,由大模型分析解读得分的变化趋势。
后续规划
- JSON 数据清洗更精确,确定好哪些是关键性能指标
- 将 Lighthouse 的具体评分规则同步给大模型
- 优化 Prompt,更换更合适的大模型
- 结合埋点数据
- 分析页面性能与用户停留时长的关系
- 分析用户跳出率与页面性能的关系
- 结合采集到的埋点数据分析页面性能对业务指标的影响