上一篇 下一篇 分享链接 返回 返回顶部

做 AI 浏览器别只会接大模型:这套降本方案和代码能省不少钱

发布人:慈云数据-客服中心 发布时间:11小时前 阅读量:0

AI浏览器 如何降低成本|附源码

当“AI能力”开始进入浏览器,很多产品的第一反应是:接入一个大模型 API,做一个侧边栏助手、网页总结、划词解释、自动写作。
但上线后很快会遇到一个现实问题:AI浏览器的成本并不低
用户每打开一个网页、每点击一次总结、每进行一次对话,背后都可能产生 Token 消耗、接口调用费用、向量检索费用、服务器带宽费用,甚至还有模型并发带来的基础设施成本。

本文将系统讲解:AI浏览器如何降低成本,并给出一个可运行的简化版源码示例,帮助你理解如何通过缓存、内容裁剪、模型分级、流式输出、请求合并等方式,把 AI 浏览器的调用成本降下来。


一、AI浏览器为什么成本高?

AI浏览器看起来只是“浏览器 + AI助手”,但实际成本来源非常复杂。

常见的 AI 浏览器功能包括:

  1. 网页内容总结
  2. 网页问答
  3. 划词翻译
  4. 划词解释
  5. 自动生成回复
  6. 表单填写辅助
  7. 多标签页内容分析
  8. 长文档阅读
  9. 网页内容结构化提取
  10. 基于浏览记录的个性化助手

这些功能背后往往都要调用大模型。

大模型计费通常基于:

  • 输入 Token 数
  • 输出 Token 数
  • 模型类型
  • 请求次数
  • 并发量
  • 上下文长度
  • 是否使用向量数据库
  • 是否启用搜索或工具调用

其中,网页内容总结网页问答尤其容易造成高成本。

因为网页正文往往很长,一篇文章可能有几千到几万字,如果每次都把完整页面内容发送给大模型,成本会非常高。

例如,一个网页有 15000 个中文字符,粗略换算可能接近 8000~12000 tokens。
如果用户每天总结 100 个页面,那么输入 token 就可能达到百万级别。
如果用户量上升到 1 万,每日调用成本会迅速放大。

所以 AI 浏览器的核心问题不是“能不能接入大模型”,而是:

如何在保证体验的前提下,尽可能减少不必要的大模型调用。


二、AI浏览器降本的核心思路

AI浏览器降本可以从以下几个方向入手:

方向 核心目标
内容裁剪 少传无用内容
缓存复用 相同内容不重复调用
模型分级 简单任务用便宜模型
请求合并 减少频繁调用
本地预处理 浏览器端先做清洗
向量检索 只把相关片段发给模型
流式输出 降低等待感,减少重复请求
用户行为控制 避免自动无限触发
Prompt 优化 减少输入输出 token
结果复用 摘要、翻译、问答可共享上下文

下面分别展开。


三、策略一:网页正文提取,避免传整页 HTML

很多初级实现会直接把网页的 document.body.innerText 发送给模型。
这样虽然简单,但会把大量无关内容也传进去,例如:

  • 顶部导航
  • 底部版权
  • 推荐文章
  • 评论区
  • 广告文本
  • 侧边栏
  • 菜单项
  • 重复按钮文字

这会显著增加 token 成本。

更合理的方式是:

  1. 在浏览器端提取正文
  2. 删除脚本、样式、导航、页脚
  3. 保留标题、正文、发布时间、作者等关键信息
  4. 对内容做长度限制
  5. 对重复段落去重

例如:

function extractMainText() {
  const clone = document.body.cloneNode(true);

  const removeSelectors = [
    'script',
    'style',
    'nav',
    'footer',
    'header',
    'aside',
    'iframe',
    'noscript',
    '.advertisement',
    '.ads',
    '.comment',
    '.comments',
    '.recommend',
    '.related'
  ];

  removeSelectors.forEach(selector => {
    clone.querySelectorAll(selector).forEach(el => el.remove());
  });

  let text = clone.innerText || '';
  text = text
    .replace(/\n{3,}/g, '\n\n')
    .replace(/[ \t]{2,}/g, ' ')
    .trim();

  return text;
}

这一步通常可以减少 30%~70% 的输入内容。

如果再结合正文识别算法,效果更明显。


四、策略二:内容指纹 + 缓存,避免重复总结

很多网页的内容是固定的。
如果多个用户访问同一个页面,或者同一个用户多次点击“总结”,其实没有必要反复调用大模型。

可以为网页内容生成一个 hash,作为缓存 key。

缓存维度可以包括:

  • 页面 URL
  • 页面标题
  • 正文 hash
  • 用户语言
  • 任务类型
  • Prompt 版本
  • 模型版本

例如:

summary:v1:gpt-4o-mini:zh:contentHash

这样只要正文没有变化,就可以复用之前生成的摘要。

缓存可以放在:

  • 浏览器本地 localStorage
  • IndexedDB
  • 后端 Redis
  • 数据库
  • CDN KV 存储

建议采用多级缓存:

浏览器本地缓存 → 后端 Redis 缓存 → 数据库缓存 → 大模型调用

命中缓存后,成本几乎为零。


五、策略三:模型分级,不要所有任务都用最贵模型

AI浏览器里的任务难度并不一样。

例如:

功能 推荐模型
划词翻译 小模型
词语解释 小模型
网页摘要 中等模型
复杂问答 中高模型
多页面分析 高级模型
代码理解 中高模型
法律/医疗类严肃推理 更强模型 + 风控

很多场景没有必要调用最强模型。

比如“把这段英文翻译成中文”,使用便宜模型即可。
“总结这篇新闻文章”,也可以先用轻量模型。
只有在用户继续追问,或者涉及复杂推理时,才升级到更强模型。

这叫做:

模型路由,Model Routing。

示例逻辑:

function selectModel(taskType, textLength) {
  if (taskType === 'translate') {
    return 'cheap-model';
  }

  if (taskType === 'explain') {
    return 'cheap-model';
  }

  if (taskType === 'summary' && textLength < 6000) {
    return 'cheap-model';
  }

  if (taskType === 'summary' && textLength >= 6000) {
    return 'middle-model';
  }

  if (taskType === 'deep_qa') {
    return 'strong-model';
  }

  return 'cheap-model';
}

模型分级是降低成本最直接的方式之一。


六、策略四:长网页先分块,再压缩

如果一个网页特别长,不建议直接把全文扔给模型。

更合理的方式是:

  1. 将网页切分成多个片段
  2. 对每个片段做局部摘要
  3. 再对局部摘要做总摘要

这叫做 Map-Reduce Summarization。

示例:

原始长文
  ↓
分块 chunk1, chunk2, chunk3...
  ↓
分别摘要 summary1, summary2, summary3...
  ↓
合并摘要 final summary

虽然看起来调用次数变多,但每次上下文更短,可控性更强。
如果使用便宜模型做第一阶段摘要,再用中等模型合并,整体成本往往比一次性调用长上下文模型更低。

此外,对于问答场景,更推荐使用检索式流程:

  1. 用户提出问题
  2. 将网页切块
  3. 计算问题和片段的相似度
  4. 只取最相关的 3~5 个片段
  5. 发给模型回答

这样不需要每次都发送完整网页。


七、策略五:Prompt 要短,不要写成小作文

很多 AI 应用的 Prompt 非常长:

你是一个非常专业、非常严谨、非常有耐心、非常擅长分析网页内容的人工智能助手……

这类 Prompt 如果每次请求都发送,会带来额外 token 成本。

AI浏览器的 Prompt 应该尽量短,但约束清晰。

例如网页总结:

请用中文总结以下网页内容,输出:
1. 一句话总结
2. 三个要点
3. 适合谁阅读

相比冗长 Prompt,这已经足够。

输出格式也要限制,否则模型可能生成过长内容。

例如:

每个要点不超过 40 字,总字数不超过 300 字。

控制输出长度同样重要,因为输出 token 也收费。


八、策略六:前端不要自动疯狂调用

AI浏览器常见的成本失控原因是:
页面打开后自动总结、自动分析、自动生成推荐问题。

这对用户体验不一定有帮助,却会造成大量无效调用。

更合理的交互方式是:

  • 默认不自动调用
  • 用户点击按钮后再总结
  • 对同一页面设置调用冷却时间
  • 页面内容变化后延迟触发
  • 滚动阅读到一定深度后再推荐总结
  • 免费用户限制每日调用次数
  • 对低价值页面不启用 AI

例如:

let lastCallTime = 0;

function canCallAI() {
  const now = Date.now();
  const cooldown = 10 * 1000;

  if (now - lastCallTime < cooldown) {
    return false;
  }

  lastCallTime = now;
  return true;
}

产品层面的控制,往往比技术优化更重要。


九、简化版 AI浏览器插件架构

下面给出一个简化架构:

Chrome Extension
├── manifest.json
├── content.js          // 提取网页内容
├── popup.html          // 插件界面
├── popup.js            // 用户交互
└── background.js       // 请求后端

Backend
├── server.js           // Express API
├── cache.js            // 简单内存缓存
└── ai.js               // 调用大模型

实际生产环境中,后端还应增加:

  • 用户鉴权
  • 用量统计
  • Redis 缓存
  • 日志系统
  • 限流系统
  • 计费系统
  • 风控策略
  • Prompt 版本管理

十、前端源码:Chrome 插件

下面是一个简化版插件源码,实现:

  • 点击按钮提取当前网页正文
  • 生成内容 hash
  • 请求后端总结
  • 显示摘要结果

1. manifest.json

{
  "manifest_version": 3,
  "name": "Cost Saving AI Browser Demo",
  "version": "1.0.0",
  "description": "一个演示如何降低AI浏览器成本的浏览器插件",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "http://localhost:3000/*"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_title": "AI网页总结"
  }
}

2. popup.html




  
  AI网页总结
  


  
  
优先使用缓存,减少重复调用大模型。

3. popup.js

const btn = document.getElementById('summaryBtn');
const result = document.getElementById('result');

btn.addEventListener('click', async () => {
  btn.disabled = true;
  result.textContent = '正在提取网页内容...';

  try {
    const [tab] = await chrome.tabs.query({
      active: true,
      currentWindow: true
    });

    const [{ result: pageData }] = await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: extractPageContent
    });

    if (!pageData.text || pageData.text.length < 100) {
      result.textContent = '当前页面可总结内容太少。';
      btn.disabled = false;
      return;
    }

    result.textContent = '正在请求AI总结...';

    const response = await fetch('http://localhost:3000/api/summary', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        url: pageData.url,
        title: pageData.title,
        text: pageData.text,
        lang: 'zh'
      })
    });

    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.message || '请求失败');
    }

    result.textContent = data.cached
      ? `[缓存命中]\n\n${data.summary}`
      : `[AI生成]\n\n${data.summary}`;
  } catch (err) {
    result.textContent = `出错了:${err.message}`;
  } finally {
    btn.disabled = false;
  }
});

function extractPageContent() {
  const clone = document.body.cloneNode(true);

  const removeSelectors = [
    'script',
    'style',
    'nav',
    'footer',
    'header',
    'aside',
    'iframe',
    'noscript',
    'svg',
    'canvas',
    '.advertisement',
    '.ads',
    '.comment',
    '.comments',
    '.recommend',
    '.related',
    '[role="navigation"]',
    '[role="banner"]',
    '[role="contentinfo"]'
  ];

  removeSelectors.forEach(selector => {
    clone.querySelectorAll(selector).forEach(el => el.remove());
  });

  let text = clone.innerText || '';

  text = text
    .split('\n')
    .map(line => line.trim())
    .filter(line => line.length > 0)
    .filter((line, index, arr) => arr.indexOf(line) === index)
    .join('\n');

  // 控制最大长度,避免把超长网页完整发送到后端
  const maxChars = 12000;

  if (text.length > maxChars) {
    text = text.slice(0, maxChars);
  }

  return {
    url: location.href,
    title: document.title,
    text
  };
}

十一、后端源码:Node.js + Express

下面是一个简化版后端,实现:

  • 内容 hash
  • 内存缓存
  • 文本裁剪
  • 模型选择
  • Prompt 控制
  • 调用大模型接口

为了便于演示,这里使用 OpenAI 风格接口。你可以替换成其他模型服务商。


1. 初始化项目

mkdir ai-browser-cost-demo
cd ai-browser-cost-demo

npm init -y

npm install express cors crypto-js dotenv

2. .env

OPENAI_API_KEY=你的_API_KEY
OPENAI_BASE_URL=https://api.openai.com/v1
PORT=3000

3. server.js

require('dotenv').config();

const express = require('express');
const cors = require('cors');
const CryptoJS = require('crypto-js');

const app = express();

app.use(cors());
app.use(express.json({ limit: '2mb' }));

const PORT = process.env.PORT || 3000;

// 简单内存缓存,生产环境建议换成 Redis
const summaryCache = new Map();

// 简单限流,生产环境建议按用户、IP、设备ID做更细粒度控制
const rateLimitMap = new Map();

function checkRateLimit(ip) {
  const now = Date.now();
  const windowMs = 60 * 1000;
  const maxRequests = 20;

  const record = rateLimitMap.get(ip) || {
    count: 0,
    startTime: now
  };

  if (now - record.startTime > windowMs) {
    record.count = 1;
    record.startTime = now;
    rateLimitMap.set(ip, record);
    return true;
  }

  if (record.count >= maxRequests) {
    return false;
  }

  record.count++;
  rateLimitMap.set(ip, record);
  return true;
}

function normalizeText(text) {
  return String(text || '')
    .replace(/\s+/g, ' ')
    .trim();
}

function createContentHash({ title, text, lang }) {
  const normalized = normalizeText(`${title}\n${text}\n${lang}`);
  return CryptoJS.SHA256(normalized).toString();
}

function trimText(text, maxChars = 10000) {
  if (!text) return '';

  if (text.length <= maxChars) {
    return text;
  }

  // 保留开头和结尾,避免只截取开头导致遗漏结论
  const head = text.slice(0, Math.floor(maxChars * 0.7));
  const tail = text.slice(-Math.floor(maxChars * 0.3));

  return `${head}\n\n……中间内容已省略……\n\n${tail}`;
}

function selectModel(taskType, textLength) {
  // 这里使用示例模型名,实际请替换成你的模型
  if (taskType === 'summary' && textLength < 5000) {
    return 'gpt-4o-mini';
  }

  if (taskType === 'summary' && textLength >= 5000) {
    return 'gpt-4o-mini';
  }

  return 'gpt-4o-mini';
}

function buildSummaryPrompt({ title, text }) {
  return [
    {
      role: 'system',
      content: '你是一个简洁、准确的网页阅读助手。'
    },
    {
      role: 'user',
      content: `请用中文总结以下网页内容。

要求:
1. 输出“一句话总结”
2. 输出“核心要点”,最多5条
3. 输出“适合人群”
4. 总字数不超过400字
5. 不要编造原文不存在的信息

网页标题:
${title}

网页正文:
${text}`
    }
  ];
}

async function callOpenAI({ model, messages }) {
  const baseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';

  const response = await fetch(`${baseUrl}/chat/completions`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model,
      messages,
      temperature: 0.2,
      max_tokens: 600
    })
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`AI接口调用失败:${text}`);
  }

  const data = await response.json();

  return data.choices?.[0]?.message?.content || '';
}

app.post('/api/summary', async (req, res) => {
  try {
    const ip = req.ip;

    if (!checkRateLimit(ip)) {
      return res.status(429).json({
        message: '请求过于频繁,请稍后再试'
      });
    }

    const { url, title, text, lang = 'zh' } = req.body;

    if (!text || text.length < 100) {
      return res.status(400).json({
        message: '网页正文内容太少'
      });
    }

    const cleanedText = normalizeText(text);
    const contentHash = createContentHash({
      title,
      text: cleanedText,
      lang
    });

    const promptVersion = 'summary_v1';
    const model = selectModel('summary', cleanedText.length);

    const cacheKey = [
      promptVersion,
      model,
      lang,
      contentHash
    ].join(':');

    if (summaryCache.has(cacheKey)) {
      return res.json({
        cached: true,
        summary: summaryCache.get(cacheKey)
      });
    }

    const finalText = trimText(cleanedText, 10000);

    const messages = buildSummaryPrompt({
      title: title || url || '未命名网页',
      text: finalText
    });

    const summary = await callOpenAI({
      model,
      messages
    });

    summaryCache.set(cacheKey, summary);

    return res.json({
      cached: false,
      summary
    });
  } catch (err) {
    console.error(err);

    return res.status(500).json({
      message: err.message || '服务器错误'
    });
  }
});

app.listen(PORT, () => {
  console.log(`AI Browser Cost Demo Server running on http://localhost:${PORT}`);
});

4. 启动后端

node server.js

然后在 Chrome 浏览器中加载插件:

  1. 打开 chrome://extensions/
  2. 开启“开发者模式”
  3. 点击“加载已解压的扩展程序”
  4. 选择插件目录
  5. 打开任意文章页面
  6. 点击插件按钮“总结当前网页”

十二、这份源码里做了哪些降本设计?

虽然这是一个 Demo,但已经包含了几个关键降本点。

1. 前端正文清洗

插件没有直接传完整 HTML,而是去掉了:

  • script
  • style
  • nav
  • footer
  • header
  • aside
  • iframe
  • 广告区域
  • 评论区域
  • 推荐区域

这可以减少大量无效 token。


2. 前端最大长度限制

const maxChars = 12000;

前端限制最多传 12000 字符,防止超长页面直接打爆成本。

生产环境中还可以根据用户等级调整:

用户类型 最大字符数
免费用户 5000
普通会员 12000
高级会员 30000

3. 后端二次裁剪

const finalText = trimText(cleanedText, 10000);

即使前端被篡改,后端仍然会做二次限制。
这是非常重要的安全和成本控制措施。


4. 内容 hash 缓存

const contentHash = createContentHash({
  title,
  text: cleanedText,
  lang
});

相同页面、相同内容、相同语言、相同模型、相同 Prompt 版本会复用缓存。

这样当用户重复总结同一页面时,不会再次调用大模型。


5. Prompt 版本纳入缓存 Key

const promptVersion = 'summary_v1';

这很关键。

如果你修改了 Prompt,但缓存 key 没变,就可能返回旧格式内容。
所以缓存 key 应该包含 Prompt 版本。


6. 输出长度限制

max_tokens: 600

模型输出也会产生费用。
限制输出长度可以避免模型生成过长内容。


7. 简单限流

const maxRequests = 20;

防止短时间内大量请求。
生产环境应该按用户 ID、IP、设备指纹、套餐等级等维度做限流。


十三、进一步优化:Redis 缓存

Demo 使用内存缓存:

const summaryCache = new Map();

缺点是:

  • 服务重启后缓存丢失
  • 多实例之间无法共享
  • 内存不可控

生产环境推荐使用 Redis。

缓存结构可以设计为:

ai:summary:v1:gpt-4o-mini:zh:{hash}

并设置过期时间:

TTL = 7天 / 30天 / 永久

对于公开网页摘要,可以缓存更久。
对于用户私有页面,应谨慎缓存,或只做用户级缓存。


十四、进一步优化:问答场景使用切块检索

网页问答比网页摘要更容易浪费 token。

错误做法:

每次用户提问,都把整篇网页发送给模型。

正确做法:

网页切块 → 用户问题匹配相关片段 → 只发送相关片段

一个简单的非向量版本可以用关键词匹配:

function splitText(text, chunkSize = 800) {
  const chunks = [];

  for (let i = 0; i < text.length; i += chunkSize) {
    chunks.push(text.slice(i, i + chunkSize));
  }

  return chunks;
}

function simpleSearch(question, chunks, topK = 3) {
  const keywords = question
    .toLowerCase()
    .split(/\s+/)
    .filter(Boolean);

  return chunks
    .map(chunk => {
      const lower = chunk.toLowerCase();
      const score = keywords.reduce((sum, kw) => {
        return sum + (lower.includes(kw) ? 1 : 0);
      }, 0);

      return {
        chunk,
        score
      };
    })
    .sort((a, b) => b.score - a.score)
    .slice(0, topK)
    .map(item => item.chunk);
}

如果要效果更好,可以使用 embedding 模型和向量数据库。
但要注意,向量化本身也有成本。对于短网页,关键词检索可能已经够用。


十五、进一步优化:本地小模型

随着 WebGPU、WASM、本地推理技术发展,一些轻量任务可以放在本地完成。

适合本地处理的任务包括:

  • 简单文本清洗
  • 语言识别
  • 页面分类
  • 敏感信息检测
  • 短文本翻译
  • 关键词提取
  • 阅读时间估计
  • 是否值得总结的判断

本地模型虽然会增加客户端资源消耗,但可以减少服务端调用成本。

不过要注意:

  • 浏览器兼容性
  • 模型体积
  • 首次加载速度
  • 用户设备性能差异
  • 隐私与权限说明

对于商业产品,通常可以采用混合方案:

本地规则 / 小模型 → 后端轻量模型 → 后端强模型

十六、成本估算方法

要真正降低成本,必须建立成本监控。

至少要记录:

  • 用户 ID
  • 请求类型
  • 输入字符数
  • 估算输入 tokens
  • 输出 tokens
  • 模型名称
  • 是否命中缓存
  • 请求耗时
  • 错误率
  • 页面 URL hash
  • Prompt 版本

可以建立一个简单公式:

单次成本 = 输入 tokens × 输入单价 + 输出 tokens × 输出单价

然后按功能统计:

功能 请求量 缓存命中率 平均输入 tokens 平均输出 tokens 日成本
网页总结 10000 45% 3000 400 xxx
划词翻译 50000 20% 80 100 xxx
网页问答 20000 15% 1200 300 xxx

你会发现,真正烧钱的功能通常不是调用次数最多的,而是长上下文输入最多的功能


十七、推荐的生产级降本组合

如果你要做一个真正可上线的 AI 浏览器,推荐使用以下组合:

免费用户

  • 不自动总结
  • 每日次数限制
  • 最大输入 5000 字符
  • 只用便宜模型
  • 强缓存
  • 输出限制 300 字以内

付费用户

  • 最大输入 20000 字符
  • 可使用更强模型
  • 支持网页问答
  • 支持历史记录总结
  • Redis 缓存
  • 向量检索

高级用户

  • 多标签页分析
  • 长文档处理
  • 高级模型路由
  • 私有知识库
  • 更高限流额度
  • 更长上下文

十八、总结

AI浏览器降低成本的关键,不是简单地换一个更便宜的模型,而是建立完整的成本控制体系。

核心原则可以总结为:

  1. 能不调用模型,就不调用模型
  2. 能用缓存,就用缓存
  3. 能少传内容,就少传内容
  4. 能用小模型,就不用大模型
  5. 能本地处理,就不走云端
  6. 能限制输出,就不要放任生成
  7. 能按需触发,就不要自动触发
  8. 能检索片段,就不要发送全文

本文给出的源码只是一个最小可用 Demo,但它已经包含 AI 浏览器降本的基本思想:

  • 正文提取
  • 内容裁剪
  • 缓存复用
  • 模型选择
  • Prompt 控制
  • 限流保护

在真实业务中,只要继续加入 Redis、用户体系、用量统计、向量检索、模型路由和套餐策略,就可以逐步演化为一个成本可控的 AI 浏览器系统。

AI浏览器的竞争,不仅是模型能力的竞争,也是工程效率、成本控制和产品体验的竞争。谁能用更低的成本提供更稳定的体验,谁就更有机会做出长期可持续的 AI 产品。

目录结构
全文