简介:面向需要将DeepSeek等大模型能力落地到办公场景的开发者,这份PDF完整呈现了在WPS中深度集成DeepSeek API、打造智能办公插件的全过程,覆盖从入门到实战的关键环节。资源共1个文件,为PDF格式,压缩包大小约2.16MB,文档共33页;内容按“开发实录”组织,依次展开WPS插件环境搭建、API密钥获取、需求分析、插件架构设计、DeepSeek API集成、文本生成/语言翻译/格式调整/信息检索等功能模块开发,以及插件注册、菜单工具栏整合、测试优化和部署发布。通过清晰目录与步骤化叙述,读者可了解API请求参数构造、HTTP调用、错误处理与重试机制、WPS文档内容读写和事件监听等实现细节,并可直接借鉴该架构完成同类智能办公插件的设计与编码。目前已有107人学习下载,适合具备一定编程基础、希望掌握WPS二次开发和AI能力集成的开发者作为系统参考。
1. 用 DeepSeekAPI 给 WPS 装上一个“会干活”的 AI 助手:标题在讲什么
写周报时,把一段流水账复制到手边某个对话框里,让它变成通顺的汇报文字;打开表格,让 AI 按表头生成一列公式;对着 PPT 页念一段需求,让它直接产出演讲词。这些场景听起来是“智能办公”该有的样子,可真落地时,多数人卡在同一个地方:DeepSeekAPI 在代码里能跑通,一回到 WPS 就不知道怎么把接口、文档对象和选区粘起来。要聊的正是这条实测过的路径:在 WPS 的 JS 宏环境里,用 DeepSeekAPI 搭一个能读写选区内容的智能办公插件。它不需要编译,不依赖外部服务器,适合想把 AI 能力真正塞进日常文档流程里的一线从业者。
2. 选型与最小可跑通:为什么走 WPS JS 宏,以及第一个能用的请求
很多人拿到“用 DeepSeekAPI 做 WPS 插件”这个题目,第一反应是去搜“WPS 插件开发”。搜索到一半就发现路由太多,有 VBA、COM 加载项、JS 宏、加载项(Add-in),还有用 Python 写独立进程的野路子。本文直接把结论亮出来:个人与中小团队做深度集成,优先走 WPS JS 宏;需要给整个部门交付统一体验时,再把同一套逻辑包装成 WPS 加载项。下面讲清楚为什么是这条路,以及第一个最小脚本怎么落地。
2.1 有四条集成路线,为什么唯独 JS 宏最适合
把 WPS 接上 DeepSeekAPI,本质上只干两件事:拿到当前文档的选区内容,再把这个内容 POST 到接口,拿到返回后写回文档。难点不在 HTTP 请求,而在“怎么在 WPS 进程内合法地碰文档”。
常见做法是以下四条路线,各自的代价完全不同。
VBA 最老牌,网上“wps vba”的教程一抓一把,很多老插件说支持 WPS,其实走的是兼容层。它的问题是环境配置依赖“启用 VBA 组件”,分发时要担心目标机器有没有装对应组件;而且 VBA 的 HTTP 请求要引用 MSXML2.XMLHTTP,在 WPS 里偶尔会因为组件版本差异翻车。做原型很快,做交付很痛苦。
COM 加载项是 Windows 平台上的正规军,用 C# 或 C++ 写 DLL,注册到注册表里,WPS 启动时加载。功能上限最高,但你要先解决开发环境、注册表权限、签名、目标机器信任策略这一串问题,对个人开发者来说成本偏高,不适合“先跑起来”的阶段。
Python 独立进程是很多熟手喜欢偷懒的方案:用 pywin32 或通过 WPS 的 COM 接口控制文档,AI 调用放在 Python 里。本质上你的 WPS 插件是一个外部控制程序,用户得先启动一个后台服务,部署时还要解决 Python 环境的依赖。团队内部自用可以,给外部交付会被运维骂。
真正省事的是 WPS 自己带的那套 JS 宏(对应热词里一堆人在搜的“wps js宏”)。WPS 的桌面版内置了 JavaScript 运行时,开发工具菜单里可以直接打开 JS 宏编辑器,新建脚本、选中函数、运行,全程不编译。JS 宏内部能访问 Application.ActiveDocument 这类文档对象,也能发 XMLHttpRequest 请求。对“用 DeepSeekAPI 做智能办公插件”这个目标来说,它几乎是零门槛。
| 集成方式 | 语言 | HTTP 请求 | 分发难度 | 适合场景 |
|---|---|---|---|---|
| VBA | VB | 需 MSXML2 组件 | 中,依赖 VBA 组件 | 老宏兼容、临时脚本 |
| JS 宏 | JavaScript | XMLHttpRequest 可用 | 低,WPS 自带 | 个人提效、团队小范围共享 |
| COM 加载项 | C#/C++ | 随便 | 高,注册表+签名 | 商业级插件、深度 UI 集成 |
| Python 独立进程 | Python | requests | 高,要部署服务 | 数据管线、复杂业务逻辑 |
JS 宏不是没有缺点,它对 UI 的支持比较弱,画不出漂亮的侧边栏。但在“读选区、调 API、写回文档”这条主链路上,它提供的阻力是最小的。先把这条链路跑通,再谈加载项的事。
2.2 DeepSeekAPI 接入 WPS 的核心参数:接口、模型与三个必调参数
DeepSeekAPI 用的是 OpenAI 兼容的接口格式,所以请求体长得很眼熟,不需要额外引 SDK。在 WPS 的 JS 宏里发请求,地址是 https://api.deepseek.com/chat/completions,模型名用“deepseek-chat”。
办公场景下真正要调的参数并不多,新手不要被接口文档里的二十多个字段吓到,只调下面这几个就够。
temperature 控制随机性,0 到 2 之间。作改写和润色时我一般用 0.3 到 0.5,靠近 0 则保守,输出稳定;作头脑风暴时放到 0.8 以上,让模型多给几个方向。总结类任务建议 0.2 到 0.3,防止模型自己加戏。
max_tokens 控制返回的最大长度。很多人的误区是给到 4000 不嫌多,实际办公场景里,一段改写 500 token 足够,一份纪要摘要 800 也够。给的太大会让等待时间变长,而且额度消耗翻倍。做插件时还应该在代码里判断这个值,防止某次手滑选中整本书导致请求体爆炸。
stream 参数在宏环境里尽量关掉。流式输出在浏览器里很好用,但在 WPS 宏里处理流式响应要反复解析缓冲区,状态栏刷新也跟不上,经常搞得像死机。发出去直接等完整 JSON 返回,体验反而稳。
还有 top_p,新版接口里建议直接忽略。文档里说它和 temperature 互斥调优,真实工作中你两三个参数都调不过来,不会去碰它。保留默认值即可。
2.3 最小验证脚本:在 WPS JS 宏里发送一次 DeepSeekAPI 请求
打开一个 WPS 文字文档,进入“开发工具 -> JS 宏”,新建一个脚本,把下面这段整体粘进去,在宏列表里选择 callDeepSeek 运行。
function callDeepSeek() { var url = "https://api.deepseek.com/chat/completions"; var apiKey = "sk-在这里换成你的Key"; var body = { model: "deepseek-chat", messages: [ { role: "system", content: "你是WPS里的智能助手,回答要简短。" }, { role: "user", content: "用一句话介绍WPS JS宏。" } ], temperature: 0.7, max_tokens: 500 }; var xhr = new XMLHttpRequest(); xhr.open("POST", url, false); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Authorization", "Bearer " + apiKey); xhr.send(JSON.stringify(body)); if (xhr.status === 200) { var resp = JSON.parse(xhr.responseText); var content = resp.choices[0].message.content; MsgBox("DeepSeek返回:" + content); } else { MsgBox("接口状态" + xhr.status + ":" + xhr.responseText); } }这段代码做了三件事:拼接标准 OpenAI 兼容请求体、用 XMLHttpRequest 同步发送、把返回内容弹出来。同步模式是刻意为之的,WPS 宏环境的生命周期不像浏览器那样有稳定的异步事件循环,异步回调里操作文档对象容易踩到对象已释放的坑。xhr.status 判断很重要,常见值是 401(Key无效)、429(限流或余额不足)、400(body 参数格式有问题)。看到非 200 时先把 responseText 弹出来,绝大多数问题一眼能看出来。
注意运行位置。当前文档如果是 WPS 表格,Application.ActiveDocument 不存在,这段代码跑起来会报空对象错误。验证时请打开“WPS 文字”类型的文档环境。
3. 做第一个智能功能:改写选中文本并写回文档
最小脚本只是把一句话发给模型,离“插件”还差得远。真正的智能办公插件至少要能回答:选中了什么、做了什么、结果写回哪里。本章以“改写选中文本”这个最常见的需求为例,把它做成一条闭环。
3.1 读取 WPS 里的选中内容:Range.Text 够用,但跨段落会截断
在 JS 宏里读选中内容,表面上一行代码:取当前文档的 Selection 对象,再取它的 Range.Text。实际使用中,跨段落选中一段五号字文本,Range.Text 有时只拿回第一段,后面全丢。这跟 WPS 底层对 Range 的实现有关,宏拿到的不一定是可视化选区,而是逻辑选区的一部分。
我一般会顺手做一个保险函数,用 Paragraphs 集合遍历拼接,保证跨段落内容不丢:
function readSelectionText() { var doc = Application.ActiveDocument; if (doc == null) { MsgBox("请先打开一个WPS文字文档"); return ""; } var sel = doc.Selection; var range = sel.Range; var text = range.Text; // 跨段落时粗暴取值容易截断,改用段落集合补齐 if (text.indexOf("\r") > -1 || text.length < 2) { var parts = []; for (var i = 1; i <= sel.Paragraphs.Count; i++) { parts.push(sel.Paragraphs.Item(i).Range.Text); } text = parts.join("\n"); } return text; }这个函数判定逻辑很直白:如果 Range.Text 里已经能看到回车符,说明至少包含两个段落,那就用段落集合重新拼一遍。拼出来的文本把段落标记统一换成 \n,后续发给模型时干净很多。注意 WPS JS 宏里的对象模型和 VBA 有一个共同的脾气:集合的下标从 1 开始,不是从 0 开始,写循环时别按浏览器习惯写成 Item(0),否则会报下标越界。
还有一个容易忽略的问题:文档里如果混入大量高亮、批注、书签,Selection.Text 读出来也会夹带特殊标记。对纯文案场景影响不大,但如果读到奇怪字符,先检查文档里有没有域代码和批注引用。
3.2 把 DeepSeek 的返回内容写回文档:先格式化,再替换
把模型返回的内容塞回选区,新手最常见的翻车操作是直接 range.Text = newText。这在简单场景能跑,但会吞掉选区原有的字体、字号和段落格式,而且一旦 newText 里有换行符或制表符,WPS 会按自己的规则重新断段。
正确的姿势是先构造好要写入的字符串,再替换,同时做好可撤销处理。推荐用下面的写回函数:
function writeBackText(newText) { var doc = Application.ActiveDocument; if (doc == null || newText.length < 1) return; var sel = doc.Selection; var range = sel.Range; // 清理模型输出里可能的 Markdown 痕迹 newText = newText.replace(/#{1,6}\s*/g, ""); newText = newText.replace(/\*\*(.*?)\*\*/g, "$1"); newText = newText.replace(/\n{3,}/g, "\n\n"); range.Text = newText; range.Font.Bold = false; }为什么先做清理再写回?DeepSeekAPI 在默认 prompt 下喜欢给结果加标题、加粗、列表符号。直接写回文档,用户会看到一整片 # 和 * 号,观感极差。上面这段正则把常见 Markdown 标题和加粗标记剥掉,再压缩多余空行,输出就已经接近干净的办公文本了。
写回之后还有一个细节:range.Font.Bold = false 是为了防止模型输出的某个片段带 ** 被清洗后残留加粗属性。你把它去掉也能用,但实际操作中遇到过选区原有文字是加粗、写回后整体变粗的问题,这一行能兜底。
如果你想保留用户选区的原始格式,不做整体替换,可以这样:先在选区末尾插入新文本,再反向删除旧文本。顺序不能反,反了会把新内容一起删掉。
3.3 prompt 怎么拼:系统角色与“格式约束”决定办公效果
同样的接口、同样的参数,prompt 措辞不同,WPS 里看到的结果完全不同。办公插件里最大的坑不是模型不懂,而是模型把结果写得太“AI”。
我常用的系统角色模板是这样的:
var systemPrompt = "你是嵌入在WPS文字里的写作助手。" + "用户会给出一段选中的文字,请改写它。" + "要求:1. 保持原意;2. 去掉口语和重复;3. 长度尽量与原段一致;4. 只输出改写后的正文,不要加任何解释、标题、列表符号。";最后一条“只输出改写后的正文”是关键。很多模型没加这条时,会输出“好的,以下是改写后的内容:”,然后把正文包在一段客套话里。写回文档时这些客套话全会被写进去,用户看到的第一反应就是“插件不行”。
user 消息部分直接传选中的原文,不要自作主张加“请帮我改进一下这段文字:”,这句可以留在 system 里。有的场景希望模型保留原文中的关键数字和专有名词,这种约束也要写进 system,否则模型会擅自做同义替换。比如“英伟达”被改成“NVIDIA”、“成本占比 37%”被改成“接近四成”,专业文档里这是不可接受的。
| prompt 写法 | 效果 |
|---|---|
| “帮我把这段话改一下” | 输出充满寒暄,格式随机 |
| “保持原意,用办公书面语改写,保留所有数字,只输出正文” | 输出干净,可直接写入文档 |
给深度集成做 prompt 时,建议把 system 和 user 拆成两个变量,将来要支持“总结”“扩写”等多种功能时,只需要切换 system 文本,读选区和写回逻辑完全复用。
4. 深度集成避坑:WPS JS 宏调 DeepSeekAPI 的 5 个翻车现场
这一章是血泪经验的总和。WPS JS 宏看着像浏览器,实际上是一个能力受限的宿主环境。你在这边发的每个请求、写的每行文档操作,都可能踩到环境特有的坑。
4.1 同步请求后界面假死:超时与重试是玄学
现象:宏运行后,WPS 整个窗口卡住,鼠标转圈,几分钟没反应,只能从任务管理器强杀进程。
原因:XMLHttpRequest 同步模式会阻塞 UI 线程,网络延迟越高卡得越久。如果 DeepSeekAPI 返回慢,或你的网络对 api.deepseek.com 握手有延迟,WPS 就表现成“死机”。很多人以为是宏写错了,其实只是请求没回来。
解决:给 xhr 加超时控制,并在超时后做友好提示。
xhr.timeout = 30000; xhr.ontimeout = function () { MsgBox("请求超时,请确认网络状态后重试"); };30 秒是办公场景的折中值。改稿任务通常 10 秒内返回,摘要长文档可能接近 20 秒。设置 30 秒能覆盖绝大多数情况,又不至于让用户等太久。再加一层保险:同步请求前先把要处理的文本长度打印到状态栏,text.length 超过 6000 字时主动提示可能超时。
4.2 fetch 不可用或不稳定:退回 XMLHttpRequest
现象:网上很多教程用 fetch 写请求,粘到 WPS JS 宏里直接报“fetch is not defined”,或者明明定义了,跑到一半回调不执行。
原因:WPS 的 JS 宏宿主版本不一。新内核预览版支持标准 fetch,稳定版内置的运行时可能只暴露 XMLHttpRequest。团队办公电脑里的 WPS 大多不追求最新版,fetch 可用性纯看运气。
解决:统一用 XMLHttpRequest,别赌环境。它的兼容性在 WPS JS 宏里是最稳的。有些版本对同步模式有限制,会抛“同步 XHR 不可用”的警告,此时把 xhr.open 里的第三个参数改成 true,再用 onreadystatechange 接收结果,但记得把文档操作挪进回调里做,否则对象会提前释放。这是一条我已经踩实的经验:能同步就同步,不能同步就老老实实在回调里处理,千万别用 setTimeout 轮询 response。
4.3 返回的 Markdown 在 WPS 里显示成乱码
现象:模型输出正常,接口调用正常,MsgBox 里看字符串也没毛病,但写入文档后到处都是 # 号、星号和横线,像一封失真的邮件。
原因:DeepSeekAPI 默认返回的是 Markdown 格式文本,WPS 不会帮你渲染,它只会原样把字符写入文档。你在代码编辑器里看得很舒服的排版,落到 WPS 里就是噪音。
解决:写回前做一次 Markdown 轻清洗,也就是前面 3.2 那套正则。注意这套清洗只是“够用”,不是完整解析器。真遇到模型返回表格或代码块,清洗就不够了,这时应该在 prompt 层拦住:明确要求“禁止输出 Markdown 表格、代码块、标题符号”。与其事后解析,不如事前约束,prompt 能解决的问题不要让代码去扛。
4.4 写回时遇到 WPS 报错 75:保护区域和多窗口的锅
现象:替换选区文本时,宏弹出“wps报错75”或“下标越界”,代码明明在读取时还好好的。
原因:报错 75 在 WPS/VBA 体系里通常跟路径访问有关,但 JS 宏里遇到它,九成是文档区域受保护,或当前焦点不在文档正文而在页眉、批注框。另一个隐蔽触发点是用户开了多个文档窗口,Selection 对象拿到的是旧窗口的引用。
解决:写回前检查当前文档是否可编辑。
if (doc.ProtectedForForms || doc.ReadOnly) { MsgBox("当前文档受保护,无法写回"); return; }同时建议在宏开头固定活动文档,而不是反复让用户保证焦点正确。多窗口场景用 Documents.Item(1) 这类显式引用并不靠谱,用户开的窗口顺序经常变。最稳的办法是取 Application.ActiveDocument,在宏启动时立刻获取,不要跨多个操作窗口后再取。
4.5 API Key 硬编码在宏文件里
现象:脚本写完能跑,过几天同事要了一份宏文件,你把这段代码发过去了。对方打开代码一眼就看到 sk- 开头的明文 Key,于是它出现在聊天记录里、共享文档里,甚至被搜索引擎索引。
原因:JS 宏脚本本质是明文 JavaScript,写在代码里的 Key 没有任何保护。
解决:把 Key 单独放到一个本地配置文件里,脚本运行时读取。
var fso = new ActiveXObject("Scripting.FileSystemObject"); var keyFile = "D:/wps-key/deepseek.key"; var apiKey = ""; if (fso.FileExists(keyFile)) { var tf = fso.OpenTextFile(keyFile, 1, false, -1); apiKey = tf.ReadLine(); tf.Close(); }如果你们单位的宏环境禁止创建 ActiveXObject,备选方案是让 key 通过接口读取,或每次启动时手工输入一次存到全局变量。无论如何,别把明文 Key 写在一个会被转发的 .js 文件里。这个坑翻车率极高,而且翻的是数据安全的车。
5. 从宏脚本升级到正式插件:加载项结构与团队分发
前面的内容能让你在本机用得很爽,但“深度集成”意味着不止一个人用、不止一个环境跑。JS 宏脚本的价值在个人自动化,一旦要交给同事、覆盖部门级场景,就需要把它从“宏列表里的一个函数”变成真正的插件形态。
5.1 为什么“能用”的宏不等于“可交付”的插件
宏脚本交付最大的痛点是入口太生硬:用户要自己打开开发工具、进 JS 宏界面、找到函数再运行。对非技术同事来说这一步已经劝退一半人。另一个痛点是更新:你改了一个 prompt,要把新的 .js 文件挨个发给所有人,还得保证对方复制到正确目录。
加载项(WPS Add-in)正是解决这两个痛点而存在的形态。它可以带自己的界面入口,常驻在右边栏或工具栏里;脚本以包的形式分发,更新时替换整个包即可。把第 3 章的 callDeepSeek 函数逻辑平移到加载项工程里,本质没有变,变的只是外层包装。如果你只需要在十人以内的小团队内网用,不一定要走完整的签名发布流程,用加载项开发模式挂载未打包目录就能跑。
5.2 最简加载项的文件结构:manifest 与入口脚本
WPS 加载项本质是一个按规则打包的目录,里面至少包含一个描述插件信息的 manifest 文件和一个入口脚本。以目前常见的结构为例,目录大致长这样:
| 文件 | 作用 |
|---|---|
| manifest.xml | 插件名、版本、入口文件路径等元数据 |
| index.js | 真正的逻辑代码,调用 DeepSeekAPI 和处理文档 |
| config.json | 模型参数、API 地址、Key 读取路径 |
| icon.png | 可选,功能区或侧边栏图标 |
manifest.xml 的骨架写法如下,字段名不同版本略有差异,以开发文档当前示例为准,但结构思想一致:
<?xml version="1.0" encoding="UTF-8"?> <manifest> <plugin> <name>DeepSeek Office Assistant</name> <version>1.0.0</version> <id>com.example.deepseek-assistant</id> <type>wps</type> <runtime>js</runtime> <main>index.js</main> <description>基于DeepSeekAPI的WPS智能办公插件</description> </plugin> </manifest>这个文件的核心是 main 字段,它告诉 WPS 启动后去加载哪个 JavaScript 文件。index.js 的职责很纯粹:把第 3 章里 readSelectionText、writeBackText、callDeepSeek 三件事收拢成一个入口函数。加载项的 UI 部分各版本接口名不统一,我不在这里贴易变的 SDK 代码,拿到开发文档里当前版本的 hello 示例,把“改写选中内容”函数接到按钮点击事件上即可。底层的三件套逻辑是通用的,可以直接搬。
5.3 安装、验证与分发:三种落地方式
加载项建好后,第一步永远是本地验证。WPS 里打开开发者模式,用“从文件夹加载”方式挂载整个目录,然后新建文档测试。注意挂载后要完全关闭 WPS 再重开,部分版本不会热加载 manifest 变更,不重启就看不到入口。
验证通过后,按场景选分发路径。个人自己用,直接把加载项目录挂到 WPS 的固定加载路径下;小团队共享,把整个目录打成压缩包放到共享盘,同事下载后同样走“从文件夹加载”;需要正式覆盖整个部门时,再把目录按官方规范打成插件包,交给 IT 做统一推送。这里要留个醒:不要直接压缩成 zip 改名,加载项包对内部文件布局有特定要求,格式不对会直接加载失败。
如果暂时不想学加载项包的构建,还有一个过渡方案:把写好的宏模板(带宏的 WPS 文档)放到共享盘,让同事用“文件 -> 打开 -> 模板”的方式进来运行。这个方案不优雅,但胜在能立刻让队友用上 AI 能力,等流程跑顺再升级成加载项。
6. 再往前走一步:验证输出质量与参数产品化
插件跑通只代表请求通路没问题,不代表输出质量稳定。办公场景最怕模型每次都给你不同的措辞,昨天给同事的改写结果是一种风格,今天又是另一种。所以插件上线前,建议先做一组回归验证。
准备 5 到 10 个固定样本:一段官方文件措辞、一段口语记录、一段带数字的技术描述、一段含表格碎片的长文本。写一个批处理宏,把每个样本分别发给 DeepSeekAPI,记录返回文本。重点看三条:长度漂移是否超过 20%,关键数字和专有名词有没有被改写,输出里有没有混入解释性话语。这几条不满意就先调 temperature 和 system prompt,参数打架时优先改 prompt 而不是改 temperature。
场景扩展也顺着这条通路走。做表格公式生成时,把选中区域的第一行表头拼进 user 消息,让模型返回 Excel 公式;遇到“html格式转换wps表格”这类高频需求,不要让模型返回 HTML,改成返回 TSV 格式的纯文本,再在宏里按 \t 切分写入单元格,比解析 HTML 标签可靠得多。批量摘要则要注意限流,DeepSeekAPI 对并发有限制,循环调用时每轮加一秒间隔,避免中途撞上 429。
到这个阶段,插件已经不再是一个脚本,而是一套从“读取文档”到“约束模型行为”再到“写回与验证”的完整链路。我会把 temperature、max_tokens、接口地址、模型名都挪进 config.json,换模型或调参数时不必改代码,业务同事也能接管这个文件。这是我从“写死参数的脚本”到“可维护工具”之间的那个坎,跨过去之后,后续加功能的速度快很多。
前阵子帮同事做一个批量归档功能,我图省事把 Key 写死在宏里,结果共享出去的瞬间就后悔了。现在我的习惯是任何对接外部 API 的 WPS 脚本,第一行就去读配置文件,宁可多写十行代码,不让密钥出现在能被旁人看见的地方。希望这些踩过的坑能让你少走一段弯路,也希望这套从宏到加载项的路径,能帮你的文档工作流真正省下时间。
本文还有配套的精品资源,点击获取