AI 浏览器卡顿、启动慢、响应慢?这套性能优化方案和源码够用了
AI浏览器 性能优化教程|附源码
适用场景:你正在开发一款带有 AI 助手能力的浏览器、AI 搜索浏览器、网页总结浏览器、RAG 浏览器插件,或基于 Electron / Chromium / WebView 封装的智能浏览器应用。
本文将从 启动速度、页面加载、AI 推理、流式响应、缓存、内存管理、前端渲染、接口并发、源码实现 等角度,系统讲解 AI 浏览器的性能优化方案,并附带可直接参考的源码示例。
一、什么是 AI 浏览器?
AI 浏览器并不只是“浏览器 + 聊天窗口”的简单组合。
一个完整的 AI 浏览器,通常包含以下能力:
-
普通网页浏览能力
- 地址栏访问网页
- 标签页管理
- 历史记录
- 收藏夹
- 下载管理
-
AI 助手能力
- 网页内容总结
- 网页问答
- 当前页面翻译
- 智能搜索
- 文档解析
- 表单自动填写
- 代码解释
- 网页内容对话
-
上下文感知能力
- 自动读取当前网页正文
- 识别标题、作者、发布时间
- 提取页面结构
- 分析用户当前任务
-
本地与云端协同能力
- 本地缓存
- 云端大模型推理
- 本地向量数据库
- RAG 检索增强生成
但是,AI 浏览器相比传统浏览器更容易遇到性能问题。原因也很明显:除了浏览网页,它还需要处理网页解析、文本提取、AI 请求、流式响应、上下文拼接、向量检索、前端渲染等额外任务。
如果不做优化,用户会明显感受到:
- 启动慢;
- 页面卡顿;
- AI 响应延迟高;
- 内存占用不断上涨;
- 多标签页时 CPU 飙升;
- 网页总结经常超时;
- AI 面板输入时掉帧;
- 流式输出不流畅;
- 请求大模型接口成本过高。
因此,AI 浏览器的性能优化不是某个点的优化,而是一套系统工程。
二、AI 浏览器常见性能瓶颈
在优化之前,先要明确瓶颈在哪里。
1. 启动阶段性能瓶颈
如果你的 AI 浏览器基于 Electron 开发,启动慢通常来自以下几个方面:
- 主进程加载过多模块;
- 启动时初始化 AI SDK;
- 启动时读取大量历史记录;
- 启动时加载多个窗口;
- 启动时创建数据库连接;
- 首屏渲染依赖过多 JS 文件;
- 首屏加载 AI 面板资源过大。
很多开发者会在应用启动时做太多事情,例如:
import OpenAI from 'openai'
import sqlite3 from 'sqlite3'
import vectorDB from './vector-db'
import historyService from './history'
import bookmarkService from './bookmark'
import aiSummaryService from './ai-summary'
这些模块一旦在启动阶段同步加载,就会拖慢应用启动速度。
2. 网页内容提取瓶颈
AI 浏览器经常需要读取当前网页内容,例如用于总结文章、问答或翻译。
常见问题包括:
- 直接读取整个
document.body.innerText; - 没有过滤导航栏、广告、评论区;
- 一次性传输大量文本到主进程;
- 文本过长导致大模型请求缓慢;
- 页面还没加载完成就开始解析;
- 对 SPA 页面重复解析。
错误示例:
const text = document.body.innerText
这行代码看似简单,但在复杂网页中可能会提取几十万字符,既影响页面性能,又增加 AI 请求成本。
3. AI 请求延迟瓶颈
AI 浏览器的核心体验之一是“快”。
用户点击“总结当前网页”后,如果等待 10 秒才看到结果,体验会很差。
AI 请求慢通常有这些原因:
- prompt 太长;
- 上下文没有压缩;
- 没有流式输出;
- 没有请求缓存;
- 多个 AI 请求串行执行;
- 模型选择不合理;
- 没有超时控制;
- 没有取消机制;
- 没有分层任务设计。
4. 前端渲染瓶颈
AI 浏览器通常有一个侧边栏或浮窗,用于展示 AI 对话。
如果流式响应每来一个 token 就更新一次 React 状态,会导致频繁渲染。
错误示例:
reader.onmessage = (token) => {
setContent(prev => prev + token)
}
当 token 很多时,React 会频繁触发更新,导致页面卡顿。
5. 内存泄漏问题
AI 浏览器常见内存泄漏来源:
- 标签页关闭后事件监听未移除;
- WebSocket 没有关闭;
- AbortController 没有释放;
- 缓存无限增长;
- DOM 引用被长期持有;
- 大文本内容被存储在内存中;
- 每个标签页都创建独立 AI 上下文。
尤其是 Electron 应用,如果不控制内存,很容易出现运行一段时间后占用几个 GB 的情况。
三、整体优化思路
AI 浏览器性能优化可以分为以下几个层级:
| 优化层级 | 重点方向 |
|---|---|
| 启动优化 | 延迟加载、按需初始化、减少首屏 JS |
| 页面优化 | 内容提取、DOM 过滤、节流防抖 |
| AI 优化 | Prompt 压缩、流式输出、缓存、模型路由 |
| 渲染优化 | 批量更新、虚拟列表、Web Worker |
| 网络优化 | 请求合并、超时取消、并发控制 |
| 内存优化 | LRU 缓存、资源释放、标签页生命周期 |
| 架构优化 | 主进程/渲染进程隔离、任务队列、插件化 |
下面开始进入实战。
四、启动性能优化
1. AI SDK 延迟加载
不要在应用启动时立即加载 AI SDK,而是在用户第一次使用 AI 功能时再加载。
优化前
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
优化后
let aiClient = null
async function getAIClient() {
if (aiClient) return aiClient
const { default: OpenAI } = await import('openai')
aiClient = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
return aiClient
}
module.exports = {
getAIClient
}
这样可以避免 AI SDK 在启动阶段参与打包和初始化,减少首屏压力。
2. 历史记录懒加载
浏览器历史记录通常数据量很大,启动时不应该全部读取。
class HistoryService {
constructor(db) {
this.db = db
}
async getRecentHistory(limit = 50) {
return this.db.all(
`SELECT title, url, visit_time
FROM history
ORDER BY visit_time DESC
LIMIT ?`,
[limit]
)
}
async searchHistory(keyword, limit = 20) {
return this.db.all(
`SELECT title, url, visit_time
FROM history
WHERE title LIKE ? OR url LIKE ?
ORDER BY visit_time DESC
LIMIT ?`,
[`%${keyword}%`, `%${keyword}%`, limit]
)
}
}
原则是:
- 首页只加载最近 50 条;
- 搜索时再查询数据库;
- 不要一次性把全部历史记录塞进内存;
- 长列表使用分页或虚拟列表。
五、网页内容提取优化
AI 浏览器的关键能力之一是理解网页。
但“提取网页正文”一定要做精细化处理,不能粗暴读取整个页面。
1. 内容提取脚本
下面是一个可注入网页的内容提取脚本。
function extractPageContent() {
const clone = document.body.cloneNode(true)
const removeSelectors = [
'script',
'style',
'noscript',
'iframe',
'svg',
'canvas',
'nav',
'footer',
'header',
'aside',
'.ad',
'.ads',
'.advertisement',
'.comment',
'.comments',
'.sidebar',
'.recommend',
'[role="navigation"]',
'[aria-hidden="true"]'
]
removeSelectors.forEach(selector => {
clone.querySelectorAll(selector).forEach(el => el.remove())
})
const title = document.title || ''
const headings = Array.from(clone.querySelectorAll('h1,h2,h3'))
.map(el => el.innerText.trim())
.filter(Boolean)
.slice(0, 20)
const paragraphs = Array.from(clone.querySelectorAll('p,article,main,section'))
.map(el => el.innerText.trim())
.filter(text => text.length > 30)
const content = paragraphs.join('\n\n')
.replace(/\n{3,}/g, '\n\n')
.replace(/[ \t]{2,}/g, ' ')
.trim()
return {
title,
url: location.href,
headings,
content: content.slice(0, 30000),
length: content.length
}
}
这个脚本做了几件事:
- 克隆
document.body,避免直接修改页面; - 删除无用节点;
- 提取标题与正文;
- 过滤过短文本;
- 限制最大字符长度;
- 返回结构化数据。
2. 防止频繁解析页面
SPA 网站内容可能不断变化,如果每次 DOM 变化都立即解析,会严重影响性能。
可以使用防抖:
function debounce(fn, delay = 500) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
const handlePageChanged = debounce(() => {
const data = extractPageContent()
window.postMessage({
type: 'AI_BROWSER_PAGE_CONTENT',
payload: data
})
}, 1000)
const observer = new MutationObserver(handlePageChanged)
observer.observe(document.body, {
childList: true,
subtree: true
})
同时,在标签页关闭或页面卸载时必须释放监听:
window.addEventListener('beforeunload', () => {
observer.disconnect()
})
六、Prompt 压缩优化
AI 浏览器很容易把整篇网页内容直接塞给大模型,但这会造成三个问题:
- 请求变慢;
- token 成本升高;
- 模型容易忽略重点。
推荐做法是先压缩上下文。
1. 基础文本压缩函数
function compressText(text, maxLength = 8000) {
if (!text) return ''
const cleanText = text
.replace(/\s+/g, ' ')
.replace(/相关推荐|广告|分享到|登录|注册/g, '')
.trim()
if (cleanText.length <= maxLength) {
return cleanText
}
const start = cleanText.slice(0, Math.floor(maxLength * 0.45))
const middleStart = Math.floor(cleanText.length * 0.45)
const middle = cleanText.slice(middleStart, middleStart + Math.floor(maxLength * 0.25))
const end = cleanText.slice(-Math.floor(maxLength * 0.3))
return `${start}\n\n[中间内容已压缩]\n\n${middle}\n\n[部分内容省略]\n\n${end}`
}
这种方式虽然简单,但对于网页总结类任务已经有明显效果。
2. 结构化 Prompt
不要写过于宽泛的 Prompt,例如:
请总结这个网页
更推荐结构化 Prompt:
function buildSummaryPrompt(page) {
const content = compressText(page.content, 8000)
return `
你是一个专业的网页阅读助手。请根据下面的网页内容生成中文总结。
要求:
1. 用简洁清晰的语言总结;
2. 输出 5 个核心要点;
3. 如果网页包含教程步骤,请按步骤整理;
4. 如果内容中存在数据、结论或观点,请单独列出;
5. 不要编造网页中不存在的信息。
网页标题:${page.title}
网页地址:${page.url}
网页正文:
${content}
请按以下格式输出:
## 一句话总结
...
## 核心要点
1. ...
2. ...
3. ...
4. ...
5. ...
## 重要细节
- ...
## 适合继续追问的问题
- ...
`
}
结构化 Prompt 的好处是:
- 模型输出更稳定;
- 减少无效废话;
- 降低二次修正成本;
- 前端更容易渲染结果。
七、AI 流式响应优化
流式输出是 AI 浏览器体验优化的关键。
用户不一定要求总耗时最低,但希望“尽快看到内容出现”。
因此,AI 接口应该优先使用 stream。
下面以 Node.js 服务端为例。
async function streamAIResponse({ prompt, res }) {
const client = await getAIClient()
const stream = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'user',
content: prompt
}
],
temperature: 0.3,
stream: true
})
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
for await (const chunk of stream) {
const text = chunk.choices?.[0]?.delta?.content || ''
if (text) {
res.write(`data: ${JSON.stringify({ text })}\n\n`)
}
}
res.write(`data: ${JSON.stringify({ done: true })}\n\n`)
res.end()
}
前端接收:
async function requestSummary(prompt, onText) {
const response = await fetch('/api/ai/summary', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt })
})
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
while (true) {
const { value, done } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
const lines = chunk.split('\n\n').filter(Boolean)
for (const line of lines) {
if (!line.startsWith('data:')) continue
const json = line.replace(/^data:\s*/, '')
const data = JSON.parse(json)
if (data.text) {
onText(data.text)
}
}
}
}
八、流式渲染防卡顿优化
如果每次收到一个 token 都更新页面,会导致 React 或 Vue 频繁渲染。
推荐使用缓冲区,每 50ms 或 100ms 批量更新一次。
React 示例源码
import React, { useRef, useState, useEffect } from 'react'
export default function AIStreamPanel() {
const [content, setContent] = useState('')
const bufferRef = useRef('')
const timerRef = useRef(null)
function appendText(text) {
bufferRef.current += text
if (!timerRef.current) {
timerRef.current = setTimeout(() => {
setContent(prev => prev + bufferRef.current)
bufferRef.current = ''
timerRef.current = null
}, 80)
}
}
async function startSummary() {
setContent('')
await requestSummary('请总结当前网页', appendText)
}
useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current)
}
}
}, [])
return (
{content}
)
}
这样可以把几十次甚至几百次状态更新合并成较少的渲染更新,大幅减少卡顿。
九、请求缓存优化
对于网页总结、翻译、解释类任务,同一个页面经常会被重复请求。
可以用缓存减少接口调用。
1. 生成缓存 key
import crypto from 'crypto'
function createCacheKey({ url, task, content }) {
const hash = crypto
.createHash('sha256')
.update(url + task + content.slice(0, 5000))
.digest('hex')
return `ai:${task}:${hash}`
}
2. 简单内存缓存
class LRUCache {
constructor(max = 100) {
this.max = max
this.cache = new Map()
}
get(key) {
if (!this.cache.has(key)) return null
const value = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, value)
return value
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key)
}
this.cache.set(key, {
value,
time: Date.now()
})
if (this.cache.size > this.max) {
const firstKey = this.cache.keys().next().value
this.cache.delete(firstKey)
}
}
}
const aiCache = new LRUCache(200)
3. 使用缓存
async function summarizePage(page) {
const prompt = buildSummaryPrompt(page)
const key = createCacheKey({
url: page.url,
task: 'summary',
content: page.content
})
const cached = aiCache.get(key)
if (cached) {
return {
fromCache: true,
result: cached.value
}
}
const result = await callAI(prompt)
aiCache.set(key, result)
return {
fromCache: false,
result
}
}
需要注意的是,流式接口不太适合直接返回完整缓存。实际开发中可以这样处理:
- 如果有缓存,直接展示完整结果;
- 如果无缓存,使用流式生成;
- 流式结束后把完整结果写入缓存。
十、并发控制与请求取消
AI 浏览器中用户可能连续点击“总结”“翻译”“解释”。
如果不处理,会产生多个无意义请求。
1. 前端 AbortController
let currentController = null
async function requestAI(prompt, onText) {
if (currentController) {
currentController.abort()
}
currentController = new AbortController()
try {
const response = await fetch('/api/ai', {
method: 'POST',
signal: currentController.signal,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt })
})
const reader = response.body.getReader()
const decoder = new TextDecoder()
while (true) {
const { value, done } = await reader.read()
if (done) break
onText(decoder.decode(value))
}
} catch (err) {
if (err.name === 'AbortError') {
console.log('AI 请求已取消')
} else {
throw err
}
}
}
2. 服务端并发队列
class TaskQueue {
constructor(limit = 3) {
this.limit = limit
this.running = 0
this.queue = []
}
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({
task,
resolve,
reject
})
this.next()
})
}
next() {
if (this.running >= this.limit) return
const item = this.queue.shift()
if (!item) return
this.running++
item.task()
.then(item.resolve)
.catch(item.reject)
.finally(() => {
this.running--
this.next()
})
}
}
const aiQueue = new TaskQueue(5)
使用方式:
app.post('/api/ai/summary', async (req, res) => {
await aiQueue.add(() => streamAIResponse({
prompt: req.body.prompt,
res
}))
})
并发控制可以避免:
- 服务端瞬间打满;
- API 额度快速消耗;
- 多个请求互相抢占资源;
- AI 浏览器整体响应变慢。
十一、模型路由优化
并不是所有任务都需要使用最强模型。
AI 浏览器常见任务可以按复杂度分层:
| 任务类型 | 推荐模型策略 |
|---|---|
| 网页标题生成 | 小模型 |
| 网页摘要 | 中小模型 |
| 翻译 | 中小模型 |
| 代码解释 | 中等模型 |
| 多网页对比 | 大模型 |
| 长文档分析 | 大模型 + RAG |
| 本地命令理解 | 小模型或规则引擎 |
示例代码:
function selectModel(task, contentLength) {
if (task === 'title') {
return 'gpt-4o-mini'
}
if (task === 'translate') {
return contentLength > 10000
? 'gpt-4o'
: 'gpt-4o-mini'
}
if (task === 'summary') {
return contentLength > 20000
? 'gpt-4o'
: 'gpt-4o-mini'
}
if (task === 'compare') {
return 'gpt-4o'
}
return 'gpt-4o-mini'
}
模型路由的核心目标是:
- 简单任务用便宜快模型;
- 复杂任务用强模型;
- 控制响应速度;
- 控制成本;
- 避免“大炮打蚊子”。
十二、Web Worker 优化文本处理
网页正文清洗、分词、摘要预处理、向量切片等任务,如果放在主线程执行,可能导致界面卡顿。
可以使用 Web Worker。
worker.js
self.onmessage = function (event) {
const { type, payload } = event.data
if (type === 'COMPRESS_TEXT') {
const result = compressText(payload.text, payload.maxLength)
self.postMessage({
type: 'COMPRESS_TEXT_DONE',
payload: result
})
}
}
function compressText(text, maxLength = 8000) {
if (!text) return ''
const cleanText = text
.replace(/\s+/g, ' ')
.trim()
if (cleanText.length <= maxLength) {
return cleanText
}
return cleanText.slice(0, maxLength)
}
主线程使用
function compressTextInWorker(text) {
return new Promise((resolve, reject) => {
const worker = new Worker('/worker.js')
worker.postMessage({
type: 'COMPRESS_TEXT',
payload: {
text,
maxLength: 8000
}
})
worker.onmessage = event => {
if (event.data.type === 'COMPRESS_TEXT_DONE') {
resolve(event.data.payload)
worker.terminate()
}
}
worker.onerror = err => {
reject(err)
worker.terminate()
}
})
}
对于高频任务,建议复用 Worker,而不是每次都创建新 Worker。
十三、标签页生命周期优化
AI 浏览器通常支持多个标签页。
每个标签页都可能注入内容脚本、AI 上下文、页面监听器。
如果没有生命周期管理,内存会持续上涨。
可以设计一个 TabManager。
class TabManager {
constructor() {
this.tabs = new Map()
}
createTab(id, webview) {
this.tabs.set(id, {
id,
webview,
aiContext: null,
lastActiveTime: Date.now(),
status: 'active'
})
}
activateTab(id) {
for (const tab of this.tabs.values()) {
tab.status = 'background'
}
const tab = this.tabs.get(id)
if (tab) {
tab.status = 'active'
tab.lastActiveTime = Date.now()
}
}
closeTab(id) {
const tab = this.tabs.get(id)
if (!tab) return
if (tab.webview) {
tab.webview.remove()
}
tab.aiContext = null
this.tabs.delete(id)
}
cleanupInactiveTabs(maxIdleTime = 30 * 60 * 1000) {
const now = Date.now()
for (const [id, tab] of this.tabs.entries()) {
if (
tab.status === 'background' &&
now - tab.lastActiveTime > maxIdleTime
) {
tab.aiContext = null
}
}
}
}
优化原则:
- 当前标签页保留完整上下文;
- 后台标签页降低更新频率;
- 长时间未使用标签页释放 AI 上下文;
- 关闭标签页时释放 DOM、事件、请求;
- 避免每个标签页都持有大文本。
十四、RAG 检索优化
如果 AI 浏览器支持“问当前网页”或“问多个网页”,就会用到 RAG。
基本流程:
- 提取网页正文;
- 文本切片;
- 生成向量;
- 存储向量;
- 用户提问时检索相关片段;
- 把相关片段交给大模型回答。
性能优化重点在切片和检索。
文本切片源码
function splitText(text, chunkSize = 800, overlap = 100) {
const chunks = []
let start = 0
while (start < text.length) {
const end = Math.min(start + chunkSize, text.length)
const chunk = text.slice(start, end)
chunks.push({
text: chunk,
start,
end
})
start += chunkSize - overlap
}
return chunks
}
简单关键词检索示例
如果暂时不接入向量数据库,也可以先用关键词评分实现轻量检索。
function keywordSearch(query, chunks, topK = 5) {
const words = query
.toLowerCase()
.split(/\s+/)
.filter(Boolean)
return chunks
.map(chunk => {
const text = chunk.text.toLowerCase()
let score = 0
for (const word of words) {
if (text.includes(word)) {
score += 1
}
}
return {
...chunk,
score
}
})
.filter(item => item.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, topK)
}
构建问答 Prompt
function buildQAPrompt(question, chunks) {
const context = chunks
.map((item, index) => `片段 ${index + 1}:\n${item.text}`)
.join('\n\n')
return `
你是一个网页问答助手。请只根据提供的网页片段回答问题。
要求:
1. 如果片段中没有答案,请回答“当前网页内容中没有找到明确答案”;
2. 不要编造信息;
3. 回答要简洁;
4. 必要时引用片段中的关键句。
网页片段:
${context}
用户问题:
${question}
请给出答案:
`
}
RAG 的好处是不用每次把整篇网页都传给模型,可以显著降低 token 消耗和响应延迟。
十五、接口完整示例源码
下面给出一个简化版 AI 浏览器后端接口,包括总结、缓存、模型选择和流式响应。
import express from 'express'
import crypto from 'crypto'
import OpenAI from 'openai'
const app = express()
app.use(express.json({ limit: '2mb' }))
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
class LRUCache {
constructor(max = 100) {
this.max = max
this.cache = new Map()
}
get(key) {
if (!this.cache.has(key)) return null
const value = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, value)
return value
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key)
}
this.cache.set(key, value)
if (this.cache.size > this.max) {
const firstKey = this.cache.keys().next().value
this.cache.delete(firstKey)
}
}
}
const cache = new LRUCache(200)
function createCacheKey(page, task) {
return crypto
.createHash('sha256')
.update(task + page.url + page.content.slice(0, 5000))
.digest('hex')
}
function compressText(text, maxLength = 8000) {
if (!text) return ''
const cleanText = text
.replace(/\s+/g, ' ')
.trim()
if (cleanText.length <= maxLength) return cleanText
return cleanText.slice(0, maxLength)
}
function buildPrompt(page) {
return `
请总结以下网页内容。
要求:
1. 输出一句话总结;
2. 输出 5 个核心要点;
3. 输出重要结论;
4. 不要编造原文没有的信息。
标题:${page.title}
地址:${page.url}
正文:
${compressText(page.content, 8000)}
`
}
function selectModel(contentLength) {
return contentLength > 20000 ? 'gpt-4o' : 'gpt-4o-mini'
}
app.post('/api/summary', async (req, res) => {
const page = req.body.page
if (!page || !page.content) {
res.status(400).json({
error: '缺少 page.content'
})
return
}
const cacheKey = createCacheKey(page, 'summary')
const cached = cache.get(cacheKey)
if (cached) {
res.json({
fromCache: true,
result: cached
})
return
}
const prompt = buildPrompt(page)
const model = selectModel(page.content.length)
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
let fullText = ''
try {
const stream = await client.chat.completions.create({
model,
messages: [
{
role: 'user',
content: prompt
}
],
temperature: 0.3,
stream: true
})
for await (const chunk of stream) {
const text = chunk.choices?.[0]?.delta?.content || ''
if (text) {
fullText += text
res.write(`data: ${JSON.stringify({ text })}\n\n`)
}
}
cache.set(cacheKey, fullText)
res.write(`data: ${JSON.stringify({ done: true })}\n\n`)
res.end()
} catch (err) {
res.write(`data: ${JSON.stringify({
error: err.message || 'AI 请求失败'
})}\n\n`)
res.end()
}
})
app.listen(3000, () => {
console.log('AI Browser Server running at http://localhost:3000')
})
十六、性能监控指标
优化不能只靠感觉,必须有指标。
AI 浏览器建议重点监控以下指标:
| 指标 | 含义 |
|---|---|
| App Startup Time | 应用启动时间 |
| First Paint | 首屏绘制时间 |
| Webview Load Time | 网页加载时间 |
| AI First Token Time | AI 首 token 时间 |
| AI Total Time | AI 完整响应时间 |
| Memory Usage | 内存占用 |
| CPU Usage | CPU 使用率 |
| Cache Hit Rate | 缓存命中率 |
| Token Usage | Token 消耗 |
| Error Rate | AI 请求失败率 |
前端可以简单记录:
const perf = {
start: performance.now()
}
function mark(name) {
perf[name] = performance.now()
}
function report() {
console.table({
firstToken: perf.firstToken - perf.start,
finished: perf.finished - perf.start
})
}
在 AI 流式请求中:
let hasFirstToken = false
requestAI(prompt, text => {
if (!hasFirstToken) {
hasFirstToken = true
mark('firstToken')
}
appendText(text)
}).then(() => {
mark('finished')
report()
})
十七、推荐优化清单
最后给出一份可直接用于项目排查的优化清单。
启动优化
- [ ] AI SDK 是否延迟加载;
- [ ] 数据库是否懒初始化;
- [ ] 首屏是否加载过多组件;
- [ ] 是否启用代码分割;
- [ ] 是否避免启动时读取全部历史记录。
网页解析优化
- [ ] 是否过滤广告、导航、评论;
- [ ] 是否限制最大正文长度;
- [ ] 是否对 DOM 变化做防抖;
- [ ] 是否在页面卸载时释放监听;
- [ ] 是否避免频繁跨进程传输大文本。
AI 请求优化
- [ ] 是否使用流式响应;
- [ ] 是否有 Prompt 压缩;
- [ ] 是否有缓存;
- [ ] 是否支持取消请求;
- [ ] 是否有超时机制;
- [ ] 是否做模型路由。
渲染优化
- [ ] 流式输出是否批量更新;
- [ ] 长对话是否使用虚拟列表;
- [ ] Markdown 渲染是否做缓存;
- [ ] 大文本处理是否放到 Worker;
- [ ] 是否避免无意义的全局状态更新。
内存优化
- [ ] 标签页关闭是否释放资源;
- [ ] WebSocket 是否关闭;
- [ ] MutationObserver 是否断开;
- [ ] 缓存是否设置上限;
- [ ] 后台标签页是否降低活跃度。
十八、总结
AI 浏览器的性能优化,本质上是浏览器工程、前端工程、后端服务和大模型工程的综合优化。
不要把性能问题简单归因于“大模型慢”。
在实际项目中,很多卡顿和延迟来自:
- 启动阶段加载过多模块;
- 网页正文提取过于粗暴;
- Prompt 未压缩;
- 流式响应没有合理渲染;
- 请求没有取消;
- 缓存没有上限;
- 标签页生命周期管理缺失;
- 多任务并发失控。
一款体验优秀的 AI 浏览器,应该做到:
- 启动快:AI 能力按需加载;
- 解析准:只提取有价值内容;
- 响应快:流式输出,尽快返回首 token;
- 成本低:缓存、压缩、模型路由;
- 不卡顿:批量渲染、Worker 处理重任务;
- 可持续运行:控制内存、释放资源、管理标签页生命周期。
如果你正在开发 AI 浏览器,建议优先落地以下五个优化:
- 网页正文清洗与长度限制;
- AI 流式响应;
- 流式渲染批量更新;
- LRU 缓存;
- AbortController 请求取消。
这五项优化投入不高,但对用户体验提升非常明显。
后续再逐步引入 RAG、向量检索、模型路由、标签页资源回收和性能监控系统,就可以构建一款真正稳定、快速、低成本的 AI 浏览器。