纲要
- 本地私有化部署的价值与适用场景
- 工具选型:
LM Studio+Cherry StudioLM Studio:模型托管与本地推理服务Cherry Studio:通用 AI 对话客户端
- 部署流程概览
- 步骤一:安装与配置
LM Studio- 下载与语言切换
- 开启 Hugging Face 代理加速
- 选择并下载模型(以 DeepSeek‑R1‑Distill‑Qwen‑14B 为例)
- 步骤二:启动本地推理 API 服务
- 进入开发者模式
- 加载模型并启动服务器
- 理解 OpenAI 兼容接口
- 步骤三:安装与配置
Cherry Studio- 添加
LM Studio作为模型服务 - 验证连接并设为默认模型
- 添加
- 步骤四:使用本地私有化对话服务
- 普通问答与思维链展示
- 扩展功能(翻译、知识库等)
- 代码实战:通过 Python 调用本地 OpenAI 兼容 API
- 直接调用
/v1/chat/completions端点 - 完整可运行脚本(含同步与流式示例)
- 直接调用
- API 速览
- Demo 示例
- 参考文档
- 总结
本地私有化部署的价值与适用场景
对于注重数据隐私、需要高频调用模型或在无网络环境下使用 AI 的开发者,本地私有化部署是较为理想的方案。通过将大语言模型运行在个人电脑或内网服务器上,所有对话数据完全留存在本地,无需担心第三方平台的数据收集,也不产生 token 费用。
本文以 DeepSeek 模型为例,使用两款免费、跨平台的工具——LM Studio 和 Cherry Studio,完整演示如何搭建一套完全运行在本地的 AI 对话助手。即使没有深度技术背景,也可按步骤在约 30 分钟内完成部署。
工具选型
LM Studio:一个模型托管与本地推理工具,支持从 Hugging Face 下载模型,并提供兼容 OpenAI 格式的本地 API 服务。支持 Windows / macOS / Linux。Cherry Studio:一个通用 AI 对话客户端,支持接入各种本地或云端模型,内置智能体、知识库、翻译、绘图等扩展能力,提供图形化交互界面。
两者的组合关系可理解为:LM Studio 承担“推理引擎”角色,Cherry Studio 承担“交互控制”角色,两者协同实现完整的本地 AI 服务链路。
部署流程概览
步骤一:安装与配置 LM Studio
访问 lmstudio.ai 下载对应平台的安装包,完成安装后打开软件。
- 切换语言:点击右下角齿轮图标进入设置,将语言切换为“简体中文”。
- 开启代理:在设置中勾选“使用 LM Studio 的内置 Hugging Face 代理”(国内用户建议开启,否则模型下载速度可能较慢或失败)。
- 切换用户模式:左下角选择“Power User”或“Developer”模式,以解锁模型下载和 API 服务功能。
下载 DeepSeek 模型
点击左侧“发现”选项卡,在搜索框输入 DeepSeek,即可看到社区上传的各类蒸馏版本。根据硬件配置选择合适的模型:
| 模型参数量 | 推荐硬件配置 |
|---|---|
| 7B | 8 GB 内存 + GTX 1660 及以上显卡 |
| 14B | 16 GB 内存 + RTX 3060 及以上显卡 |
| 32B | 24 GB 显存及以上 |
本文以 DeepSeek‑R1‑Distill‑Qwen‑14B‑Q4_K_M 为例,大小约 9 GB,量化级别 Q4,平衡速度与精度。点击“Download”等待下载完成。
步骤二:启动本地推理 API 服务
模型下载完毕后,切换到“开发者模式”,在顶部选择已下载的模型,然后点击“Start Server”按钮。日志中会显示:
Server started on http://127.0.0.1:1234
这表示本地推理服务已启动,默认端口为 1234。LM Studio 提供与 OpenAI 兼容的接口端点:
POST /v1/chat/completionsPOST /v1/completionsPOST /v1/embeddingsGET /v1/models
可在设置中修改端口,或开启局域网访问,使其他设备也能调用该服务。
步骤三:安装与配置 Cherry Studio
访问 cherry-ai.com 下载并安装 Cherry Studio。打开后进入设置 → 模型服务 → 添加 → 选择“LM Studio”。
- API 地址:默认
http://127.0.0.1:1234/v1 - API Key:可填写任意内容(如
lm-studio),因为本地服务不验证密钥 - 模型列表:点击管理,选择已在
LM Studio中加载的模型
点击“检查”按钮,看到“连接成功”提示即表示配置无误。在“默认模型”中将对话、翻译等功能的默认模型设为该本地模型。
步骤四:使用本地私有化对话服务
返回 Cherry Studio 聊天界面,即可与本地部署的 DeepSeek 进行对话。由于是推理型模型,回答前会展示完整的思考过程(思维链)。在本地 14B 模型上,首 token 延迟约 20 秒左右,整体体验与官方 API 基本一致。
Cherry Studio 内置的翻译、知识库、智能体等功能均可依赖本地模型运行,实现真正的离线 AI 工作站。
代码实战:通过 Python 调用本地 OpenAI 兼容 API
除图形化客户端外,也可直接通过代码与本地推理服务交互。以下提供完整的 Python 脚本,调用 LM Studio 提供的 OpenAI 兼容接口。
环境准备
安装 openai 库:
pip install openai
同步调用示例
import openai
# ================= 配置区 =================
BASE_URL = "http://127.0.0.1:1234/v1" # LM Studio 默认地址
API_KEY = "not-needed" # 本地服务不验证密钥
MODEL = "deepseek-r1-distill-qwen-14b" # 请替换为实际加载的模型名称
# ==========================================
client = openai.OpenAI(base_url=BASE_URL, api_key=API_KEY)
def chat(prompt: str) -> str:
response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=512,
)
return response.choices[0].message.content
if __name__ == "__main__":
question = "请用一句话解释什么是机器学习"
print("用户:", question)
print("助手:", chat(question))
流式调用示例(支持思维链实时输出)
import openai
BASE_URL = "http://127.0.0.1:1234/v1"
API_KEY = "not-needed"
MODEL = "deepseek-r1-distill-qwen-14b"
client = openai.OpenAI(base_url=BASE_URL, api_key=API_KEY)
def chat_stream(prompt: str):
response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=512,
stream=True,
)
for chunk in response:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)
if __name__ == "__main__":
question = "请用一句话解释什么是机器学习"
print("用户:", question)
print("助手: ", end="")
chat_stream(question)
获取已加载模型列表
import openai
client = openai.OpenAI(base_url="http://127.0.0.1:1234/v1", api_key="not-needed")
models = client.models.list()
for m in models.data:
print(m.id)
API 速览
LM Studio 提供的本地 API 与 OpenAI 官方接口兼容,核心端点如下:
| 端点 | 方法 | 功能描述 |
|---|---|---|
/v1/chat/completions | POST | 对话补全,支持多轮对话 |
/v1/completions | POST | 文本补全(非对话形式) |
/v1/embeddings | POST | 文本向量嵌入 |
/v1/models | GET | 获取当前加载的模型列表 |
/v1/chat/completions 请求体关键参数
| 参数 | 类型 | 说明 |
|---|---|---|
model | string | 模型名称,需与 LM Studio 加载的模型名称一致 |
messages | array | 对话消息列表,格式为 [{"role": "user", "content": "..."}] |
temperature | number | 采样温度,范围 0-2,值越高随机性越强 |
max_tokens | integer | 最大生成 token 数 |
stream | boolean | 是否启用流式输出 |
错误码参考
| HTTP 状态码 | 含义 |
|---|---|
| 200 | 成功 |
| 404 | 模型未加载或端点不存在 |
| 500 | 服务内部错误 |
Demo 示例
以下提供一个完整的 HTML 文件,通过 JavaScript 调用 LM Studio 本地 API,实现一个简单的 Web 聊天界面,可直接在浏览器中运行。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>本地 AI 对话助手</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
background: #f7f9fc;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.container {
width: 100%;
max-width: 720px;
background: white;
border-radius: 24px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.10);
overflow: hidden;
display: flex;
flex-direction: column;
height: 600px;
}
.header {
background: #1e293b;
color: white;
padding: 20px 24px;
font-weight: 600;
font-size: 18px;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.header .badge {
background: #22c55e;
padding: 4px 12px;
border-radius: 40px;
font-size: 12px;
font-weight: 500;
}
.messages {
flex: 1;
overflow-y: auto;
padding: 20px 24px;
display: flex;
flex-direction: column;
gap: 12px;
background: #f8fafc;
}
.message {
max-width: 80%;
padding: 12px 18px;
border-radius: 18px;
line-height: 1.6;
font-size: 15px;
animation: fadeIn 0.25s ease;
}
.message.user {
background: #2563eb;
color: white;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
.message.assistant {
background: white;
color: #0f172a;
align-self: flex-start;
border: 1px solid #e2e8f0;
border-bottom-left-radius: 4px;
white-space: pre-wrap;
}
.message .loading {
display: inline-block;
width: 8px;
height: 8px;
background: #94a3b8;
border-radius: 50%;
animation: pulse 1.2s infinite;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%, 100% { opacity: 0.3; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1.2); }
}
.input-area {
display: flex;
gap: 12px;
padding: 16px 24px 20px;
background: white;
border-top: 1px solid #e2e8f0;
flex-shrink: 0;
}
.input-area input {
flex: 1;
padding: 12px 18px;
border: 1px solid #d1d5db;
border-radius: 40px;
font-size: 15px;
outline: none;
transition: border 0.2s;
}
.input-area input:focus {
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}
.input-area button {
padding: 12px 28px;
background: #2563eb;
color: white;
border: none;
border-radius: 40px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
white-space: nowrap;
}
.input-area button:hover {
background: #1d4ed8;
}
.input-area button:active {
transform: scale(0.96);
}
.input-area button:disabled {
background: #94a3b8;
cursor: not-allowed;
}
.status-bar {
padding: 6px 24px;
font-size: 12px;
color: #64748b;
background: #f1f5f9;
border-top: 1px solid #e2e8f0;
flex-shrink: 0;
display: flex;
justify-content: space-between;
}
.status-bar .connected {
color: #22c55e;
}
.status-bar .disconnected {
color: #ef4444;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<span>🧠 本地 AI 对话</span>
<span class="badge">DeepSeek 14B</span>
</div>
<div class="messages" id="chatBox"></div>
<div class="input-area">
<input type="text" id="promptInput" placeholder="输入消息..." />
<button id="sendBtn">发送</button>
</div>
<div class="status-bar">
<span id="statusText">⏳ 检测服务状态...</span>
<span id="modelName">模型: 未加载</span>
</div>
</div>
<script>
// ================= 配置区 =================
const BASE_URL = "http://127.0.0.1:1234/v1";
const API_KEY = "not-needed";
const MODEL = "deepseek-r1-distill-qwen-14b"; // 请修改为实际模型名
// ==========================================
const chatBox = document.getElementById('chatBox');
const inputEl = document.getElementById('promptInput');
const sendBtn = document.getElementById('sendBtn');
const statusText = document.getElementById('statusText');
const modelNameEl = document.getElementById('modelName');
let isWaiting = false;
// 添加消息到界面
function addMessage(role, content) {
const div = document.createElement('div');
div.className = `message ${role}`;
div.textContent = content;
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
return div;
}
// 设置状态栏
function setStatus(text, isConnected) {
statusText.innerHTML = text;
}
// 检查服务状态
async function checkStatus() {
try {
const resp = await fetch(`${BASE_URL}/models`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
if (resp.ok) {
const data = await resp.json();
const names = data.data.map(m => m.id).join(', ');
modelNameEl.textContent = `模型: ${names || '未知'}`;
setStatus('✅ 已连接 LM Studio', true);
} else {
modelNameEl.textContent = '模型: 未加载';
setStatus('❌ 服务异常 (HTTP ' + resp.status + ')', false);
}
} catch (_) {
modelNameEl.textContent = '模型: 未加载';
setStatus('❌ 无法连接 LM Studio,请确认服务已启动', false);
}
}
// 发送消息
async function sendMessage() {
const prompt = inputEl.value.trim();
if (!prompt || isWaiting) return;
inputEl.value = '';
// 显示用户消息
addMessage('user', prompt);
// 占位助手消息
const assistantMsg = addMessage('assistant', '');
isWaiting = true;
sendBtn.disabled = true;
inputEl.disabled = true;
try {
const resp = await fetch(`${BASE_URL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: MODEL,
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
max_tokens: 512,
stream: true
})
});
if (!resp.ok) {
const errText = await resp.text();
assistantMsg.textContent = `错误 (HTTP ${resp.status}): ${errText}`;
isWaiting = false;
sendBtn.disabled = false;
inputEl.disabled = false;
return;
}
const reader = resp.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let fullContent = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const payload = line.slice(6);
if (payload === '[DONE]') continue;
try {
const json = JSON.parse(payload);
const delta = json.choices?.[0]?.delta?.content;
if (delta) {
fullContent += delta;
assistantMsg.textContent = fullContent;
chatBox.scrollTop = chatBox.scrollHeight;
}
} catch (_) { /* 忽略非 JSON 行 */ }
}
}
}
if (!fullContent) {
assistantMsg.textContent = '(无内容返回)';
}
} catch (err) {
assistantMsg.textContent = `请求失败: ${err.message}`;
}
isWaiting = false;
sendBtn.disabled = false;
inputEl.disabled = false;
inputEl.focus();
}
// 事件绑定
sendBtn.addEventListener('click', sendMessage);
inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter') sendMessage();
});
// 初始化
checkStatus();
setInterval(checkStatus, 10000); // 每10秒刷新状态
</script>
</body>
</html>
Demo 运行说明
- 确保
LM Studio已启动并加载模型,API 服务运行在http://127.0.0.1:1234。 - 将 HTML 文件保存为
index.html,在浏览器中直接打开。 - 修改脚本中
MODEL变量为实际加载的模型名称。 - 输入消息后点击发送或按回车键即可对话,支持流式实时输出。
- 页面顶部状态栏会显示服务连接状态和当前模型名称。
Demo 技术点总结
- 使用原生
Fetch API调用LM Studio的/v1/chat/completions端点。 - 支持
stream: true流式响应,逐块渲染思维链与回答内容。 - 自动检测服务状态并展示模型列表,增强用户体验。
- 完整的错误处理与加载状态管理。
参考文档
官方文档
参考链接
总结
本文系统介绍了基于 LM Studio 与 Cherry Studio 构建本地私有化 AI 对话助手的完整流程,涵盖从模型下载、本地推理服务启动到图形客户端配置及代码级调用的全过程。核心技术栈要点如下:
LM Studio提供 OpenAI 兼容的本地推理 API,支持/v1/chat/completions、/v1/embeddings等标准端点,默认监听127.0.0.1:1234。Cherry Studio作为通用客户端,可连接本地或云端模型,内置翻译、知识库、智能体等扩展能力。- 部署流程包括:LM Studio 安装 → Hugging Face 代理配置 → 模型下载 → 开发者模式启动 API 服务 → Cherry Studio 服务添加与验证 → 开始对话。
- Python 调用方式支持同步与流式(
stream=True),通过openai库即可完成集成。 - 通过 HTML + JavaScript 可直接在浏览器中构建轻量级对话界面,利用流式 API 实现实时交互。
4661



被折叠的 条评论
为什么被折叠?



