【浏览器插件】DeepSeek++:给网页版 DeepSeek 引入SKILL技能工具
Lv.2潜力创作者
DeepSeek++ 是一个浏览器扩展,它在 DeepSeek 网页版的基础上增加了一套 SKILL 技能系统,让 AI 从"只能聊天"变成"能执行命令、操作文件、搜索网页"的 Agent。
使用方式
在 DeepSeek 聊天框里以 / 开头输入指令:
/shell ls —— 列出当前目录的文件
/memory 记住我喜欢简洁的回答风格 —— 让 AI 记住一条偏好
/web_search 2024年诺贝尔物理学奖得主 —— 搜索网页
/frontend-design 做一个登录页面 —— 生成前端代码
敲下回车后,AI 稍等片刻就会返回执行结果。你还可从 GitHub 一键导入别人写好的技能包,或在侧边栏自己编写新技能。
一个跑在浏览器沙箱里的网页,为什么可以执行 Shell 命令、读写本地文件?答案在于:DeepSeek++ 在所有请求进出浏览器的必经之路上,安插了一个"中间件"——替 AI 把该做的事情执行掉,再把结果悄无声息地塞回对话。而且,插件是零服务端依赖:不修改 DeepSeek 服务端代码,不搭建任何后台服务器,所有能力完全依靠浏览器扩展提供的开放 API 实现。
一、拦截
你敲下 /shell ls 后,浏览器本该把这句话原样发给 DeepSeek 服务器。但 DeepSeek 的服务端根本不认识 /shell 这个命令。所以第一步很明确:在消息离开浏览器之前,截住它。
这里要用到浏览器扩展提供的一种叫 Content Script 的机制。你可以把它理解成一段"寄生"在网页里的 JavaScript 代码——它和网页跑在同一个标签页,既能操作页面上的输入框、按钮这些 DOM 元素,又能调用扩展才有的特殊权限,比如拦截网络请求和读写本地存储。两样能力缺一不可:没有 DOM 访问权就填不了命令,没有网络拦截权就截不住请求。
DeepSeek++ 部署了两套 Content Script,对应 Chrome 扩展的两种运行环境:
MAIN world(主世界):对应源码 src/content_scripts/main.ts,和网页共享全局变量和 DOM。它负责弹出 / 面板、筛选渲染技能列表,还通过 Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value') 绕过 React 对输入框的受控组件限制——否则你填入的 /shell 瞬间就被 React 清掉了。
Isolated world(隔离世界):对应源码 src/content_scripts/isolated.ts,关在独立沙箱里,与网页完全隔离。它独占网络请求拦截能力——用自定义函数替换 window.fetch 和 XMLHttpRequest.prototype.send,这是截胡任务的核心。
双 World 分工
两套脚本都在 document_start 注入——网页 HTML 刚下载完、页面脚本还没运行的瞬间。具体通过 chrome.scripting.registerContentScripts() 注册,runAt: "document_start" 指定注入时机。晚一丁点,页面代码可能已经发出了第一个 API 请求,截胡就漏掉了。
具体拦截手法:浏览器发网络请求只有两条路——fetch() 和 XMLHttpRequest。DeepSeek++ 用自定义函数替换 window.fetch 和 XMLHttpRequest.prototype.send,网页实际调用的是"替身"。替身检查目标 URL,如果命中 DeepSeek 聊天接口 https://chat.deepseek.com/api/v0/chat/completion 或 https://chat.deepseek.com/api/v0/chat/regenerate,就扣下来改造;不是则原样放行。为什么两条路都堵?因为 DeepSeek 页面在不同场景下会交替使用 fetch 和 XHR——只堵一条就有漏网的可能。
二、翻译
请求截下来之后,要将人类输入的命令 /shell ls 翻译为 AI 能理解的自然语言。AI 需要一段完整的指令,告诉它"你是谁、能干什么、怎么干"。
这个"翻译"过程叫 Prompt 增强,由 src/background/augment.ts 中的 augmentRequestBody() 函数完成,分六步推进:
六步增强流程
提取 → 匹配 → 替换。 用正则 /^\/(\S+)\s*([\s\S]*)$/ 检测用户输入是否以 / 开头,如果是就拆出技能名(shell)和参数(ls)。然后在 src/skill-registry.ts 维护的技能注册表里找到 shell,它对应的是一条几十行的完整指令——不再是六个字符,而是一段详细的"角色设定":"你现在是一个命令行终端,使用 <shell_exec> 标签执行命令,格式如下……"用这条长指令完全替换掉原始的 /shell ls。链式调用(如 /shell /memory 记住结果)会递归解析,依次匹配出多条指令。
注入记忆。 AI 每次对话有上下文长度限制,无法记住很久之前的对话内容。DeepSeek++ 把关键信息存在 chrome.storage.local 的 memory 键下,发请求前自动选出最相关的几条附着在 Prompt 里:先粗估当前对话已占多少 Token(中文每字约 1.5 Token,英文每字母约 0.25 Token),算出还能塞多少记忆——公式是 max(800, 1500 - (已用Token - 3000) * 0.2),确保最少保留 800 Token 的记忆空间;再按关键词匹配度、标签命中、最近访问时间和频率四个维度排序,挑出最优的一批注入系统 Prompt。
注入工具清单 → 组装。 告诉 AI:"你现在可以调用以下工具",把每个工具的 XML 标签格式、参数 Schema 和调用示例一起附上。工具多了这段内容会很占 Token——一个 shell_exec 的 Schema 就有近二十行 JSON,启用十几个工具时光工具描述就能吃掉上千 Token。最终组装结构:预设指令 → 系统模板(含记忆+工具清单)→ 处理后的用户输入 → 格式提醒,拼好后替换请求体放行。
三、执行:工具调用
主流 AI 平台用 JSON Function Call 做工具调用,但 DeepSeek++ 选了 XML 标签。原因藏在流式回复的机制里。
AI 不是一次性吐出完整答案,而是通过 SSE(Server-Sent Events,服务器推送事件) 一个字一个字往外蹦。你看到 AI 回复时一个字一个字地出现在屏幕上,就是 SSE 在工作。每一小段数据的格式大概是 data: {"choices":[{"delta":{"content":"你"}}]}——很碎,但保证了实时性。
XML 在流式场景下有天然优势:<shell_exec> 这 12 个字符一出现在流里,正则就能匹配到标签头;而 JSON 的 {"name":"shell_exec","arguments":{ 得等花括号全部配对完才知道这是一个完整的函数调用。逐字到达的场景下,XML 的前缀匹配延迟更低——更早判断"这是一个工具调用",更快触发执行。如果 AI 先回复了两百字分析才决定调用工具,这个差距就很明显了。
DeepSeek++ 同时拦截 fetch 的 Response 流和 XHR 的 progress 事件两个通道。每收到一小段文字就累积到缓冲区,根据当前启用的工具列表动态拼装正则表达式。一旦匹配到完整的开闭标签对——比如 <shell_exec>{"command":"ls"}</shell_exec>——立即触发执行,不等回复结束。如果工具执行出错,会追加 error 标签交 AI 二次处理。
三种执行通道
执行走三条通道:native 通过 chrome.runtime.connectNative('com.deepseekpp.host') 协议把命令发给本机独立程序。Native Messaging Host 的注册表路径在 Windows 为 HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.deepseekpp.host,指向 %LOCALAPPDATA%\deepseek-pp\host.json。host 程序可执行任意本地命令,安全取决于其权限隔离程度。in_process 最快但仅能做 chrome.storage 操作和 API 请求。bridge 通过 chrome.runtime.sendMessage 在扩展组件间灵活中转。执行结果通过一次新的 follow-up API 请求(重新调用 /api/v0/chat/completion)回传 AI,AI 读到结果后继续生成回复。整个过程对用户透明——只看到"让我查一下"的短暂等待,然后答案就来了。
四、存储
所有技能存在浏览器 chrome.storage.local 里——扩展专用的本地数据库,不依赖任何远程服务器。存储结构为 JSON 对象,键为 skills,值为技能数组,每条记录包含 name、instructions、source 等字段。source 字段决定来源:
builtin:v0.6.4 预装 9 个核心技能(shell、memory、frontend-design 等)+ OfficeCLI 文档处理库。不可删改,只能开关。
custom:在 SidePanel 侧边栏里手写,自由编辑。
remote:从 GitHub 仓库一键导入。
GitHub 远程导入
GitHub 导入全流程在扩展内自动完成:解析仓库 URL → 调用 https://api.github.com/repos/{owner}/{repo}/contents/{path} 列出目录结构 → 递归扫描每个目录下的 SKILL.md 文件 → 用 YAML 解析器提取技能元数据 → 去重后写入 chrome.storage.local。每个导入源保留完整仓库信息(URL、分支、最后更新时间),支持手动检查更新但不自动轮询——避免触发 GitHub API 的每小时 60 次访问限制。
所有数据还支持通过 WebDAV 协议加密同步到你自己的云存储(如 Nextcloud、OwnCloud)。同步时读取 chrome.storage.local 的全部键值,序列化为 JSON,通过 PUT 请求上传到 WebDAV 服务器。同步策略简单直接——全量覆盖、以时间戳较新的为准。
五、总结
总结下整个运行机制:
四层架构
从 /shell ls 被敲下到文件列表出现在屏幕上,这条请求的完整旅程是:你在输入框敲入命令 → 界面层(MAIN world)捕捉并填入 → 拦截层(Isolated world)截住请求 → 增强层把六个字符翻译成完整的 AI 指令 → DeepSeek 服务器处理 → AI 流式回复产生 <shell_exec> 标签 → 执行层通过 native 通道真跑 Shell → 结果通过 follow-up API 请求回传 AI 后格式化为自然语言 → 存储层持久化上下文。
Service Worker(扩展后台进程,对应源码 src/background/service_worker.ts)闲置约 30 秒后会被 Chrome 杀掉以节省内存,所以所有状态必须持久化到 chrome.storage.local、每次事件处理时重新加载——多了几次磁盘读写,换来了零内存泄漏的保证和随时重启的可靠性。