1. 为什么要在 VS Code 里自己写注释校验插件
团队里最容易被忽略、又最容易在 Code Review 时被反复提起的,就是 JavaScript/ES6 的注释规范。函数有没有 JSDoc、参数有没有写@param、返回值有没有@returns、文件头有没有作者和创建时间,这些规则写在文档里没人看,靠人肉检查又累又容易漏。VS Code 里现成的方案是 koroFileHeader 负责生成文件头和函数注释,JavaScript (ES6) code snippets 负责快速补全箭头函数和常用语法,但它们一个偏「生成」、一个偏「补全」,真正做「校验」这一步往往还是空的。
我想要的链路是:写完一个函数,保存文件的瞬间,插件自动把这段代码发给模型,让模型按团队规范判断注释是否合格,不合格就在编辑器里给出提示。问题在于,如果每个插件都自己维护一套模型调用逻辑,Key 散落在各个插件的配置里,换一次通道就要改一堆地方。所以这篇的做法是:把模型调用统一收敛到 TaoToken 的 Key 和 API 通道上,插件只负责「取代码 → 发请求 → 展示结果」,剩下的鉴权和模型路由交给统一入口。
这篇适合两类人:一是想给自己团队做一套轻量注释校验插件的 VS Code 插件开发者,二是已经在用 koroFileHeader、ES6 snippets,但想再加一层 AI 校验的前端同学。下面会从插件工程结构讲起,给出可复制的settings.json配置骨架,再走一遍本地端到端验证,最后把常见的报错逐个排掉。全程不需要你懂模型部署,只要会写 JavaScript 和一点 VS Code 插件 API 就能跟下来。
2. TaoToken 前置:统一 Key 与 API 通道
插件要调模型,第一步是拿到一个能用的 Key 和稳定的 API 地址。TaoToken 在这里扮演的角色是「统一入口」:你只需要在它那边生成一个 Key,插件里所有模型请求都走同一个baseURL,不用为不同模型分别配置。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册后在控制台生成 Key 即可。
具体操作路径是这样的:先打开控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,在 API Keys 页面创建一个新 Key,复制出来先存到本地环境变量里,别直接写进代码。API 的基础地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,插件里拼接时用baseURL + '/v1/chat/completions'这种标准 OpenAI 兼容格式就行。
注意:Key 属于敏感信息,插件里读取时优先走
process.env或 VS Code 的 SecretStorage,不要硬编码在package.json或源码里,否则一旦仓库公开就泄露了。
如果你只是想先验证模型能不能通,可以先用模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 手动发一条消息,确认 Key 有效、通道正常,再回到插件里写代码。这一步能帮你把「Key 问题」和「插件代码问题」提前分开,省掉后面大量排查时间。
3. 可复制配置:settings.json 骨架与插件调用
3.1 settings.json 配置骨架
先在 VS Code 的用户或工作区settings.json里加上插件需要的配置项。下面这段可以直接复制,把apiKey换成你自己的,或者留空走环境变量:
{ "commentLinter.enabled": true, "commentLinter.baseURL": "https://taotoken.net/api", "commentLinter.apiKey": "", "commentLinter.model": "gpt-4o-mini", "commentLinter.rules": { "requireFileHeader": true, "requireFunctionComment": true, "requireParamTag": true, "requireReturnTag": true }, "commentLinter.debounceMs": 800, "commentLinter.maxCodeLength": 4000 }几个参数说明一下:baseURL固定指向 TaoToken 的 API 地址;apiKey留空时插件会去读TAOTOKEN_API_KEY环境变量;debounceMs是保存后延迟多久触发校验,避免频繁请求;maxCodeLength限制单次发送的代码长度,防止大文件把请求撑爆。rules里就是团队注释规范的具体开关,按需增减。
3.2 插件主逻辑:取代码、发请求、展示结果
插件入口在extension.js,核心是监听保存事件,拿到当前文档内容,拼一个 prompt 发给 TaoToken,再把返回结果用DiagnosticCollection标出来。下面是最小可运行版本:
const vscode = require('vscode'); function activate(context) { const diagnostics = vscode.languages.createDiagnosticCollection('commentLinter'); const config = () => vscode.workspace.getConfiguration('commentLinter'); const disposable = vscode.workspace.onDidSaveTextDocument(async (doc) => { if (doc.languageId !== 'javascript') return; if (!config().get('enabled')) return; const code = doc.getText().slice(0, config().get('maxCodeLength')); const apiKey = config().get('apiKey') || process.env.TAOTOKEN_API_KEY; if (!apiKey) { vscode.window.showWarningMessage('未配置 TaoToken API Key'); return; } const prompt = buildPrompt(code, config().get('rules')); const result = await callModel(apiKey, config().get('baseURL'), config().get('model'), prompt); renderDiagnostics(diagnostics, doc, result); }); context.subscriptions.push(disposable, diagnostics); } function buildPrompt(code, rules) { return [ '你是 JavaScript/ES6 注释规范检查器。', '按以下规则检查代码注释,只输出 JSON 数组,每项包含 line、message。', `规则:${JSON.stringify(rules)}`, '代码:', code ].join('\n'); } async function callModel(apiKey, baseURL, model, prompt) { const res = await fetch(`${baseURL}/v1/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }], temperature: 0 }) }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); return JSON.parse(data.choices[0].message.content); } function renderDiagnostics(collection, doc, issues) { const diags = issues.map((it) => { const line = Math.max(0, it.line - 1); const range = new vscode.Range(line, 0, line, 200); return new vscode.Diagnostic(range, it.message, vscode.DiagnosticSeverity.Warning); }); collection.set(doc.uri, diags); } module.exports = { activate };这段代码里有两个关键点:一是temperature: 0,让模型输出尽量稳定,方便你解析 JSON;二是 prompt 里明确要求「只输出 JSON 数组」,否则模型可能加一堆解释文字,JSON.parse直接报错。renderDiagnostics把行号转成 VS Code 的 Range,问题就会以波浪线形式出现在编辑器里。
3.3 package.json 里的配置声明
别忘了在package.json的contributes.configuration里声明这些配置项,否则getConfiguration读不到:
{ "contributes": { "configuration": { "title": "Comment Linter", "properties": { "commentLinter.enabled": { "type": "boolean", "default": true }, "commentLinter.baseURL": { "type": "string", "default": "https://taotoken.net/api" }, "commentLinter.apiKey": { "type": "string", "default": "" }, "commentLinter.model": { "type": "string", "default": "gpt-4o-mini" }, "commentLinter.debounceMs": { "type": "number", "default": 800 }, "commentLinter.maxCodeLength": { "type": "number", "default": 4000 } } } } }4. 验证请求:本地端到端跑通
配置写完,先别急着按 F5 调试插件,用一段独立脚本验证 TaoToken 通道是否通。新建test-call.js:
const apiKey = process.env.TAOTOKEN_API_KEY; async function main() { const res = await fetch('https://taotoken.net/api/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: '回复 ok 两个字母' }], temperature: 0 }) }); console.log('status:', res.status); const data = await res.json(); console.log('content:', data.choices?.[0]?.message?.content); } main().catch((e) => console.error('failed:', e.message));在终端里设置环境变量后运行:
export TAOTOKEN_API_KEY=你的Key node test-call.js成功的话会看到status: 200和content: ok。这一步通了,说明 Key、baseURL、模型名三者都对。接着按 F5 启动插件调试窗口,打开一个.js文件,故意写一个没有 JSDoc 的函数:
function add(a, b) { return a + b; }保存后,如果配置正确,编辑器里应该在这个函数上方出现黄色波浪线,悬停能看到类似「缺少函数注释」的提示。这就是端到端跑通的标志:保存触发 → 请求发出 → 模型返回 → 诊断渲染。
提示:第一次跑建议把
debounceMs调大一点,比如 1500,给自己留出观察日志的时间。插件调试窗口的「输出」面板里可以加console.log看请求体和返回体。
5. 本篇常见错排查
5.1 401 / 403:Key 没读到或格式不对
最常见的是apiKey为空。检查顺序:先确认settings.json里填了 Key,或者终端里echo $TAOTOKEN_API_KEY有输出;再确认请求头是Authorization: Bearer xxx,中间有一个空格,少了空格会直接 401。如果 Key 是从控制台复制的,注意别把首尾空格带进去。
5.2 404:baseURL 拼错
baseURL应该是https://taotoken.net/api,请求路径是/v1/chat/completions。如果你把baseURL写成https://taotoken.net/api/v1,再拼/v1/chat/completions就变成/api/v1/v1/...,直接 404。统一按「baseURL 不带版本号」来配,能避免这类问题。
5.3 JSON.parse 报错:模型返回了非 JSON
模型有时会在 JSON 外面包一层 ```json 代码块,或者加一句「以下是检查结果」。解决办法是在 prompt 里强调「只输出 JSON 数组,不要任何解释和代码块标记」,解析前先做一次清洗:
function safeParse(text) { const cleaned = text.replace(/```json|```/g, '').trim(); return JSON.parse(cleaned); }5.4 诊断不显示:Range 越界或 collection 没 set
如果模型返回的line超过了文件实际行数,new vscode.Range会抛异常,整个渲染就断了。加一层保护:const line = Math.min(it.line - 1, doc.lineCount - 1)。另外确认diagnostics.set(doc.uri, diags)里的doc.uri和保存的文档是同一个,跨文件时容易搞混。
5.5 请求太频繁:debounce 没生效
onDidSaveTextDocument本身是保存才触发,但如果你的插件还监听了onDidChangeTextDocument,就会每次输入都发请求。检查一下是不是多注册了监听器,或者把debounceMs用setTimeout真正实现一遍防抖。
6. 把校验链路接到你的日常编码里
到这里,一个能跑的注释校验插件就成型了:VS Code 保存 JavaScript 文件 → 插件取代码 → 走 TaoToken 统一 Key 和 API 通道 → 模型按规则返回问题行 → 编辑器波浪线提示。整个过程你只需要维护一个 Key 和一个baseURL,后面想换模型、加规则,改配置就行,不用动插件核心逻辑。
如果你后面想把这条链路扩展到更重的场景,比如让模型直接参与代码补全、批量重构注释,可以了解一下 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,它更适合长期编码和 Agent 类任务。接入过程中如果遇到鉴权或路径问题,接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 里有完整的接口说明,配合 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 一起看,基本能覆盖从建 Key 到发请求的全流程。
最后留一个我踩过的坑:插件调试时改了settings.json不生效,多半是工作区配置覆盖了用户配置,用Ctrl+Shift+P打开「首选项:打开工作区设置」确认一下优先级。把这条链路跑顺之后,你会发现注释规范这件事终于不用靠人盯了。