You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展Summarizer:OpenAI API文本摘要集成问题与代码修复求助

Summarizer Chrome扩展修复方案

我开发了一款名为Summarizer的Chrome扩展,功能是让用户高亮段落生成摘要,基于OpenAI API实现。目前遇到三个核心问题:API集成存在安全风险、摘要逻辑有故障、生成的摘要无法保存到侧边栏历史,同时希望优化API调用成本。

扩展预期运行流程:

  • 用户高亮一段文本
  • 高亮区域附近弹出「Summarize It?」按钮
  • 点击按钮后,高亮文本发送至OpenAI API生成摘要
  • 摘要通过弹窗展示,同时保存到侧边栏历史记录

原始代码存在的问题

  1. API密钥暴露:content.js中硬编码API密钥,任何用户都能查看,存在安全风险;直接在content script发起API请求,易引发安全隐患
  2. 按钮事件逻辑错误:点击其他区域隐藏按钮的代码中,使用了未定义的summarizeBtn变量,导致报错,按钮无法正常隐藏
  3. 摘要未存入历史:生成摘要后未调用Chrome存储API保存,侧边栏无法展示历史记录
  4. 成本控制缺失:未限制输入文本长度,API调用的max_tokens固定,易造成不必要的token消耗

修复后的文件代码

manifest.json

添加必要权限,确保API请求合法:

{
    "manifest_version": 3,
    "name": "Summarizer",
    "version": "1.0",
    "description": "Quickly summarize and save important text.",
    "permissions": ["storage", "sidePanel", "activeTab"],
    "action": {
        "default_icon": {
            "48": "icons/icon48.png",
            "128": "icons/icon128.png"
        }
    },
    "side_panel": {
        "default_path": "sidebar.html"
    },
    "background": {
        "service_worker": "background.js"
    },
    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content.js"],
            "run_at": "document_end"
        }
    ],
    "host_permissions": ["https://api.openai.com/*"]
}

background.js

将API请求逻辑移至后台,避免密钥暴露,同时处理摘要存储:

chrome.runtime.onInstalled.addListener(() => {
    chrome.sidePanel
        .setPanelBehavior({ openPanelOnActionClick: true })
        .catch((error) => console.error("Side Panel Error:", error));
});

// 处理content script的摘要请求
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
    if (request.action === "summarizeText") {
        const apiKey = "YOUR_OPENAI_API_KEY"; // 建议通过chrome.storage.local安全存储
        const url = "https://api.openai.com/v1/chat/completions";
        try {
            const response = await fetch(url, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${apiKey}`
                },
                body: JSON.stringify({
                    model: "gpt-3.5-turbo",
                    messages: [{ 
                        role: "system", 
                        content: "用简洁准确的语言总结文本,控制在100词以内" 
                    }, { 
                        role: "user", 
                        content: request.text 
                    }],
                    max_tokens: 100,
                    temperature: 0.3
                })
            });

            if (!response.ok) {
                const errorMsg = await response.text();
                console.error("API Error:", errorMsg);
                sendResponse({ success: false, message: "API请求失败:" + errorMsg });
                return;
            }

            const data = await response.json();
            const summary = data.choices?.[0]?.message?.content.trim() || "生成摘要失败";
            
            // 保存摘要到本地存储
            chrome.storage.local.get(["summaries"], (result) => {
                const summaries = result.summaries || [];
                summaries.unshift(summary);
                chrome.storage.local.set({ summaries }, () => {
                    sendResponse({ success: true, summary });
                });
            });
            return true;
        } catch (error) {
            console.error("Summarization error:", error);
            sendResponse({ success: false, message: "生成摘要时出错:" + error.message });
        }
    }
});

content.js

修复按钮事件错误,改为向后台发送请求:

document.addEventListener("mouseup", function (event) {
    let selectedText = window.getSelection().toString().trim();
    let existingButton = document.getElementById("summarize-btn");
    
    // 取消选中文本时移除按钮
    if (!selectedText) {
        if (existingButton) existingButton.remove();
        return;
    }
    
    // 创建按钮(仅一次)
    if (!existingButton) {
        existingButton = document.createElement("button");
        existingButton.id = "summarize-btn";
        existingButton.innerText = "Summarize It?";
        existingButton.style.position = "absolute";
        existingButton.style.zIndex = "1000";
        existingButton.style.background = "#ff5722";
        existingButton.style.color = "white";
        existingButton.style.border = "none";
        existingButton.style.padding = "8px";
        existingButton.style.cursor = "pointer";
        existingButton.style.borderRadius = "5px";
        existingButton.style.boxShadow = "2px 2px 5px rgba(0,0,0,0.2)";
        document.body.appendChild(existingButton);

        // 绑定点击事件
        existingButton.onclick = async function () {
            existingButton.innerText = "Summarizing...";
            existingButton.disabled = true;
            
            // 向后台发送请求
            chrome.runtime.sendMessage(
                { action: "summarizeText", text: selectedText },
                (response) => {
                    alert(response.success ? `摘要:\n${response.summary}` : `错误:\n${response.message}`);
                    existingButton.remove();
                }
            );
        };

        // 点击其他区域隐藏按钮
        document.addEventListener("click", function removeBtn(event) {
            if (!existingButton.contains(event.target)) {
                existingButton.remove();
                document.removeEventListener("click", removeBtn);
            }
        }, { once: true });
    }
    
    // 定位按钮到高亮文本下方
    let rect = window.getSelection().getRangeAt(0).getBoundingClientRect();
    existingButton.style.top = `${rect.bottom + window.scrollY + 5}px`;
    existingButton.style.left = `${rect.left + window.scrollX}px`;
});

优化历史记录展示,添加实时更新监听:

document.addEventListener("DOMContentLoaded", function () {
    const summaryList = document.getElementById("summary-list");

    function loadSummaries() {
        chrome.storage.local.get(["summaries"], function (result) {
            summaryList.innerHTML = "";
            const summaries = result.summaries || [];
            summaries.forEach((summary, index) => {
                displaySummary(summary, index);
            });
        });
    }

    function displaySummary(summary, index) {
        const summaryItem = document.createElement("div");
        summaryItem.classList.add("summary-item");
        summaryItem.innerHTML = `
            <p><strong>摘要 ${index + 1}:</strong><br>${summary}</p>
            <button data-index="${index}">删除</button>
        `;

        summaryItem.querySelector("button").addEventListener("click", function () {
            deleteSummary(index);
        });

        summaryList.appendChild(summaryItem);
    }

    function deleteSummary(index) {
        chrome.storage.local.get(["summaries"], function (result) {
            let summaries = result.summaries || [];
            summaries.splice(index, 1);

            chrome.storage.local.set({ summaries: summaries }, function () {
                loadSummaries();
            });
        });
    }

    // 实时更新历史记录
    chrome.storage.onChanged.addListener((changes) => {
        if (changes.summaries) loadSummaries();
    });

    loadSummaries();
});

成本优化建议

  • 限制输入文本长度:在content.js中添加判断,超过1000词时提示用户缩短文本
  • 模型选择:提供选项让用户切换到更经济的模型(如gpt-3.5-turbo-16k适合长文本)
  • 精准Prompt:明确要求摘要长度,减少不必要的token生成
  • 本地缓存:对相同文本的摘要进行缓存,避免重复调用API

内容的提问来源于stack exchange,提问作者Jeric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 23:29:59