GLM-4-9B-Chat-1M在VSCode中的智能编程助手插件开发
GLM-4-9B-Chat-1M在VSCode中的智能编程助手插件开发
想象一下,你正在写一段复杂的业务逻辑代码,突然卡在一个函数设计上,或者不确定某个库的用法。这时候,如果有个懂行的伙伴坐在旁边,看一眼你的代码,就能给出精准的建议,甚至帮你把代码补全,那该多省心。
现在,这个“伙伴”可以随时待命在你的VSCode里了。今天我们就来聊聊,如何把GLM-4-9B-Chat-1M这个拥有百万上下文能力的“代码专家”,变成一个真正能帮你干活的VSCode智能编程助手。
1. 为什么是GLM-4-9B-Chat-1M?
你可能听说过很多大模型,但GLM-4-9B-Chat-1M在编程助手这个场景下,有几个特别实在的优势。
首先,它支持100万token的上下文长度。这是什么概念?差不多相当于200万个中文字符,或者几十个完整的代码文件。这意味着你的插件可以一次性把整个项目的主要文件都“喂”给模型,让它对你的代码库有全局的理解。当你问“这个函数在哪儿被调用了?”或者“这个模块的接口是什么?”时,它不会只盯着当前这几行代码瞎猜。
其次,9B的参数规模,在保证不错推理能力的同时,对硬件的要求相对友好。用一张消费级的显卡(比如RTX 3090/4090)就能跑起来,这让个人开发者或者小团队自己部署成为可能,不用总依赖云端API,既省钱又不用担心代码隐私泄露。
最后,它原生支持代码执行和工具调用。这意味着你开发的插件不仅能聊天、补全代码,未来还能扩展出更高级的功能,比如让模型分析代码后直接运行单元测试,或者调用外部工具来格式化代码、检查语法。
说白了,选它就是为了做一个更懂你项目、更私有安全、潜力更大的编程助手。
2. 插件核心功能设计
一个光会聊天的模型放在编辑器里,用处不大。我们的目标是让它成为开发工作流的一部分。我建议先从下面几个最能直接提升效率的功能做起。
2.1 智能代码补全与生成
这不是简单的按Tab键补全一个变量名。而是基于你正在写的代码上下文和整个项目的语义,生成整块有意义的代码。
比如,你刚定义了一个User类,有name和email属性。当你开始输入def get_的时候,插件能提示def get_user_info(self):,甚至直接把函数体return {"name": self.name, "email": self.email}都给你生成好。它理解你的意图是创建一个获取用户信息的方法。
实现上,我们需要监听编辑器的文件变化和光标位置,把当前文件的内容、以及相关依赖文件的部分内容作为上下文,发送给模型,请求它生成接下来的几行代码。
2.2 代码解释与文档生成
读别人(或者几个月前自己写的)代码时,最头疼的就是看不懂某段复杂逻辑是干嘛的。选中这段代码,右键点击“解释这段代码”,插件就能用大白话告诉你:“这段代码是在处理用户登录请求,先验证密码,然后检查账户是否被锁定,最后生成一个访问令牌。”
反过来,写完一个函数后,一键“生成文档字符串”,它就能根据函数名、参数和代码逻辑,自动写出规范的docstring,省去你不少机械劳动。
2.3 错误检测与重构建议
比静态检查工具更智能一点。它不仅能告诉你语法错误,还能从逻辑和设计模式层面给出建议。
例如,它可能提示你:“这个循环里每次都在重复计算同一个值,建议提到循环外面来提升性能。”或者“这个类的职责似乎过于复杂,考虑拆分成UserValidator和UserPersister两个类?”
2.4 自然语言对话与问答
这是最直接的人机交互方式。在侧边栏开一个聊天面板,你可以问:
- “项目里处理支付失败重试的逻辑在哪里?”
- “帮我写一个用
requests库发送POST请求并处理异常的例子。” - “
config.yaml里database.pool_size这个配置项是什么意思?”
模型可以结合被问及的代码文件内容来回答,做到“指哪打哪”。
3. 一步步搭建开发环境
理论说完了,我们动手把环境搭起来。这里假设你已经有基本的Python和Node.js开发环境。
3.1 模型服务端部署
首先,我们需要一个地方来运行GLM-4-9B-Chat-1M模型,并提供API给VSCode插件调用。为了简单高效,我推荐使用vLLM作为推理后端。
# 创建一个新的项目目录
mkdir glm4-vscode-assistant && cd glm4-vscode-assistant
# 创建模型服务端目录
mkdir model_server && cd model_server
# 建议使用Python虚拟环境
python -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# 安装核心依赖
pip install vllm transformers torch
接下来,创建一个启动脚本server.py:
# server.py
from vllm import LLM, SamplingParams
from transformers import AutoTokenizer
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
from typing import List, Optional
# 定义API请求模型
class ChatMessage(BaseModel):
role: str # "user" or "assistant"
content: str
class CompletionRequest(BaseModel):
messages: List[ChatMessage]
max_tokens: Optional[int] = 1024
temperature: Optional[float] = 0.7
# 初始化模型和tokenizer
print("正在加载GLM-4-9B-Chat-1M模型,这可能需要几分钟...")
model_name = "THUDM/glm-4-9b-chat-1m"
# 根据你的GPU显存调整参数。40G显存(如A100)可以尝试以下配置。
# 如果显存较小,请减小max_model_len或使用量化模型。
llm = LLM(
model=model_name,
tensor_parallel_size=1, # 单GPU
max_model_len=131072, # 初始可设为128K,后续根据需求调整
trust_remote_code=True,
enforce_eager=True, # 兼容性选项
)
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
print("模型加载完成!")
app = FastAPI(title="GLM-4 Code Assistant API")
@app.post("/v1/chat/completions")
async def create_chat_completion(request: CompletionRequest):
try:
# 将消息列表转换为模型所需的聊天模板格式
prompt_text = tokenizer.apply_chat_template(
[msg.dict() for msg in request.messages],
tokenize=False,
add_generation_prompt=True
)
# 设置生成参数
sampling_params = SamplingParams(
temperature=request.temperature,
max_tokens=request.max_tokens,
stop_token_ids=[151329, 151336, 151338] # GLM模型特定的停止符
)
# 生成回复
outputs = llm.generate([prompt_text], sampling_params)
generated_text = outputs[0].outputs[0].text
return {
"choices": [{
"message": {
"role": "assistant",
"content": generated_text.strip()
}
}]
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
# 启动服务,默认在本地8000端口
uvicorn.run(app, host="0.0.0.0", port=8000)
运行这个服务:
python server.py
看到“模型加载完成!”的日志后,你的本地模型API服务就启动了。你可以用curl测试一下:
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "用Python写一个快速排序函数。"}],
"max_tokens": 300
}'
3.2 VSCode插件项目初始化
模型服务跑起来了,现在我们来创建VSCode插件本体。打开一个新的终端窗口,回到项目根目录。
# 安装Yeoman和VSCode插件生成器
npm install -g yo generator-code
# 生成插件脚手架
yo code
在交互式命令行里,按照提示选择:
- 项目类型:
New Extension (TypeScript) - 名称:
glm4-code-assistant - 标识符:
glm4-code-assistant - 描述:
AI-powered code assistant using GLM-4-9B-Chat-1M - 是否初始化Git仓库:
Yes - 包管理器:
npm
完成后,进入插件目录并安装一个用于HTTP请求的库:
cd glm4-code-assistant
npm install axios
4. 实现核心插件功能
我们聚焦实现两个最常用的功能:代码补全和侧边栏聊天。打开src/extension.ts,这是插件的入口文件。
4.1 配置模型连接
首先,在package.json里添加配置项,让用户能设置自己模型服务的地址:
// 在 package.json 的 contributes.configuration 部分添加
"configuration": {
"title": "GLM-4 Assistant",
"properties": {
"glm4Assistant.modelEndpoint": {
"type": "string",
"default": "http://localhost:8000/v1/chat/completions",
"description": "GLM-4模型API的服务地址"
},
"glm4Assistant.apiKey": {
"type": "string",
"default": "",
"description": "API密钥(如需)"
}
}
}
4.2 实现智能代码补全
我们来创建一个代码补全提供器。在src目录下新建一个CompletionProvider.ts:
// src/CompletionProvider.ts
import * as vscode from 'vscode';
import axios from 'axios';
export class GLM4CompletionProvider implements vscode.CompletionItemProvider {
private getConfig() {
const config = vscode.workspace.getConfiguration('glm4Assistant');
return {
endpoint: config.get<string>('modelEndpoint', 'http://localhost:8000/v1/chat/completions'),
apiKey: config.get<string>('apiKey', '')
};
}
async provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
context: vscode.CompletionContext
): Promise<vscode.CompletionItem[]> {
// 获取当前行的文本和光标前的部分
const linePrefix = document.lineAt(position).text.substr(0, position.character);
// 简单判断:如果用户正在输入,且不是在写注释或字符串,则触发补全
if (linePrefix.trim().length === 0 || linePrefix.trim().startsWith('//') || linePrefix.trim().startsWith('#')) {
return [];
}
// 获取当前文件的前面一部分内容作为上下文(比如前50行)
const startLine = Math.max(0, position.line - 50);
const contextRange = new vscode.Range(
new vscode.Position(startLine, 0),
position
);
const codeContext = document.getText(contextRange);
const config = this.getConfig();
// 构建给模型的提示
const prompt = `你是一个专业的编程助手。请根据下面的代码上下文,预测并生成接下来最可能的一行或几行代码。只返回代码,不要解释。
代码上下文:
\`\`\`
${codeContext}
\`\`\`
当前光标位置在行末,用户刚输入了:“${linePrefix}”
请补全代码:`;
try {
const response = await axios.post(config.endpoint, {
messages: [{ role: "user", content: prompt }],
max_tokens: 100,
temperature: 0.3 // 低温度,让输出更确定
}, {
headers: config.apiKey ? { 'Authorization': `Bearer ${config.apiKey}` } : {},
timeout: 10000 // 10秒超时
});
const generatedCode = response.data.choices[0]?.message?.content?.trim();
if (!generatedCode) {
return [];
}
// 将模型返回的代码包装成补全项
const completionItem = new vscode.CompletionItem(generatedCode, vscode.CompletionItemKind.Snippet);
completionItem.detail = 'GLM-4 智能补全';
completionItem.documentation = new vscode.MarkdownString(`由GLM-4模型生成的代码补全建议。`);
// 设置插入文本,并确保插入后光标位置合理
completionItem.insertText = new vscode.SnippetString(generatedCode);
return [completionItem];
} catch (error) {
console.error('GLM-4补全请求失败:', error);
// 失败时静默返回,不干扰用户
return [];
}
}
}
4.3 实现侧边栏聊天视图
聊天面板能让用户自由提问。我们需要创建一个Webview。在src下创建ChatPanel.ts:
// src/ChatPanel.ts
import * as vscode from 'vscode';
import axios from 'axios';
export class ChatPanel {
public static currentPanel: ChatPanel | undefined;
private readonly _panel: vscode.WebviewPanel;
private _disposables: vscode.Disposable[] = [];
private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
this._panel = panel;
this._panel.webview.html = this._getHtmlForWebview();
// 处理来自Webview的消息
this._panel.webview.onDidReceiveMessage(
async (message) => {
switch (message.command) {
case 'sendMessage':
await this._handleUserMessage(message.text);
return;
}
},
null,
this._disposables
);
}
private _getHtmlForWebview(): string {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GLM-4 编程助手</title>
<style>
body { padding: 10px; font-family: var(--vscode-font-family); }
#chatContainer { display: flex; flex-direction: column; height: 90vh; }
#messages { flex-grow: 1; overflow-y: auto; margin-bottom: 10px; }
.message { margin-bottom: 15px; padding: 10px; border-radius: 5px; }
.user { background-color: var(--vscode-input-background); align-self: flex-end; }
.assistant { background-color: var(--vscode-editor-background); align-self: flex-start; }
#inputArea { display: flex; }
#messageInput { flex-grow: 1; margin-right: 10px; }
</style>
</head>
<body>
<div id="chatContainer">
<div id="messages"></div>
<div id="inputArea">
<input type="text" id="messageInput" placeholder="输入你的问题...">
<button id="sendButton">发送</button>
</div>
</div>
<script>
const vscode = acquireVsCodeApi();
const messagesDiv = document.getElementById('messages');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
function addMessage(text, isUser) {
const messageDiv = document.createElement('div');
messageDiv.className = 'message ' + (isUser ? 'user' : 'assistant');
messageDiv.textContent = (isUser ? '你: ' : '助手: ') + text;
messagesDiv.appendChild(messageDiv);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
sendButton.addEventListener('click', () => {
const text = messageInput.value.trim();
if (text) {
addMessage(text, true);
vscode.postMessage({ command: 'sendMessage', text: text });
messageInput.value = '';
}
});
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendButton.click();
}
});
// 监听来自扩展的消息(用于显示助手回复)
window.addEventListener('message', event => {
const message = event.data;
if (message.command === 'receiveMessage') {
addMessage(message.text, false);
}
});
</script>
</body>
</html>`;
}
private async _handleUserMessage(userMessage: string) {
const config = vscode.workspace.getConfiguration('glm4Assistant');
const endpoint = config.get<string>('modelEndpoint', 'http://localhost:8000/v1/chat/completions');
const apiKey = config.get<string>('apiKey', '');
try {
// 可以在这里获取当前活跃的编辑器内容,作为上下文附加到提示中
let codeContext = '';
const editor = vscode.window.activeTextEditor;
if (editor) {
const document = editor.document;
// 获取前100行代码作为参考
const lineCount = Math.min(100, document.lineCount);
const range = new vscode.Range(
new vscode.Position(0, 0),
new vscode.Position(lineCount, 0)
);
codeContext = document.getText(range);
}
const prompt = `你是一个集成在VSCode中的编程助手。用户的问题是:${userMessage}
${codeContext ? `\n当前打开的文件内容(部分)供参考:\n\`\`\`\n${codeContext}\n\`\`\`` : ''}
请用友好、专业的语气回答,专注于提供编程相关的帮助。`;
const response = await axios.post(endpoint, {
messages: [{ role: "user", content: prompt }],
max_tokens: 500,
temperature: 0.7
}, {
headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {},
timeout: 15000
});
const assistantReply = response.data.choices[0]?.message?.content;
if (assistantReply) {
this._panel.webview.postMessage({ command: 'receiveMessage', text: assistantReply });
}
} catch (error) {
console.error('聊天请求失败:', error);
this._panel.webview.postMessage({
command: 'receiveMessage',
text: '抱歉,请求模型服务时出错了。请检查模型服务是否运行,以及网络连接。'
});
}
}
public static createOrShow(extensionUri: vscode.Uri) {
const column = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;
if (ChatPanel.currentPanel) {
ChatPanel.currentPanel._panel.reveal(column);
return;
}
const panel = vscode.window.createWebviewPanel(
'glm4Chat',
'GLM-4 编程助手',
column || vscode.ViewColumn.Two,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
ChatPanel.currentPanel = new ChatPanel(panel, extensionUri);
}
public dispose() {
ChatPanel.currentPanel = undefined;
this._panel.dispose();
while (this._disposables.length) {
const x = this._disposables.pop();
if (x) { x.dispose(); }
}
}
}
4.4 整合功能到主扩展
最后,修改src/extension.ts,把上面两个功能注册进去:
// src/extension.ts
import * as vscode from 'vscode';
import { GLM4CompletionProvider } from './CompletionProvider';
import { ChatPanel } from './ChatPanel';
export function activate(context: vscode.ExtensionContext) {
console.log('GLM-4 Code Assistant 插件已激活');
// 注册代码补全提供器
const completionProvider = new GLM4CompletionProvider();
const languages = ['python', 'javascript', 'typescript', 'java', 'go', 'cpp'];
languages.forEach(lang => {
const selector = { scheme: 'file', language: lang };
const provider = vscode.languages.registerCompletionItemProvider(selector, completionProvider);
context.subscriptions.push(provider);
});
// 注册打开聊天面板的命令
const chatCommand = vscode.commands.registerCommand('glm4-assistant.openChat', () => {
ChatPanel.createOrShow(context.extensionUri);
});
context.subscriptions.push(chatCommand);
// 注册代码解释命令(示例)
const explainCommand = vscode.commands.registerCommand('glm4-assistant.explainCode', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showWarningMessage('请先打开一个代码文件。');
return;
}
const selection = editor.selection;
const selectedText = editor.document.getText(selection);
if (!selectedText.trim()) {
vscode.window.showWarningMessage('请先选择一段代码。');
return;
}
// 这里可以调用模型服务来解释代码,逻辑与聊天类似
vscode.window.showInformationMessage(`已选中代码,解释功能开发中...`);
});
context.subscriptions.push(explainCommand);
}
export function deactivate() {}
别忘了更新package.json,注册我们创建的命令和视图:
// 在 package.json 的 contributes 部分添加
"contributes": {
"commands": [
{
"command": "glm4-assistant.openChat",
"title": "GLM-4: 打开聊天助手"
},
{
"command": "glm4-assistant.explainCode",
"title": "GLM-4: 解释选中代码"
}
],
"menus": {
"editor/context": [
{
"command": "glm4-assistant.explainCode",
"when": "editorHasSelection",
"group": "glm4"
}
]
}
}
5. 调试、打包与优化建议
现在,一个基础可用的插件就成型了。按F5键,会打开一个扩展开发宿主窗口,你可以在里面测试插件的功能。
调试技巧:
- 在模型服务端,注意观察GPU显存使用情况。如果处理长上下文时内存不足(OOM),可以回到
server.py里,调低max_model_len参数,或者考虑使用量化版本的模型(如GGUF格式)。 - VSCode插件的输出控制台(Output -> Extension Host)是查看插件日志的好地方。
性能优化方向:
- 上下文管理:百万上下文是优势,但全量发送每次请求都又慢又耗资源。需要设计智能的上下文窗口,只选取与当前光标位置最相关的代码片段(如当前文件、导入的文件、同目录下的文件)发送给模型。
- 缓存机制:对常见的、通用的代码补全请求结果进行缓存,避免重复查询模型。
- 流式响应:对于代码生成或长回答,可以实现流式输出,让用户边等边看,体验更好。
- 本地模型量化:如果觉得原版9B模型对硬件要求还是高,可以探索使用4bit或8bit量化版本的模型,能在几乎不损失太多精度的情况下,显著降低显存消耗和提升推理速度。
打包发布: 开发测试满意后,可以使用vsce工具打包成.vsix文件,分享给团队成员或发布到市场。
npm install -g @vscode/vsce
vsce package
6. 总结
走完这一趟,你会发现,把一个强大的大模型变成编辑器里趁手的工具,并没有想象中那么遥不可及。核心思路就是搭好一个本地的模型服务,然后通过VSCode插件这个桥梁,把编辑器的上下文(代码)和用户意图(操作、提问)传递给模型,再把模型的智慧带回到编辑器中。
我们今天实现的代码补全和聊天面板,只是一个起点。GLM-4-9B-Chat-1M的长上下文和工具调用能力,留出了巨大的想象空间。比如,未来可以做一个“一键代码审查”功能,把整个Pull Request的改动喂给模型,让它生成审查意见;或者做一个“自动化测试生成”功能,让它根据你的业务逻辑代码,自动编写对应的单元测试用例。
开发这类插件的乐趣就在于,你不仅是在做一个工具,更是在为自己和同行们塑造一种全新的、与代码对话的交互方式。从一行行补全开始,慢慢让它成长为项目里一个真正懂行的“副驾驶”。如果你已经部署好了模型服务,不妨就从今天写的这个简单插件开始试起,感受一下AI辅助编程最直接的温度。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐


所有评论(0)