浏览器插件开发实战:零基础构建DeepSeek智能写作助手

浏览器插件开发实战:零基础构建DeepSeek智能写作助手

浏览器插件开发实战:零基础构建DeepSeek智能写作助手_第1张图片

在人工智能技术爆发式发展的今天,DeepSeek作为国内领先的大语言模型平台,正改变着我们的工作方式。本文将深入解析如何通过浏览器插件技术,无缝集成DeepSeek API到日常浏览体验中,打造个人专属的AI写作助手。

一、浏览器插件架构设计

1.1 插件核心组件解析

浏览器插件由多个相互协作的组件构成,每个组件都有特定职责:

Manifest.json
后台脚本
内容脚本
弹出页面
API通信
网页交互
用户界面

组件功能说明

  • Manifest.json:插件配置文件,定义元数据和权限
  • 后台脚本:持久化运行,处理核心逻辑和API调用
  • 内容脚本:注入到网页中,与DOM交互
  • 弹出页面:用户交互界面,提供设置和功能入口

1.2 插件通信机制

不同组件间通过消息传递机制通信:

// 内容脚本发送消息到后台
chrome.runtime.sendMessage(
  { action: "processText", text: selectedText },
  response => console.log("Received:", response)
);

// 后台接收消息处理
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "processText") {
    callDeepSeekAPI(request.text).then(result => {
      sendResponse({ result });
    });
    return true; // 表示异步响应
  }
});

// 弹出页面与后台通信
document.getElementById("apiKeySave").addEventListener("click", () => {
  const apiKey = document.getElementById("apiKeyInput").value;
  chrome.storage.sync.set({ apiKey }, () => {
    console.log("API Key saved");
  });
});

二、DeepSeek API对接实战

2.1 API认证与调用

DeepSeek API使用API Key进行认证,需要安全的存储机制:

// 后台脚本中的API调用函数
async function callDeepSeekAPI(prompt) {
  // 从存储获取API Key
  const { apiKey } = await chrome.storage.sync.get("apiKey");
  
  try {
    const response = await fetch("https://api.deepseek.com/v1/chat/completions", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${apiKey}`
      },
      body: JSON.stringify({
        model: "deepseek-chat",
        messages: [
          { role: "system", content: "你是一个专业的写作助手" },
          { role: "user", content: prompt }
        ],
        temperature: 0.7,
        max_tokens: 2000
      })
    });

    if (!response.ok) throw new Error(`API Error: ${response.status}`);
    
    const data = await response.json();
    return data.choices[0].message.content;
  } catch (error) {
    console.error("API调用失败:", error);
    return "请求失败,请检查API Key和网络连接";
  }
}

2.2 流式响应处理

对于长文本生成,使用流式响应提升用户体验:

// 流式API响应处理
async function streamDeepSeekResponse(prompt, onData) {
  const { apiKey } = await chrome.storage.sync.get("apiKey");
  
  const response = await fetch("https://api.deepseek.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${apiKey}"
    },
    body: JSON.stringify({
      model: "deepseek-chat",
      messages: [{ role: "user", content: prompt }],
      stream: true
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    
    buffer += decoder.decode(value, { stream: true });
    
    // 处理服务器发送的事件流
    const parts = buffer.split("\n\n");
    buffer = parts.pop();
    
    for (const part of parts) {
      if (!part.startsWith("data: ")) continue;
      
      const data = part.replace("data: ", "");
      if (data === "[DONE]") break;
      
      try {
        const json = JSON.parse(data);
        const content = json.choices[0]?.delta?.content || "";
        onData(content);
      } catch (e) {
        console.error("解析错误:", e);
      }
    }
  }
}

三、用户界面设计与实现

3.1 弹出页面UI

弹出页面提供用户配置和功能入口:

DOCTYPE html>
<html>
<head>
  <title>DeepSeek助手title>
  <style>
    :root {
      --primary: #4f46e5;
      --secondary: #818cf8;
    }
    body {
      width: 400px;
      padding: 20px;
      font-family: 'Segoe UI', sans-serif;
    }
    .tab-container {
      display: flex;
      margin-bottom: 20px;
    }
    .tab {
      padding: 10px 20px;
      cursor: pointer;
      border-bottom: 2px solid transparent;
    }
    .tab.active {
      border-color: var(--primary);
      color: var(--primary);
    }
    .panel {
      display: none;
    }
    .panel.active {
      display: block;
    }
    input, textarea, button {
      width: 100%;
      padding: 10px;
      margin: 8px 0;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    button {
      background: var(--primary);
      color: white;
      border: none;
      cursor: pointer;
    }
    #responseArea {
      height: 200px;
      border: 1px solid #ddd;
      padding: 10px;
      overflow-y: auto;
      white-space: pre-wrap;
    }
  style>
head>
<body>
  <div class="tab-container">
    <div class="tab active" data-tab="settings">设置div>
    <div class="tab" data-tab="chat">聊天div>
    <div class="tab" data-tab="history">历史记录div>
  div>

  <div class="panel active" id="settingsPanel">
    <h3>API 设置h3>
    <input type="password" id="apiKeyInput" placeholder="输入DeepSeek API密钥">
    <button id="apiKeySave">保存密钥button>
    
    <h3>写作风格预设h3>
    <select id="stylePreset">
      <option value="professional">专业正式option>
      <option value="casual">轻松随意option>
      <option value="creative">创意写作option>
    select>
  div>

  <div class="panel" id="chatPanel">
    <textarea id="promptInput" placeholder="输入您的请求...">textarea>
    <button id="sendButton">发送到DeepSeekbutton>
    <div id="responseArea">div>
  div>

  <script src="popup.js">script>
body>
html>

3.2 内容脚本注入

内容脚本与网页DOM交互,实现文本选择和替换:

// 内容脚本 content.js
document.addEventListener('mouseup', function(event) {
  const selectedText = window.getSelection().toString().trim();
  if (selectedText.length > 2) {
    showFloatingButton(event.clientX, event.clientY, selectedText);
  }
});

function showFloatingButton(x, y, text) {
  // 移除已存在的按钮
  const existingBtn = document.getElementById('deepseek-float-btn');
  if (existingBtn) existingBtn.remove();
  
  const btn = document.createElement('button');
  btn.id = 'deepseek-float-btn';
  btn.innerHTML = '✨ DeepSeek';
  btn.style = `
    position: fixed;
    left: ${x}px;
    top: ${y}px;
    z-index: 9999;
    padding: 8px 16px;
    background: linear-gradient(135deg, #6e8efb, #a777e3);
    color: white;
    border: none;
    border-radius: 20px;
    cursor: pointer;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    font-size: 14px;
    transform: translate(-50%, -100%);
    transition: all 0.2s ease;
  `;
  
  btn.addEventListener('click', () => {
    chrome.runtime.sendMessage({
      action: 'processText',
      text: text
    }, response => {
      replaceSelectedText(response.result);
      btn.remove();
    });
  });
  
  document.body.appendChild(btn);
  
  // 5秒后自动消失
  setTimeout(() => {
    if (document.body.contains(btn)) {
      btn.style.opacity = '0';
      setTimeout(() => btn.remove(), 300);
    }
  }, 5000);
}

function replaceSelectedText(newText) {
  const selection = window.getSelection();
  if (!selection.rangeCount) return;
  
  const range = selection.getRangeAt(0);
  range.deleteContents();
  range.insertNode(document.createTextNode(newText));
}

四、安全与性能优化

4.1 API密钥安全存储

// 使用chrome.storage加密存储API密钥
async function saveApiKey(apiKey) {
  // 使用Web Crypto API进行基本加密
  const encoder = new TextEncoder();
  const data = encoder.encode(apiKey);
  const key = await crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
  
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    data
  );
  
  // 存储加密数据
  await chrome.storage.local.set({
    encryptedKey: Array.from(new Uint8Array(encrypted)),
    iv: Array.from(iv),
    cryptoKey: await crypto.subtle.exportKey("jwk", key)
  });
}

async function getApiKey() {
  const { encryptedKey, iv, cryptoKey } = await chrome.storage.local.get([
    "encryptedKey", "iv", "cryptoKey"
  ]);
  
  if (!encryptedKey) return null;
  
  const key = await crypto.subtle.importKey(
    "jwk",
    cryptoKey,
    { name: "AES-GCM" },
    true,
    ["decrypt"]
  );
  
  const decrypted = await crypto.subtle.decrypt(
    { name: "AES-GCM", iv: new Uint8Array(iv) },
    key,
    new Uint8Array(encryptedKey)
  );
  
  return new TextDecoder().decode(decrypted);
}

4.2 性能优化策略

// API调用缓存机制
const responseCache = new Map();

async function cachedApiCall(prompt, maxCacheAge = 60 * 60 * 1000) {
  const cacheKey = hashString(prompt);
  
  // 检查缓存
  if (responseCache.has(cacheKey)) {
    const { timestamp, response } = responseCache.get(cacheKey);
    if (Date.now() - timestamp < maxCacheAge) {
      return response;
    }
  }
  
  // 调用API
  const response = await callDeepSeekAPI(prompt);
  
  // 更新缓存
  responseCache.set(cacheKey, {
    timestamp: Date.now(),
    response
  });
  
  // 限制缓存大小
  if (responseCache.size > 100) {
    const oldestKey = [...responseCache.keys()][0];
    responseCache.delete(oldestKey);
  }
  
  return response;
}

function hashString(str) {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = ((hash << 5) - hash) + str.charCodeAt(i);
    hash |= 0; // 转换为32位整数
  }
  return hash.toString();
}

五、高级功能实现

5.1 多语言实时翻译

// 实时翻译功能实现
function initTranslation() {
  // 监听鼠标悬停事件
  document.addEventListener('mouseover', async (event) => {
    const target = event.target;
    if (target.tagName === 'SPAN' && target.classList.contains('translatable')) {
      return;
    }
    
    const text = getTextUnderCursor(event);
    if (!text || text.length < 2) return;
    
    // 请求翻译
    const translation = await translateText(text);
    
    // 显示翻译结果
    showTranslationPopup(event.clientX, event.clientY, text, translation);
  });
}

async function translateText(text) {
  const prompt = `将以下内容翻译为中文,保持专业语气:\n\n"${text}"`;
  
  try {
    const response = await cachedApiCall(prompt);
    return response.replace(/^"|"$/g, ''); // 移除可能的引号
  } catch (error) {
    console.error("翻译失败:", error);
    return "翻译服务不可用";
  }
}

function showTranslationPopup(x, y, original, translation) {
  const popup = document.createElement('div');
  popup.className = 'deepseek-translation-popup';
  popup.innerHTML = `
    
${original}
${translation}
`
; // 样式设置 popup.style = `/* 样式细节省略 */`; document.body.appendChild(popup); // 鼠标移出时移除 popup.addEventListener('mouseleave', () => { popup.remove(); }); }

5.2 智能写作模板

// 写作模板功能
const writingTemplates = {
  professionalEmail: {
    name: "专业邮件",
    systemPrompt: "你是一名专业的商务人士,请根据以下要点撰写一封正式商务邮件:",
    template: `
尊敬的{name}:

您好!我是{yourName},{yourPosition}。

{body}

期待您的回复。

此致
敬礼

{yourFullName}
{yourPosition}
{companyName}
    `
  },
  socialMediaPost: {
    name: "社交媒体帖子",
    systemPrompt: "你是一名社交媒体专家,请根据以下主题创作一个吸引人的社交媒体帖子:",
    template: `
 {主题} 

{内容}

{话题标签}
    `
  }
};

function applyTemplate(templateId, variables) {
  const template = writingTemplates[templateId];
  if (!template) return null;
  
  let content = template.template;
  for (const [key, value] of Object.entries(variables)) {
    const regex = new RegExp(`{${key}}`, 'g');
    content = content.replace(regex, value);
  }
  
  return {
    system: template.systemPrompt,
    content
  };
}

// 在弹出页面中使用模板
document.getElementById('templateSelect').addEventListener('change', (e) => {
  const templateId = e.target.value;
  const templateForm = document.getElementById('templateForm');
  templateForm.innerHTML = '';
  
  if (templateId === 'none') return;
  
  const template = writingTemplates[templateId];
  const variables = template.template.match(/{([^}]+)}/g)
    .map(v => v.replace(/[{}]/g, ''))
    .filter((v, i, arr) => arr.indexOf(v) === i);
  
  variables.forEach(variable => {
    const label = document.createElement('label');
    label.textContent = `${variable}:`;
    
    const input = document.createElement('input');
    input.type = 'text';
    input.dataset.variable = variable;
    input.placeholder = `输入 ${variable}`;
    
    templateForm.appendChild(label);
    templateForm.appendChild(input);
  });
  
  const generateBtn = document.createElement('button');
  generateBtn.textContent = '生成内容';
  generateBtn.addEventListener('click', generateFromTemplate);
  templateForm.appendChild(generateBtn);
});

async function generateFromTemplate() {
  const templateId = document.getElementById('templateSelect').value;
  const inputs = document.querySelectorAll('#templateForm input[data-variable]');
  
  const variables = {};
  inputs.forEach(input => {
    variables[input.dataset.variable] = input.value;
  });
  
  const templateData = applyTemplate(templateId, variables);
  const prompt = `${templateData.system}\n\n${templateData.content}`;
  
  const response = await chrome.runtime.sendMessage({
    action: 'processText',
    text: prompt
  });
  
  document.getElementById('generatedContent').textContent = response.result;
}

六、插件打包与发布

6.1 Manifest V3配置

{
  "manifest_version": 3,
  "name": "DeepSeek智能写作助手",
  "version": "1.0.0",
  "description": "集成DeepSeek AI的浏览器写作助手",
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "permissions": [
    "storage",
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "https://api.deepseek.com/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": [""],
      "js": ["content.js"],
      "css": ["content.css"],
      "run_at": "document_idle"
    }
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon16.png",
      "24": "icon24.png",
      "32": "icon32.png"
    }
  },
  "web_accessible_resources": [
    {
      "resources": ["inject.js"],
      "matches": [""]
    }
  ]
}

6.2 发布到Chrome应用商店

发布流程:

  1. 创建开发者账号(需一次性支付$5)
  2. 打包插件为ZIP文件
  3. 登录Chrome开发者控制台
  4. 上传ZIP文件并填写商店信息
  5. 提交审核(通常1-3天)
  6. 审核通过后发布上线

商店信息优化技巧

  • 使用高质量屏幕截图和宣传图
  • 编写详细的功能描述和更新日志
  • 添加相关关键词:“AI写作”、“DeepSeek”、“浏览器助手”
  • 设置合理的分类:“生产力工具”、“写作助手”

七、未来功能展望

7.1 多模态集成

  • 图像描述生成
  • 网页内容智能摘要
  • PDF文档内容提取与分析
// 图像描述功能概念代码
document.addEventListener('contextmenu', async (event) => {
  if (event.target.tagName === 'IMG') {
    const imgUrl = event.target.src;
    
    // 发送图像到后台处理
    chrome.runtime.sendMessage({
      action: "describeImage",
      imageUrl: imgUrl
    }, response => {
      showImageDescription(event.clientX, event.clientY, response.description);
    });
  }
});

7.2 本地模型集成

通过WebAssembly和WebGPU技术实现本地模型运行:

// 使用ONNX Runtime在浏览器中运行模型
async function loadLocalModel() {
  const session = await ort.InferenceSession.create('model/deepseek-mini.onnx');
  
  return async function infer(prompt) {
    const tokenized = tokenizer.encode(prompt);
    const input = new ort.Tensor('int64', tokenized, [1, tokenized.length]);
    
    const feeds = { input };
    const results = await session.run(feeds);
    
    return tokenizer.decode(results.output.data);
  };
}

7.3 协作功能增强

  • 多用户实时协同编辑
  • 版本历史与智能恢复
  • 团队知识库集成

结论:浏览器插件的无限可能

通过本教程,我们实现了:

  1. 无缝集成:将DeepSeek API深度集成到浏览器工作流
  2. 高效写作:在任何网页上实现智能写作辅助
  3. 安全可靠:采用最佳实践的密钥管理和数据安全
  4. 性能优化:通过缓存和流式响应提升用户体验

浏览器插件作为轻量级应用平台,结合DeepSeek强大的语言模型,正在重塑人机交互边界。随着WebAssembly、WebGPU等技术的发展,未来浏览器插件将具备更强大的本地计算能力,成为AI应用的重要入口。


参考资源

  1. DeepSeek API官方文档
  2. Chrome扩展开发文档
  3. Web Crypto API参考
  4. ONNX Runtime Web
  5. 浏览器插件安全最佳实践
平台类别 具体方案/模型 费用描述 额外说明
官方在线服务 免费网页版/移动端 基础功能(对话、搜索、代码纠错等)免费,可能有速率限制(如每小时30次交互) 无需注册或付费,适合日常使用
官方API调用 DeepSeek-V3 输入Token缓存命中:¥0.5/百万,未命中:¥2/百万;输出Token:¥8/百万 适用于开发者API集成,缓存命中可显著降低成本
DeepSeek-R1 输入Token:$0.55/百万;输出Token:$2.19/百万 美元计价,适合国际场景
DeepSeek-Coder(8B) 输入Token:$0.2/百万;输出Token:$0.6/百万 按模型大小分层收费
云平台托管 华为云(DeepSeek-V3) 输入Token:¥1/百万;输出Token:¥2/百万 合作方定价,与DeepSeek官方价格类似
华为云(DeepSeek-R1) 输入Token:¥4/百万;输出Token:¥16/百万
阿里云(PAI ModelGallery) 支持一键部署V3/R1,费用参考阿里云平台定价(未公开详细费率) 需通过PAI服务配置,成本浮动
本地部署 企业级(R1满血版) 硬件成本:¥80-120万(含8张H100显卡);年运维:¥35-58万(含电费、人力) 671B参数,适合大型机构
个人开发者(量化版70B) 初始成本:¥2.5-3万(含RTX 4090显卡等);年运维:¥0.5-1万 4bit量化,适合小型团队
云租赁方案 华为云租赁实例 按需:¥58/小时;包月:¥3.5-4万/月(含8卡A100算力) 适合短期项目,免硬件投入
AWS/GCP(p4d实例) 约$98/小时(折合¥700/小时) 高弹性但成本较高

你可能感兴趣的:(浏览器插件开发实战:零基础构建DeepSeek智能写作助手)