在实际开发和学习过程中我们经常需要将网页上的文本内容例如代码片段、技术文档、问题描述复制到 AI 助手如豆包、ChatGPT、DeepSeek 等的对话窗口中进行提问或分析。这个过程通常需要手动复制、切换标签页、粘贴如果内容分散在多个地方操作就更加繁琐。更麻烦的是一些 AI 平台对输入格式有要求或者我们希望在提问时附带一些固定的上下文如编程语言、项目背景每次手动添加既容易出错又浪费时间。一个高效的解决方案是使用浏览器插件。它可以直接捕获当前页面的选中文本、整个标签页内容甚至根据预设规则自动格式化然后一键发送到指定的 AI 服务接口或者填充到剪贴板实现“所见即所得一点即发送”。本文将围绕如何理解、选择并实际应用这样一款提升效率的浏览器插件展开即使你没有任何插件开发经验也能通过清晰的步骤将其集成到你的工作流中。我们将重点关注插件的核心功能内容捕获、格式化与一键操作。虽然输入材料提到了“GaryPrompt”这个具体名称但本文不会局限于某个特定插件而是会剖析这类工具的通用实现思路、关键配置以及如何安全、稳定地将其用于你的日常开发。你会了解到从环境准备、权限配置、与 AI API 交互到处理常见错误的全过程。1. 理解浏览器插件如何提升 AI 使用效率浏览器插件Extension是一种小型的软件程序用于定制和增强浏览器的功能。它基于 Web 技术HTML, CSS, JavaScript开发并能够通过浏览器提供的 API 与网页内容、浏览器标签页、书签、历史记录以及操作系统如剪贴板进行交互。1.1 传统工作流 vs. 插件增强工作流在分析技术实现之前我们先对比一下两种工作流这能清楚地说明为什么需要这样一个工具。传统手动工作流在浏览器中阅读技术文章或查看代码。用鼠标拖动选中需要的文本。按下CtrlC(或CmdC) 复制。切换到 AI 助手的浏览器标签页或应用窗口。将光标定位到输入框。按下CtrlV(或CmdV) 粘贴。可选手动添加一些提示词如“请解释以下代码”。按下回车发送。这个过程涉及多次上下文切换和手动操作容易打断思路且格式如代码缩进可能在复制粘贴过程中丢失。插件增强工作流在浏览器中选中文本。点击浏览器工具栏上的插件图标或使用右键菜单选项。插件自动捕获选中文本并可能根据预设模板进行格式化例如自动包裹成 Markdown 代码块并添加语言标识和提问前缀。插件自动将处理后的文本复制到剪贴板或直接通过 API 发送到配置好的 AI 服务。用户切换到 AI 助手界面直接粘贴或查看回复。这个流程将多步操作压缩为一两步减少了干扰并保证了内容格式的规范性。1.2 核心功能模块拆解一个用于辅助 AI 交互的浏览器插件其核心通常包含以下几个模块内容捕获模块负责获取用户想要处理的文本。这可以通过监听当前页面的选中事件、解析整个网页的 DOM 结构或者读取特定 HTML 元素的内容来实现。内容处理/格式化模块对捕获的原始文本进行加工。例如清洗多余的空白字符、转义特殊符号、将纯文本代码包装成 language\n...\n 的格式或者在文本前后添加固定的提示词模板。输出模块决定处理后的文本如何被使用。常见方式有复制到剪贴板最通用、最安全的方式兼容任何 AI 平台。填充到指定输入框通过脚本自动在目标网页的输入框中填入内容。通过 API 直接发送插件在后台将文本发送到如 OpenAI、DeepSeek、智谱 AI 等服务的 API并获取返回结果。这种方式功能最强但涉及网络请求、API 密钥管理和更复杂的错误处理。用户配置模块允许用户自定义格式化模板、API 端点、密钥通常安全地存储在浏览器的本地存储中等。1.3 浏览器扩展 API 简介开发或配置这类插件需要了解几个关键的浏览器扩展 APIchrome.tabs/browser.tabs: 用于与浏览器标签页交互如获取当前活动标签页的 ID 和 URL。chrome.scripting/browser.scripting: 用于向指定标签页注入 JavaScript 脚本从而读取或操作页面 DOM。chrome.storage/browser.storage: 用于安全地存储和读取插件的配置数据如 API Key。chrome.runtime/browser.runtime: 插件运行时 API用于处理消息通信、后台生命周期等。clipboardAPI: 用于读写系统剪贴板。注意现代 Web 标准有navigator.clipboard但在插件环境中可能需要使用chrome或browser命名空间下的特定 API 以获得更高权限。对于用户而言理解这些 API 的能力边界有助于在遇到插件权限请求时做出判断并在排查问题时知道可能的原因所在。2. 环境准备与插件获取/开发基础无论你是想直接使用一个现成插件还是有意基于开源项目进行二次开发都需要准备好相应的环境。2.1 浏览器选择与开发者模式主流的 Chromium 内核浏览器如 Google Chrome, Microsoft Edge, 新版 Opera 等都支持相同的扩展程序体系。Firefox 也支持但其 API 命名空间通常为browser.*与chrome.*略有不同不过很多现代插件会做兼容处理。要安装非商店来源的插件例如自己打包的.crx文件或解压的开发者模式插件必须开启浏览器的“开发者模式”。在 Chrome/Edge 中开启开发者模式打开浏览器在地址栏输入chrome://extensions/或edge://extensions/。在页面右上角找到“开发者模式”开关并将其打开。开启后页面会刷新并出现“加载已解压的扩展程序”、“打包扩展程序”等新按钮。注意以开发者模式加载的插件在每次浏览器启动时可能会有提示并且某些高级 API 可能受限。用于生产环境时建议通过官方商店安装已签名的正式版。2.2 获取插件的方式官方应用商店最安全、最便捷的方式。在 Chrome Web Store 或 Microsoft Edge Add-ons 中搜索相关关键词如 “AI prompt”, “copy to chat”, “text formatter”进行查找和安装。加载已解压的扩展程序如果你有插件的源代码目录或者下载了插件的解压包通常是一个包含manifest.json文件的文件夹可以通过点击“加载已解压的扩展程序”按钮选择该文件夹来安装。安装.crx文件较旧的方式。将.crx文件拖入chrome://extensions/页面即可安装。但现代浏览器出于安全考虑可能默认禁止安装非商店的.crx文件。2.3 理解插件的基本结构一个最简单的浏览器插件至少包含以下文件your-extension/ ├── manifest.json # 核心配置文件定义插件元数据、权限和入口 ├── popup.html # 点击插件图标时弹出的页面可选 ├── popup.js # popup页面的逻辑 ├── background.js # 后台服务脚本处理长期任务和事件监听 ├── content.js # 注入到网页中的脚本用于与页面DOM交互 └── icons/ # 插件图标多种尺寸 ├── icon16.png ├── icon48.png └── icon128.png其中manifest.json是最关键的文件它声明了插件的名称、版本、权限请求和功能点。下面是一个简化版的示例展示了一个具有内容脚本和后台脚本的插件配置{ manifest_version: 3, name: AI Prompt Helper, version: 1.0.0, description: 一键捕获并格式化文本用于AI提问。, permissions: [ activeTab, scripting, clipboardWrite, storage ], host_permissions: [ https://api.openai.com/*, https://api.deepseek.com/* ], background: { service_worker: background.js }, action: { default_popup: popup.html, default_icon: { 16: icons/icon16.png, 48: icons/icon48.png, 128: icons/icon128.png } }, content_scripts: [ { matches: [all_urls], js: [content.js] } ] }permissions: 声明插件需要的权限。activeTab允许临时访问当前活动标签页scripting允许注入脚本clipboardWrite允许写入剪贴板storage允许本地存储数据。host_permissions: 声明插件需要访问哪些外部域名。如果插件需要调用外部 API这里必须列出对应的 API 端点地址。content_scripts: 定义注入到哪些网页的脚本。all_urls表示匹配所有网址需谨慎使用。3. 实现核心功能内容捕获与格式化本节我们将构建一个最小可行版本实现选中文本、格式化并复制到剪贴板的功能。我们将以 Manifest V3 为标准这是 Chrome 扩展开发的最新规范。3.1 项目初始化与文件结构首先创建一个新的目录ai-prompt-helper并按照上述基本结构创建文件。manifest.json(完整版):{ manifest_version: 3, name: AI Prompt Helper, version: 1.0, description: Capture and format selected text for AI prompts., permissions: [activeTab, scripting, clipboardWrite, storage], action: { default_popup: popup.html, default_icon: { 16: images/icon16.png, 48: images/icon48.png, 128: images/icon128.png } }, icons: { 16: images/icon16.png, 48: images/icon48.png, 128: images/icon128.png }, background: { service_worker: background.js } }popup.html(弹出窗口界面):!DOCTYPE html html head meta charsetutf-8 style body { width: 300px; padding: 15px; font-family: sans-serif; } button { width: 100%; padding: 10px; margin: 5px 0; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background: #45a049; } textarea { width: 100%; height: 100px; margin: 10px 0; box-sizing: border-box; } .config { margin-top: 15px; } label { display: block; margin: 5px 0; } /style /head body h3AI Prompt Helper/h3 button idcapturePage捕获整个页面文本/button button idcaptureSelected捕获选中文本并格式化/button div classconfig label提示词前缀:/label textarea idprefixTemplate请分析以下代码\n\n{{SELECTION}}\n/textarea button idsaveConfig保存配置/button /div script srcpopup.js/script /body /html3.2 后台服务脚本与消息通信后台脚本 (background.js) 负责处理来自弹出页面 (popup.js) 的消息并执行需要高权限的操作如注入脚本、访问所有标签页。background.js:// 监听来自popup或content script的消息 chrome.runtime.onMessage.addListener((request, sender, sendResponse) { if (request.action getSelectedText) { // 获取当前活动标签页 chrome.tabs.query({ active: true, currentWindow: true }, (tabs) { if (tabs[0].id) { // 向该标签页注入内容脚本并执行获取选中文本的函数 chrome.scripting.executeScript({ target: { tabId: tabs[0].id }, func: getSelectedTextFromPage, }, (results) { if (results results[0] results[0].result) { sendResponse({ text: results[0].result }); } else { sendResponse({ text: }); } }); } }); // 返回true表示将异步发送响应 return true; } else if (request.action copyToClipboard) { // 将文本写入剪贴板 navigator.clipboard.writeText(request.text).then(() { sendResponse({ success: true }); }).catch(err { sendResponse({ success: false, error: err.message }); }); return true; } }); // 这个函数将在目标网页的上下文中执行 function getSelectedTextFromPage() { return window.getSelection().toString(); }3.3 弹出页面逻辑与配置管理弹出页面脚本 (popup.js) 处理用户点击事件与后台脚本通信并管理本地配置。popup.js:document.addEventListener(DOMContentLoaded, function() { // 加载保存的配置 chrome.storage.local.get([prefixTemplate], function(result) { if (result.prefixTemplate) { document.getElementById(prefixTemplate).value result.prefixTemplate; } }); // 捕获选中文本按钮 document.getElementById(captureSelected).addEventListener(click, function() { // 1. 从后台获取选中文本 chrome.runtime.sendMessage({ action: getSelectedText }, (response) { if (response response.text) { const selectedText response.text.trim(); if (selectedText) { // 2. 获取格式化模板 const prefix document.getElementById(prefixTemplate).value; // 3. 格式化文本将模板中的 {{SELECTION}} 替换为实际文本 // 这里实现一个简单的检测如果选中文本包含换行可能是一段代码 let formattedText prefix.replace({{SELECTION}}, selectedText); // 4. 复制到剪贴板 chrome.runtime.sendMessage({ action: copyToClipboard, text: formattedText }, (resp) { if (resp resp.success) { alert(格式化后的文本已复制到剪贴板); } else { alert(复制失败: (resp.error || 未知错误)); } }); } else { alert(未选中任何文本。); } } }); }); // 保存配置按钮 document.getElementById(saveConfig).addEventListener(click, function() { const template document.getElementById(prefixTemplate).value; chrome.storage.local.set({ prefixTemplate: template }, function() { alert(配置已保存); }); }); });3.4 加载与测试插件将上述文件按结构保存好并准备几个简单的图标文件如 16x16, 48x48, 128x128 的 PNG 图片放在images/文件夹下。打开chrome://extensions/确保“开发者模式”已开启。点击“加载已解压的扩展程序”按钮选择你创建的ai-prompt-helper文件夹。加载成功后插件图标会出现在浏览器工具栏。点击图标弹出我们设计的界面。打开一个包含代码或文本的网页例如 GitHub 上的一个项目页面用鼠标选中一段文本。点击插件图标在弹出的窗口中点击“捕获选中文本并格式化”。如果一切正常你会收到成功提示。此时切换到任意文本编辑器或 AI 助手的输入框按CtrlV粘贴应该能看到格式化后的文本例如请分析以下代码function hello() { console.log(Hello, world!); }4. 进阶功能集成 AI API 与错误处理将文本复制到剪贴板只是第一步。更强大的功能是让插件直接与 AI API 对话并将结果返回。这涉及到网络请求、API 密钥管理和更复杂的异步处理。4.1 配置 API 密钥与端点首先我们需要在弹出页面增加 API 配置区域。修改popup.html在配置区添加div classconfig labelAI API 服务商:/label select idapiProvider option valueopenaiOpenAI/option option valuedeepseekDeepSeek/option option valuezhipu智谱AI/option /select labelAPI Key:/label input typepassword idapiKey placeholdersk-... labelAPI 端点 (可选使用默认):/label input typetext idapiEndpoint placeholderhttps://api.openai.com/v1/chat/completions label模型名称:/label input typetext idapiModel valuegpt-3.5-turbo placeholder例如: gpt-4, deepseek-chat button idsaveApiConfig保存 API 配置/button /div在popup.js中增加加载和保存 API 配置的逻辑// 加载API配置 chrome.storage.local.get([apiProvider, apiKey, apiEndpoint, apiModel], function(result) { if (result.apiProvider) document.getElementById(apiProvider).value result.apiProvider; if (result.apiKey) document.getElementById(apiKey).value result.apiKey; if (result.apiEndpoint) document.getElementById(apiEndpoint).value result.apiEndpoint; if (result.apiModel) document.getElementById(apiModel).value result.apiModel; }); // 保存API配置 document.getElementById(saveApiConfig).addEventListener(click, function() { const config { apiProvider: document.getElementById(apiProvider).value, apiKey: document.getElementById(apiKey).value, apiEndpoint: document.getElementById(apiEndpoint).value || getDefaultEndpoint(document.getElementById(apiProvider).value), apiModel: document.getElementById(apiModel).value }; chrome.storage.local.set(config, function() { alert(API 配置已保存); }); }); function getDefaultEndpoint(provider) { const endpoints { openai: https://api.openai.com/v1/chat/completions, deepseek: https://api.deepseek.com/v1/chat/completions, zhipu: https://open.bigmodel.cn/api/paas/v4/chat/completions }; return endpoints[provider] || ; }4.2 实现 API 调用与后台请求我们不能在前台页面 (popup.js) 中直接发起跨域网络请求因为弹出窗口的生命周期很短且权限受限。正确的做法是将 API 请求放在后台脚本 (background.js) 中。首先更新manifest.json在host_permissions中添加可能用到的 API 域名host_permissions: [ https://api.openai.com/*, https://api.deepseek.com/*, https://open.bigmodel.cn/* ]然后在background.js中添加处理 API 请求的消息监听器// 在 onMessage 监听器中添加新的 action 分支 else if (request.action callAIAPI) { callAIAPI(request.prompt, request.config).then(response { sendResponse({ success: true, data: response }); }).catch(error { sendResponse({ success: false, error: error.message }); }); return true; // 保持消息通道开放以进行异步响应 } async function callAIAPI(prompt, userConfig) { // 从存储中获取配置如果请求中未提供 let config userConfig; if (!config) { config await new Promise(resolve { chrome.storage.local.get([apiProvider, apiKey, apiEndpoint, apiModel], resolve); }); } if (!config.apiKey) { throw new Error(API Key 未配置。请在插件设置中填写。); } const endpoint config.apiEndpoint; const model config.apiModel || gpt-3.5-turbo; const requestBody { model: model, messages: [{ role: user, content: prompt }], stream: false // 为简化示例关闭流式输出 }; const response await fetch(endpoint, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${config.apiKey} }, body: JSON.stringify(requestBody) }); if (!response.ok) { const errorText await response.text(); let errorMsg API 请求失败 (${response.status}); try { const errorJson JSON.parse(errorText); errorMsg : ${errorJson.error?.message || errorText}; } catch (e) { errorMsg : ${errorText}; } throw new Error(errorMsg); } const data await response.json(); // 提取回复内容。不同API返回结构可能不同这里以OpenAI格式为例。 return data.choices[0]?.message?.content || 未收到有效回复。; }4.3 在弹出页面中调用 API在popup.html中增加一个按钮和一个用于显示结果的区域button idsendToAI发送选中文本到 AI/button div idresultArea stylemargin-top: 10px; padding: 10px; border: 1px solid #ccc; display: none; strongAI 回复/strong div idaiResponse stylewhite-space: pre-wrap; max-height: 200px; overflow-y: auto;/div /div在popup.js中为新增按钮添加事件并处理 API 调用和结果显示document.getElementById(sendToAI).addEventListener(click, function() { const resultArea document.getElementById(resultArea); const aiResponse document.getElementById(aiResponse); resultArea.style.display none; aiResponse.textContent 请求中...; chrome.runtime.sendMessage({ action: getSelectedText }, (selectionResponse) { if (selectionResponse selectionResponse.text) { const selectedText selectionResponse.text.trim(); if (selectedText) { const prefix document.getElementById(prefixTemplate).value; const finalPrompt prefix.replace({{SELECTION}}, selectedText); // 获取当前配置并发送请求 chrome.storage.local.get([apiProvider, apiKey, apiEndpoint, apiModel], (config) { chrome.runtime.sendMessage({ action: callAIAPI, prompt: finalPrompt, config: config }, (apiResponse) { if (apiResponse apiResponse.success) { aiResponse.textContent apiResponse.data; resultArea.style.display block; } else { alert(AI 请求失败: (apiResponse?.error || 未知错误)); aiResponse.textContent 请求失败。; } }); }); } else { alert(未选中任何文本。); } } }); });现在插件具备了将选中文本格式化后直接发送给 AI API 并显示回复的能力。5. 常见问题排查与最佳实践在实际使用或开发此类插件时你会遇到各种问题。下面列出一些典型场景及其排查路径。5.1 插件安装与加载问题问题现象可能原因检查与解决步骤无法加载已解压的扩展程序1. 文件夹结构错误缺少manifest.json。2.manifest.json格式错误或版本不支持。3. 浏览器开发者模式未开启。1. 检查文件夹根目录是否有manifest.json。2. 使用 JSON 验证工具检查manifest.json语法确保manifest_version为 3。3. 确认chrome://extensions/页面右上角的“开发者模式”已开启。插件图标不显示或功能不生效1. 图标文件路径错误或缺失。2. 权限 (permissions) 声明不足。3. 内容脚本 (content_scripts) 未正确注入。1. 检查manifest.json中action.default_icon和icons的路径是否正确。2. 对照本文第 3.1 节检查是否声明了activeTab,scripting等必要权限。3. 在chrome://extensions/页面点击插件详情下的“错误”链接查看具体报错。点击插件图标无反应1.default_popup指定的 HTML 文件路径错误。2.popup.html或popup.js存在语法错误。1. 右键点击插件图标选择“审查弹出内容”打开开发者工具查看控制台错误。5.2 API 调用相关错误这是集成外部服务时最常见的问题。问题现象 (错误信息)可能原因排查与解决方案API error: 400请求格式错误。常见于模型名称错误、请求体结构不符合 API 要求、或消息角色 (role) 设置错误。1. 检查apiModel配置是否正确如gpt-3.5-turbo,deepseek-chat。2. 在后台脚本的callAIAPI函数中使用console.log输出最终的requestBody对比官方 API 文档。3. 确认messages数组格式为[{role: user, content: ...}]。API error: 401API 密钥无效、过期或没有权限。1. 在插件配置页面重新输入正确的 API Key确保没有多余空格。2. 登录对应 AI 服务平台确认密钥状态是否启用、额度是否充足。3. 对于某些服务如智谱注意密钥可能需要添加前缀如Bearer后的部分。API error: 402 Insufficient balance或429账户余额不足或请求频率超限。1. 登录对应平台检查账户余额或套餐用量。2. 如果是频率限制尝试降低请求频率或在代码中增加延时重试逻辑。API error: Connection lost mid-response网络连接不稳定或服务器中断。1. 检查本地网络。2. 查看对应 AI 服务的状态页面确认服务是否正常。3. 考虑在代码中实现简单的重试机制例如失败后等待 2 秒重试一次。API error: This model‘s maximum context length is ... tokens输入的文本提示词历史超过了模型的最大上下文长度。1. 减少输入的文本长度。可以尝试只发送关键代码片段而非整个文件。2. 如果使用流式或长对话需要自行管理上下文裁剪旧消息。5.3 内容脚本与页面交互问题问题现象可能原因解决方案无法获取选中文本 (getSelection返回空)1. 目标页面是 PDF 或特殊格式如某些视频播放器。2. 内容脚本注入时机过早页面尚未加载完成。1. 对于特殊页面可能需要使用更复杂的方法如通过document.body.innerText获取全部文本再过滤。2. 在content.js中监听DOMContentLoaded或load事件后再执行操作。插件在特定网站如 Chrome 网上应用店不工作浏览器出于安全策略默认禁止插件在某些特权页面运行。这是正常限制。无法也不应该绕过。5.4 安全与最佳实践API 密钥安全永远不要将 API 密钥硬编码在插件的源代码中尤其是打算公开分享时。使用chrome.storage.local存储密钥。虽然它相对于普通网页存储更安全但仍在用户设备本地。提醒用户不要在不受信任的电脑上使用此插件。更高级的方案是开发一个配套的后端服务插件将请求发送到你的后端由后端持有密钥并转发请求。但这增加了复杂度。权限最小化在manifest.json中只声明插件运行所必需的权限。例如如果不需要访问所有网站就不要用all_urls而是指定具体的匹配模式。谨慎使用scripting权限确保注入的脚本是安全的。错误处理与用户反馈像上面示例一样对所有异步操作chrome.runtime.sendMessage,fetch进行错误捕获并向用户提供清晰的错误提示而不是静默失败。对于网络请求考虑增加超时设置和重试逻辑。用户体验长时间操作如调用 AI API时在弹出页面或通过浏览器通知给予“处理中”的反馈。成功复制到剪贴板后可以提供一个更优雅的提示如短暂的颜色变化而非alert弹窗。6. 扩展方向与生产环境建议这个基础插件可以沿多个方向进行功能增强以适应更复杂的生产场景。6.1 功能扩展建议多模板管理允许用户保存多个格式化模板如“解释代码”、“翻译成英文”、“总结文章”并快速切换。上下文保留实现简单的对话记忆功能将 AI 的回复也作为上下文的一部分在后续提问中发送实现多轮对话。快捷键支持在manifest.json中声明commands让用户可以通过键盘快捷键如CtrlShiftP直接触发文本捕获和格式化而无需点击图标。右键菜单集成添加右键菜单选项让用户可以在网页上直接右键选中文本并选择“发送到 AI”。结果处理不仅显示 AI 回复还提供“复制回复”、“朗读”或“在新标签页中打开详细结果”的选项。支持更多 AI 服务集成 Claude、Gemini、国内大模型等更多 API并提供统一的配置界面。6.2 发布与分发代码压缩与混淆对前端 JavaScript 代码进行压缩和混淆以保护业务逻辑并减小体积。商店发布将插件打包后提交到 Chrome 网上应用店或 Edge 外接程序网站。这需要注册开发者账号并支付一次性费用Chrome 为 5 美元。商店发布能获得自动更新和更广泛的用户信任。版本更新在manifest.json中管理好版本号。当发布更新时用户端的插件会自动更新。6.3 性能与资源考量后台脚本Manifest V3 的后台脚本是 Service Worker它会在不活动时休眠。确保你的脚本是事件驱动的避免长时间运行的循环以免被浏览器终止。内容脚本注入到每个页面的内容脚本应尽可能轻量避免影响页面性能。复杂的逻辑应放在后台脚本中。存储使用chrome.storage有容量限制通常 5MB 或更多。避免存储过大的数据。对于 API 密钥等敏感信息考虑使用chrome.storage.session会话级存储或chrome.storage.managed由管理员策略设置。通过遵循上述步骤和最佳实践你可以构建一个既强大又可靠的浏览器插件彻底改变与 AI 工具交互的方式将碎片化的操作整合为流畅的一键式体验。核心在于理解浏览器扩展的能力模型妥善处理权限、异步通信和错误并始终将安全性和用户体验放在首位。