在人工智能技术爆发式发展的今天,DeepSeek作为国内领先的大语言模型平台,正改变着我们的工作方式。本文将深入解析如何通过浏览器插件技术,无缝集成DeepSeek API到日常浏览体验中,打造个人专属的AI写作助手。
浏览器插件由多个相互协作的组件构成,每个组件都有特定职责:
组件功能说明:
不同组件间通过消息传递机制通信:
// 内容脚本发送消息到后台
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使用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和网络连接";
}
}
对于长文本生成,使用流式响应提升用户体验:
// 流式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);
}
}
}
}
弹出页面提供用户配置和功能入口:
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>
内容脚本与网页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));
}
// 使用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);
}
// 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();
}
// 实时翻译功能实现
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();
});
}
// 写作模板功能
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;
}
{
"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": ["" ]
}
]
}
发布流程:
商店信息优化技巧:
// 图像描述功能概念代码
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);
});
}
});
通过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);
};
}
通过本教程,我们实现了:
浏览器插件作为轻量级应用平台,结合DeepSeek强大的语言模型,正在重塑人机交互边界。随着WebAssembly、WebGPU等技术的发展,未来浏览器插件将具备更强大的本地计算能力,成为AI应用的重要入口。
参考资源:
平台类别 | 具体方案/模型 | 费用描述 | 额外说明 |
---|---|---|---|
官方在线服务 | 免费网页版/移动端 | 基础功能(对话、搜索、代码纠错等)免费,可能有速率限制(如每小时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/小时) | 高弹性但成本较高 |