MacBook M4 搭建千问模型 API 服务和 Web 对话界面
在前两篇教程中,我们介绍了用 Ollama、llama.cpp 和 MLX 在 MacBook M4 上运行千问模型的方法。但这些都是命令行交互,如果你想要一个类似 ChatGPT 的 Web 界面,或者想让局域网内的其他设备也能调用千问模型 API,本文将手把手教你搭建完整的 API 服务和 Web 对话界面。
整体架构
我们将搭建以下架构:
+------------------+ +------------------+ +------------------+
| Open WebUI | | Ollama / API | | Qwen 模型 |
| (Web 对话界面) | --> | (API 服务层) | --> | (M4 GPU 推理) |
| localhost:8080 | | localhost:11434 | | Metal 加速 |
+------------------+ +------------------+ +------------------+
技术选型:
- API 层:Ollama(内置 OpenAI 兼容 API)或 llama.cpp server
- Web UI:Open WebUI(开源 ChatGPT 替代品,功能强大)
- 局域网访问:通过 Nginx 反向代理或直接暴露端口
一、使用 Ollama 搭建 API 服务
1. 启动 Ollama 服务
Ollama 启动后默认提供 REST API,端口 11434:
# 启动 Ollama 服务(默认监听 11434)
ollama serve
# 测试 API 是否正常
curl http://localhost:11434/api/version
# 返回: {"name":"ollama","version":"0.5.x"}
2. 调用千问模型 API
Ollama 提供两种 API 格式:原生格式和 OpenAI 兼容格式。
原生 API(流式输出)
curl http://localhost:11434/api/generate -d '{
"model": "qwen2.5:7b",
"prompt": "你好,请用Python写一个快速排序",
"stream": true
}'
OpenAI 兼容 API(推荐)
这个接口完全兼容 OpenAI API 格式,可以直接用 openai SDK 调用:
curl http://localhost:11434/v1/chat/completions -d '{
"model": "qwen2.5:7b",
"messages": [
{"role": "system", "content": "你是一个Python编程助手"},
{"role": "user", "content": "写一个快速排序"}
],
"stream": false,
"temperature": 0.7
}'
Python 调用示例
pip install openai
# 使用 OpenAI SDK 调用本地千问模型
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # Ollama 不验证 key,随便填
)
response = client.chat.completions.create(
model="qwen2.5:7b",
messages=[
{"role": "system", "content": "你是一个专业的Python开发工程师"},
{"role": "user", "content": "请写一个装饰器,用于函数执行耗时统计"}
],
temperature=0.7,
max_tokens=2000
)
print(response.choices[0].message.content)
3. 开启局域网访问
默认 Ollama 只监听 127.0.0.1,要让局域网其他设备也能访问,需要修改监听地址:
# 方法一:设置环境变量
# 编辑 ~/.zshrc 或 ~/.bash_profile
export OLLAMA_HOST=0.0.0.0:11434
# 重新启动 Ollama
ollama serve
# 方法二:使用 launchd(推荐)
# 创建 ~/Library/LaunchAgents/com.ollama.plist
# 设置 EnvironmentVariables 中的 OLLAMA_HOST
从局域网其他设备测试:
# 将 192.168.1.100 替换为你 MacBook 的 IP
curl http://192.168.1.100:11434/v1/chat/completions -d '{
"model": "qwen2.5:7b",
"messages": [{"role": "user", "content": "你好"}]
}'
二、使用 llama.cpp 搭建 API 服务(备选)
如果你使用 llama.cpp,它也内置了 OpenAI 兼容的 API 服务器:
# 启动 API 服务器
./build/bin/llama-server \
-m ./models/qwen2.5-7b-instruct-q4_k_m.gguf \
--host 0.0.0.0 \
--port 8080 \
--gpu-layers 99 \
-c 4096
# 服务器启动后访问 http://localhost:8080 即可看到 Web 界面
# API 地址: http://localhost:8080/v1/chat/completions
三、部署 Open WebUI 对话界面
Open WebUI 是一个功能强大的开源 Web 界面,支持多模型切换、对话历史、文档上传、知识库等功能,界面酷似 ChatGPT。
1. Docker 部署(推荐)
# 安装 Docker Desktop for Mac(如果还没有)
# 从 https://docker.com 下载安装
# 一键启动 Open WebUI
docker run -d \
--name open-webui \
-p 3000:8080 \
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 \
-v open-webui:/app/backend/data \
--restart always \
ghcr.io/open-webui/open-webui:main
启动后访问 http://localhost:3000,第一次打开需要注册管理员账号。
2. 非 Docker 部署
如果不想用 Docker,也可以直接用 pip 安装:
# 创建独立虚拟环境
python3 -m venv ~/open-webui-env
source ~/open-webui-env/bin/activate
# 安装 Open WebUI
pip install open-webui
# 启动服务
open-webui --host 0.0.0.0 --port 3000
3. 配置连接 Ollama
打开 Open WebUI 后,进入 设置 → 连接,确保 Ollama 地址正确:
- Docker 部署:
http://host.docker.internal:11434 - 非 Docker 部署:
http://localhost:11434 - 局域网其他设备:
http://你的MacBook_IP:11434
4. 使用 Web 界面
配置完成后,在聊天界面选择 qwen2.5:7b 模型即可开始对话。Open WebUI 支持:
- 多轮对话和对话历史管理
- Markdown 渲染和代码高亮
- 上传文档进行问答(RAG)
- 多用户管理和权限控制
- 自定义系统提示词
- 导出对话记录
- 多模型对比回答
四、用 FastAPI 搭建自定义 API 服务
如果你需要更多自定义功能(如速率限制、用户认证、日志记录),可以用 FastAPI 封装一层:
pip install fastapi uvicorn
# 创建 server.py
cat > server.py << 'PYEOF'
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import httpx
import time
from typing import List, Optional
app = FastAPI(title="Qwen API Service")
# 允许跨域
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
OLLAMA_URL = "http://localhost:11434"
# 请求模型
class Message(BaseModel):
role: str
content: str
class ChatRequest(BaseModel):
messages: List[Message]
model: str = "qwen2.5:7b"
temperature: float = 0.7
max_tokens: int = 2000
class ChatResponse(BaseModel):
response: str
model: str
elapsed: float
def log_request(model, messages, elapsed):
print(f"[{time.strftime('%H:%M:%S')}] model={model} "
f"msgs={len(messages)} time={elapsed:.2f}s")
@app.post("/v1/chat", response_model=ChatResponse)
async def chat(req: ChatRequest):
start = time.time()
try:
async with httpx.AsyncClient(timeout=120) as client:
resp = await client.post(
f"{OLLAMA_URL}/api/chat",
json={
"model": req.model,
"messages": [m.dict() for m in req.messages],
"stream": False,
"options": {
"temperature": req.temperature,
"num_predict": req.max_tokens
}
}
)
resp.raise_for_status()
data = resp.json()
elapsed = time.time() - start
log_request(req.model, req.messages, elapsed)
return ChatResponse(
response=data["message"]["content"],
model=req.model,
elapsed=elapsed
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/v1/models")
async def list_models():
async with httpx.AsyncClient() as client:
resp = await client.get(f"{OLLAMA_URL}/api/tags")
return resp.json()
@app.get("/health")
async def health():
return {"status": "ok", "service": "qwen-api"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
PYEOF
# 启动服务
python server.py
启动后即可通过 http://localhost:8000/docs 查看 API 文档,接口包括:
POST /v1/chat:对话接口GET /v1/models:列出可用模型GET /health:健康检查
五、使用 Nginx 反向代理(生产级部署)
如果你希望通过域名或统一端口访问,可以用 Nginx 做反向代理:
# 安装 Nginx
brew install nginx
# 配置文件: /opt/homebrew/etc/nginx/nginx.conf
# 添加 server 配置:
server {
listen 80;
server_name localhost;
# Web UI
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# API 代理
location /api/ {
proxy_pass http://127.0.0.1:11434/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# SSE 流式输出支持
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
}
# 自定义 API
location /v1/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
}
}
# 启动 Nginx
nginx
六、性能优化技巧
1. 模型预加载
Ollama 默认在空闲后卸载模型。可以通过 API 保持模型常驻内存:
# 设置模型保持加载(TTL 为 0 表示永不卸载)
curl http://localhost:11434/api/generate -d '{
"model": "qwen2.5:7b",
"keep_alive": 0
}'
2. 并发请求处理
# Ollama 默认串行处理请求
# 可以设置并行处理的请求数(会增加内存占用)
export OLLAMA_NUM_PARALLEL=2
# 设置同时加载的模型数
export OLLAMA_MAX_LOADED_MODELS=2
3. 上下文长度优化
更大的上下文窗口意味着更多内存占用和更慢的推理速度。按需设置:
# Ollama 设置上下文窗口(通过 Modelfile)
cat > Modelfile << EOF
FROM qwen2.5:7b
PARAMETER num_ctx 8192
PARAMETER temperature 0.7
PARAMETER top_p 0.9
EOF
# 创建自定义模型
ollama create qwen2.5-8k -f Modelfile
七、实际应用场景
- 编程助手:在 VS Code 中配置 Continue 插件,连接本地 Ollama API,实现 AI 代码补全
- 文档问答:通过 Open WebUI 上传 PDF/Word 文档,利用 RAG 功能进行问答
- 翻译服务:调用 API 实现批量文档翻译,完全离线运行
- 数据分析:结合 Python 脚本,让千问模型分析 CSV 数据并生成报告
- 团队协作:局域网内部署,团队成员通过浏览器访问共享 AI 助手
VS Code 配置 Continue 插件
# 在 VS Code 中安装 Continue 扩展
# 编辑 ~/.continue/config.json
{
"models": [{
"title": "Qwen2.5 Local",
"provider": "ollama",
"model": "qwen2.5-coder:7b",
"apiBase": "http://localhost:11434"
}],
"tabAutocompleteModel": {
"title": "Qwen Coder",
"provider": "ollama",
"model": "qwen2.5-coder:7b"
}
}
总结
通过本系列三篇教程,你已经学会了在 MacBook M4 上部署千问模型的完整方案:
- 第一篇:Ollama + llama.cpp 命令行快速部署
- 第二篇:MLX 框架原生加速运行
- 第三篇:搭建 API 服务和 Web 对话界面(本文)
MacBook M4 凭借统一内存架构和 Metal 加速,完全可以在本地流畅运行 7B-72B 级别的大模型。相比云端 API,本地部署有三大优势:数据隐私(数据不离开设备)、零成本(无需 API 费用)、离线可用(无需网络)。希望这个系列能帮助你充分利用 MacBook M4 的 AI 能力!
系列文章
- ✅ 第一篇:Ollama + llama.cpp 快速部署
- ✅ 第二篇:MLX 框架原生加速运行千问模型
- ✅ 第三篇:搭建千问 API 服务和 Web 对话界面(本文)






