本地部署大模型完全指南⑧:实战应用——本地AI编程助手
本地部署大模型完全指南⑧:实战应用——本地AI编程助手
前7篇文章我们从硬件选型到性能优化全走了一遍。最后一篇来点实在的——把本地模型变成你VS Code里的AI编程搭档,让代码补全、审查、重构一条龙自动化。
前言:为什么需要本地编程助手?
云端编程助手(如GitHub Copilot、Cursor)很强,但有两个痛点:
- 数据隐私:完整代码库上传到云端,企业场景不放心
- 离线受限:出差、内网开发环境无法使用
- 费用不菲:Copilot $10/月,一堆团队账号开销不小
本地模型解决所有问题——脱机可用、数据不出门、一次投入无限次调用。
一、VS Code集成方案
1.1 方案一:Continue插件(推荐)
Continue 是最成熟的开源代码AI插件,原生支持Ollama。
安装步骤:
VS Code → 扩展 → 搜索 "Continue" → 安装
配置Continue连接Ollama:
安装完成后,打开 config.json(按 Ctrl+Shift+P 输入 Continue: Open Config):
{
"models": [
{
"title": "本地DeepSeek",
"provider": "ollama",
"model": "deepseek-r1:7b",
"contextLength": 8192
},
{
"title": "本地Qwen代码版",
"provider": "ollama",
"model": "qwen2.5-coder:7b",
"contextLength": 8192
},
{
"title": "代码补全专用",
"provider": "ollama",
"model": "qwen2.5-coder:7b",
"completionOptions": {
"temperature": 0.2,
"topP": 0.9
}
}
],
"tabAutocompleteModel": {
"title": "代码自动补全",
"provider": "ollama",
"model": "qwen2.5-coder:7b"
},
"slashCommands": [
{
"name": "edit",
"description": "编辑选中代码"
},
{
"name": "review",
"description": "审查选中代码"
},
{
"name": "test",
"description": "为选中代码生成单元测试"
}
]
}
配置完成后即可使用:
Tab键:内联代码补全Ctrl+I:内联对话Ctrl+L:在侧边栏对话- 选中代码 →
Ctrl+L:针对代码提问
1.2 方案二:CodeGPT插件(更全面的选择)
# VS Code安装CodeGPT
# 扩展市场搜索 "CodeGPT" 并安装
配置Ollama连接:
CodeGPT设置 → 模型供应商 → Ollama
→ 填写API地址:http://localhost:11434
→ 选择模型:qwen2.5-coder:7b
CodeGPT vs Continue 功能对比:
| 功能 | Continue | CodeGPT |
|---|---|---|
| 代码补全 | Tab内联补全 | 有 |
| 代码审查 | 命令模式 | 有 |
| 对话 | 侧边栏 | 侧边栏 |
| 自定义Agent | 有 | 有 |
| MCP支持 | 有(新版) | 无 |
| 多模型切换 | 原生支持 | 原生支持 |
二、自定义代码助手工作流
2.1 代码审查自动化
Continue支持自定义流程,我们可以创建一个代码审查Agent:
创建 ~/.continue/slashCommands/review.py:
# review命令:代码审查
def review_context(code: str, language: str) -> str:
return f"""你是一个经验丰富的代码审查员。
请审查以下{language}代码,从以下维度给出反馈:
## 审查清单
1. **代码正确性**:是否有逻辑错误或bug?
2. **安全性**:是否有SQL注入、XSS、认证越权等安全问题?
3. **性能**:是否有性能瓶颈或冗余计算?
4. **可维护性**:命名规范、代码结构、注释质量
5. **最佳实践**:是否遵循语言/框架的惯用写法
## 输出格式
请用以下格式输出审查结果:
### 发现的问题
| 严重程度 | 位置 | 问题描述 | 建议 |
|---------|------|---------|------|
| 🔴 严重 | 第X行 | ... | ... |
| 🟡 一般 | 第X行 | ... | ... |
| 🟢 建议 | 第X行 | ... | ... |
### 改进建议
1. 具体建议...
2. 优化方向...
### 代码优化版
(如果发现问题,给出优化后的完整代码)
---
待审查代码:
```{language}
{code}
```"""
2.2 自定义提示词模板
在Continue中创建常用提示词模板:
## 模板一:代码解释
请解释以下代码的功能、输入输出和关键逻辑步骤:
{selectedCode}
使用思维链方式,先分析整体架构,再逐行解释关键部分。
## 模板二:添加注释
为以下代码添加符合语言规范的注释,包括:
- 文件/模块级别注释(功能、作者、日期)
- 函数注释(功能、参数、返回值、异常)
- 关键逻辑的说明注释
{selectedCode}
## 模板三:重构建议
请对以下代码进行重构评估:
- 是否存在重复代码可以提取?
- 函数是否过长需要拆分?
- 命名是否需要改进?
- 是否可以用设计模式优化?
- 如果重构,给出具体方案
{selectedCode}
## 模板四:单元测试生成
请为以下函数生成全面的单元测试:
- 覆盖正常输入、边界情况、异常输入
- 使用 pytest 框架
- 包含 mock 外部依赖
- 测试命名要有描述性
{selectedCode}
2.3 自动提交消息生成
# git_commit.py — 根据Git diff生成提交信息
import subprocess
import requests
def generate_commit_message():
"""根据git diff生成提交信息"""
# 获取git diff
diff = subprocess.getoutput("git diff --cached --stat")
diff_content = subprocess.getoutput("git diff --cached")
if not diff_content:
print("没有暂存的变更")
return
prompt = f"""基于以下Git变更信息,生成一个规范的提交消息。
变更文件概览:
{diff}
变更详情:
{diff_content}
请按以下格式生成提交消息:
(): <简短描述(不超过50字)>
<详细描述(可选,每行不超过72字)>
<关联Issue(可选)>
type 必须是以下之一:feat, fix, docs, style, refactor, perf, test, chore, ci
请用中文写描述。
"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "qwen2.5-coder:7b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.3}
})
return response.json()["response"]
if __name__ == "__main__":
msg = generate_commit_message()
print("\n推荐提交信息:")
print(msg)
三、项目级代码理解
3.1 RAG接入项目代码库
把整个项目代码当知识库,问AI任何代码问题:
# project_rag.py — 项目代码RAG
import os
from pathlib import Path
from typing import List, Dict
import requests
class ProjectCodeRAG:
"""项目代码RAG系统"""
def __init__(self, project_path: str):
self.project_path = Path(project_path)
self.ollama_base = "http://localhost:11434"
self.code_embeddings = [] # [(embedding, filepath, snippet), ...]
self.supported_exts = {'.py', '.js', '.ts', '.jsx', '.tsx',
'.java', '.go', '.rs', '.cpp', '.h',
'.vue', '.css', '.scss', '.json', '.yaml'}
self._index_project()
def _split_code(self, content: str, filepath: str) -> List[Dict]:
"""按函数/类分割代码"""
import re
chunks = []
# 匹配函数定义
func_pattern = r'(?:def |function |public |private |protected |fn |export )\w+\s*\([^)]*\)'
# 匹配类定义
class_pattern = r'(?:class |struct |interface )\w+'
all_matches = []
for pattern in [class_pattern, func_pattern]:
for match in re.finditer(pattern, content):
all_matches.append((match.start(), match.end(), match.group()))
# 如果没找到函数/类,按文件整体处理
if not all_matches:
return [{"file": filepath, "content": content[:2000]}]
# 提取每个代码块
all_matches.sort()
for i, (start, end, name) in enumerate(all_matches):
# 获取代码块结束位置(下一个匹配开始或文件结束)
chunk_end = all_matches[i+1][0] if i + 1 < len(all_matches) else len(content)
chunk_content = content[start:chunk_end].strip()
if len(chunk_content) > 50: # 过滤太短的片段
chunks.append({
"file": filepath,
"name": name,
"content": chunk_content
})
return chunks
def _index_project(self):
"""构建项目索引"""
print(f"正在索引项目:{self.project_path}")
for file_path in self.project_path.rglob('*'):
if file_path.suffix.lower() not in self.supported_exts:
continue
if 'node_modules' in str(file_path) or '.git' in str(file_path):
continue
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
chunks = self._split_code(content, str(file_path.relative_to(self.project_path)))
for chunk in chunks:
# 获取向量
response = requests.post(f"{self.ollama_base}/api/embeddings", json={
"model": "nomic-embed-text",
"prompt": chunk["content"]
})
if response.status_code == 200:
embedding = response.json()["embedding"]
self.code_embeddings.append((embedding, chunk))
print(f" ✓ {file_path.name}")
except Exception as e:
print(f" ✗ {file_path.name}: {e}")
print(f"\n共索引 {len(self.code_embeddings)} 个代码片段")
def query(self, question: str, top_k: int = 5) -> str:
"""查询项目代码"""
import numpy as np
# 向量化问题
response = requests.post(f"{self.ollama_base}/api/embeddings", json={
"model": "nomic-embed-text",
"prompt": question
})
query_embedding = response.json()["embedding"]
# 计算相似度
similarities = []
for emb, chunk in self.code_embeddings:
sim = np.dot(emb, query_embedding) / (
np.linalg.norm(emb) * np.linalg.norm(query_embedding)
)
similarities.append((sim, chunk))
similarities.sort(key=lambda x: x[0], reverse=True)
top_chunks = similarities[:top_k]
# 构建上下文
context = ""
for i, (sim, chunk) in enumerate(top_chunks):
context += f"\n### 文件 {chunk['file']}(相似度:{sim:.2f})\n```\n{chunk['content']}\n```\n"
# 生成回答
prompt = f"""基于以下项目代码,回答问题。
## 项目代码片段
{context}
## 问题
{question}
## 回答要求
1. 引用具体的文件路径和代码行
2. 如果代码中没有相关信息,请说明
3. 保持回答简洁、精确
"""
response = requests.post(f"{self.ollama_base}/api/generate", json={
"model": "qwen2.5-coder:7b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.3}
})
return response.json()["response"]
# 使用示例
# rag = ProjectCodeRAG("/path/to/your/project")
# result = rag.query("这个项目如何进行用户认证?")
# print(result)
3.2 代码库文档自动生成
# doc_gen.py — 自动生成项目文档
import os
from pathlib import Path
class ProjectDocGenerator:
"""项目文档自动生成器"""
def __init__(self, project_path: str):
self.project_path = Path(project_path)
self.ollama_base = "http://localhost:11434"
def generate_readme(self):
"""生成README.md"""
# 获取项目结构
structure = self._get_project_structure()
# 获取主要文件内容摘要
summaries = self._get_main_files_summary()
prompt = f"""根据以下项目信息,生成一个专业的README.md文档。
项目结构:
{structure}
主要文件摘要:
{summaries}
请生成README.md,包含:
1. 项目名称和简介
2. 技术栈
3. 核心功能
4. 快速开始(安装+使用)
5. 项目结构说明
6. 贡献指南
7. 许可信息
格式要专业、清晰,使用Markdown。
"""
response = requests.post(f"{self.ollama_base}/api/generate", json={
"model": "qwen2.5:14b",
"prompt": prompt,
"stream": False
})
readme_content = response.json()["response"]
# 写入文件
output_path = self.project_path / "README.md"
output_path.write_text(readme_content, encoding='utf-8')
print(f"README.md 已生成:{output_path}")
def _get_project_structure(self, max_depth=3):
"""获取项目目录结构"""
structure = []
self._walk_dir(self.project_path, 0, max_depth, structure)
return '\n'.join(structure)
def _walk_dir(self, path, depth, max_depth, result):
if depth > max_depth:
return
if path.name.startswith('.') or path.name == 'node_modules':
return
indent = ' ' * depth
if path.is_dir():
result.append(f"{indent}📁 {path.name}/")
for child in sorted(path.iterdir()):
self._walk_dir(child, depth + 1, max_depth, result)
elif path.is_file():
result.append(f"{indent}📄 {path.name}")
def _get_main_files_summary(self):
"""获取主要文件的摘要"""
main_files = [
"package.json", "pyproject.toml", "Cargo.toml",
"main.py", "app.py", "index.js", "main.go",
"docker-compose.yml", "Dockerfile", ".env.example"
]
summaries = []
for fname in main_files:
fpath = self.project_path / fname
if fpath.exists():
content = fpath.read_text(encoding='utf-8', errors='ignore')[:500]
summaries.append(f"### {fname}\n```\n{content}\n```")
return '\n\n'.join(summaries)
四、Git工作流集成
4.1 自动代码审查Hook
创建Git pre-commit hook,提交前自动审查代码:
#!/bin/bash
# .git/hooks/pre-commit — 提交前代码审查
echo "🔍 运行AI代码审查..."
# 获取暂存的文件
staged_files=$(git diff --cached --name-only --diff-filter=ACM)
for file in $staged_files; do
# 只审查代码文件
case "$file" in
*.py|*.js|*.ts|*.java|*.go|*.rs)
# 获取diff内容
diff_content=$(git diff --cached "$file")
if [ ${#diff_content} -gt 100 ]; then
echo "审查: $file"
# 调用Ollama审查
response=$(curl -s -X POST http://localhost:11434/api/generate \
-d "{\"model\":\"qwen2.5-coder:7b\",\"prompt\":\"审查以下代码变更,只输出'通过'或有问题的具体行号和建议,控制在200字以内:\n$diff_content\",\"stream\":false}")
result=$(echo "$response" | python3 -c "import sys,json;print(json.load(sys.stdin).get('response',''))" 2>/dev/null)
if echo "$result" | grep -qi "严重\|bug\|安全"; then
echo "⚠️ 发现严重问题:"
echo "$result"
exit 1
fi
fi
;;
esac
done
echo "✅ AI审查通过"
4.2 自动生成Changelog
# changelog.py — 自动生成Changelog
import subprocess
import requests
from datetime import datetime
def generate_changelog(since_tag: str = None):
"""根据Git log生成Changelog"""
# 获取Git日志
if since_tag:
log_cmd = f"git log {since_tag}..HEAD --oneline --no-merges"
else:
log_cmd = "git log --oneline --no-merges -30"
git_log = subprocess.getoutput(log_cmd)
prompt = f"""根据以下Git提交记录,生成规范的CHANGELOG。
提交记录:
{git_log}
请按以下格式输出:
## [版本号] - 发布日期
### 新增功能
- ...
### Bug修复
- ...
### 重构/优化
- ...
### 文档
- ...
格式要求:
1. 用中文总结变更
2. 按功能分类
3. 每个变更点一行,简洁描述
4. 去掉Git commit hash
"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "qwen2.5:7b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.3}
})
return response.json()["response"]
五、综合效率工具:AI命令助手
5.1 Shell命令生成
# shell_assistant.py — 终端命令助手
def ask_shell(task_description: str) -> str:
"""把自然语言转换为shell命令"""
prompt = f"""用户需要完成以下任务,请生成对应的Shell/PowerShell命令:
任务:{task_description}
要求:
1. 只输出命令本身,不要解释
2. 确保命令安全(不加rm -rf等破坏性命令)
3. 如果任务有风险,先输出说明再提供命令
4. 使用Windows PowerShell兼容的语法
"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "deepseek-r1:7b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.2}
})
return response.json()["response"]
# 使用
print(ask_shell("查找所有超过100MB的Python文件"))
# 输出:Get-ChildItem -Recurse -Filter *.py | Where-Object { $_.Length -gt 100MB }
5.2 代码脚手架生成
# scaffold.py — 项目脚手架生成
def generate_scaffold(project_type: str, project_name: str) -> str:
"""生成项目脚手架结构"""
prompt = f"""生成一个 {project_type} 项目的脚手架。
项目名称:{project_name}
输出格式:
project_name/
├── src/
│ ├── init.py
│ ├── …
├── tests/
│ ├── …
├── README.md
├── requirements.txt
对每个文件给出其核心内容(关键代码,不是全部),用注释标注文件路径。
"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "qwen2.5-coder:7b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.4}
})
return response.json()["response"]
# 快速生成Flask API项目骨架
print(generate_scaffold("Flask REST API", "my-api"))
六、监控与统计
6.1 使用统计看板
# stats.py — AI助手使用统计
import json
import os
from datetime import datetime
class UsageStats:
"""AI助手使用统计"""
def __init__(self, stats_file="ai_stats.json"):
self.stats_file = stats_file
self.stats = self._load()
def _load(self):
if os.path.exists(self.stats_file):
with open(self.stats_file) as f:
return json.load(f)
return {"total_requests": 0, "by_date": {}, "by_model": {}}
def record(self, model: str, tokens: int, duration: float):
"""记录一次调用"""
today = datetime.now().strftime("%Y-%m-%d")
self.stats["total_requests"] += 1
self.stats["by_date"][today] = self.stats["by_date"].get(today, 0) + 1
self.stats["by_model"][model] = self.stats["by_model"].get(model, 0) + 1
self._save()
def _save(self):
with open(self.stats_file, 'w') as f:
json.dump(self.stats, f, indent=2, ensure_ascii=False)
def report(self):
"""生成使用报告"""
print("=" * 50)
print("AI助手使用统计")
print("=" * 50)
print(f"总调用次数:{self.stats['total_requests']}")
print(f"\n按日期:")
for date, count in sorted(self.stats["by_date"].items()):
print(f" {date}: {count}次")
print(f"\n按模型:")
for model, count in sorted(self.stats["by_model"].items()):
print(f" {model}: {count}次")
# 使用
stats = UsageStats()
stats.record("qwen2.5-coder:7b", 256, 1.5)
stats.report()
七、总结与展望
7.1 整本书回顾
这个系列从零开始,带你走完了本地部署大模型的完整旅程:
① 硬件选型 → ② Ollama部署 → ③ RAG知识库
④ API服务化 → ⑤ 前端界面 → ⑥ 多模型管理
⑦ 性能优化 → ⑧ 编程助手 → 完成!🎉
7.2 进阶方向
本地大模型的世界远不止于此。接下来你可以探索:
- 模型微调:用LoRA在自有数据上训练专属模型
- 多模态:部署视觉模型(LLaVA、Qwen-VL)
- Agent系统:让AI自主调用工具和执行任务
- 分布式推理:多机多卡集群部署
7.3 资源推荐
Ollama官网:https://ollama.com
模型下载:https://ollama.com/library
Continue插件:https://continue.dev
Open WebUI:https://github.com/open-webui/open-webui
Dify平台:https://github.com/langgenius/dify
全文完
8篇文章,从选显卡到写代码,你一定已经拥有了一套完整的本地AI工作环境。数据安全、离线可用、无限调用——自己的模型,才是最好的模型。
需要完整脚本和配置文件的同学,可以看我主页的付费资源专栏。
有问题欢迎评论区留言,大家一起讨论!
更多推荐
所有评论(0)