LLM 本地部署运行初体验

前言

  最近看到这么一个工具——ollama-ocr,利用本地大模型直接进行 OCR,试用了一下,感觉效果还不错,联想之前看到的一个项目——PDFMathTranslate,感觉本地大模型已经非常成熟了,部署使用也越来越简单了。

如果需要提供 LLM 服务,还是用 vLLM 部署相对合适。

这篇文章 Friends Don't Let Friends Use Ollama,不推荐使用 Ollama,推荐直接使用 llama.cpp,或者 LM Studio。

配置篇

  硬件:M3 MBP,10 核 GPU(Metal 3),16G 内存

  系统&软件:macOS Sonoma 14.5Ollama 0.5.4,AnythingLLM 1.7.2;

体验篇

ollama 篇

  第一次启动 Ollama 时,会出现安装「command line」的引导界面,输入电脑用户密码安装即可,安装命令行之后,可以直接点击「Finish」,之后通过终端命令 ollama 操作 Ollama,毕竟 Ollama 没有提供 UI 界面,当然也有很多三方的界面

  之后在终端中直接运行 ollama run llama3.2:3b,等待模型拉取完成后,即可直接与模型对话。之后可以在 Ollama 官网 Models 搜索尝试更多模型。

命令解析:

  • ollama pull [model:tag]:拉取模型;
  • ollama run [model:tag]:运行拉取的模型,若没有,会自动拉取之后运行;
  • ollama list:查看全部已拉取的模型;
  • ollama show [model:tag]:显示模型信息;
  • ollama rm [model:tag]:删除已拉取的模型;
  • ollama ps:查看正在运行的模型;
  • ollama stop [model:tag]:停止正在运行的模型;

  Mac 中修改全局环境变量可通过 launchctl setenv 命令,eg: launchctl setenv OLLAMA_ORIGINS "*",允许 ollama 请求跨域。

  Mac 中 ollama 拉取的模型文件默认放在 ~/.ollama/models 目录中,可通过修改 OLLAMA_MODELS 环境变量更改模型安装目录。

  Ollama 默认服务地址端口是:127.0.0.1:11434,Mac 中查看进程监听的端口号命令为 lsof -nP -p <pid>。可通过修改 OLLAMA_HOST 环境变量更改默认端口,eg:launchctl setenv OLLAMA_HOST "0.0.0.0:6006"

  默认情况下,运行模型后,如果 5 分钟未与模型进行交互,将会自动停止该模型。

  Mac 启动 Ollama 后,会在菜单栏上出现一个羊驼图标,但有时这个图标会被“刘海”挡住,导致无法退出 Ollama,这时可以使用 osascript -e 'tell app "Ollama" to quit' 命令退出 Ollama。

  对于开发者,若需要更改默认端口,需修改环境变量:export OLLAMA_HOST=0.0.0.0:6006(如此可将 ollama 的默认端口修改为 6006),之后通过 ollama serve 命令启动 Ollama,通过该命令启动的不会在菜单栏上出现羊驼图标。若出现跨域问题,同样需要修改环境变量:export OLLAMA_ORIGINS="*"

AnythingLLM 篇

  AnythingLLM 有两种安装模式,一种是桌面版,一种是 Docker 版,桌面版只能本地使用,Docker 版相当于是服务版,支持多用户云端使用。本次选用的桌面版,基本的 RAG 功能也都有。Docker vs Desktop Version

  第一次启动 AnythingLLM 时,有一些设置引导,设置「LLM 偏好」时选择 Ollama,其他的都默认即可。创建工作空间之后就可以上传本地文件,建立自己的知识库。

  Shaun 在使用中感觉,AnythingLLM 响应还是比较慢,分析/提炼/归纳/总结本地文档的速度有限。可能是机器配置还是有点低了。

PDFMathTranslate 篇

  命令行使用:

1
2
3
4
5
# 用 ollama 将本地文件 Black Hat Rust.pdf 从英文翻译为中文
pdf2zh Black\ Hat\ Rust.pdf -s ollama -li en -lo zh

# 启动 pdf2zh 网页,免去命令行使用 Web 页面设置翻译参数
pdf2zh -i

  出现 NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3' 警告时可忽略,不影响使用。

  如果出现 huggingface_hub.errors.LocalEntryNotFoundError 错误,需要配置 hugging_face 国内镜像:

1
2
pip3 install -U huggingface_hub hf_transfer -i https://pypi.tuna.tsinghua.edu.cn/simple
export HF_ENDPOINT=https://hf-mirror.com

  同时下载模型到本地(需要具体看报错的是哪个模型,这里是wybxc/DocLayout-YOLO-DocStructBench-onnx):

1
huggingface-cli download --resume-download wybxc/DocLayout-YOLO-DocStructBench-onnx --local-dir .

  选择本地ollama模型作为翻译服务的话,需要配置环境变量:

1
2
3
4
# 本地ollama服务地址
export OLLAMA_HOST=http://127.0.0.1:11434
# 选用phi4模型翻译
export OLLAMA_MODEL=phi4

  有个浏览器插件「沉浸式翻译 - Immersive Translate」同样是个比较好用的翻译工具。不过对于 PDF 文件,都有可能出现译文重叠的现象,需要二次编辑一下,或者将 PDF 格式转换为其他格式(eg:html,epub 等,相关 issue)。

VSC 辅助编程插件篇

Continue

   Continue 插件可结合本地的 ollama 使用 qwen2.5-coder:7b 模型,可辅助读/写代码,需打开以下设置:

  • Continue: Enable Quick Actions
  • Continue: Enable Tab Autocomplete
  • Continue: Show Inline Tip

模型设置添加:

1
2
3
4
5
{
"model": "qwen2.5-coder:7b",
"title": "ollama-qwen2.5-coder",
"provider": "ollama"
}

自动补全设置中 apiKey 保持为空字符串就行。

若需要使用远程部署的 ollama 服务,可以新增参数 "apiBase": "http://<my endpoint>:11434"

Cline

   Cline 插件同样可结合本地的 ollama 使用,可辅助 CR 以及自动化优化修改代码。如果使用 vLLM 部署的 AI 服务,API Provider 选择 OpenAI Compatible,Bsae URL 填 http://ip:port/v1,API Key 随便填就行(eg: ollama),Model ID 则是模型名称(eg: deepseek-r1:14b)。


20250207 更新:

  • 英译中模型推荐使用 qwen2.5:14b;
  • 中文问答聊天模型推荐使用 deepseek-r1:14b,模型会输出详尽的思考过程;

后记

  本地部署 LLM 的好处在于无数据泄漏问题,对于个人使用而言,轻量级的模型也差不多够用了,但即使已经轻量化了,本地运行大模型还是有点吃力,在 Shaun 的电脑上运行 phi4:14b 略显勉强(8 tok/s)。Mac 的内存和显存是共享的,后续如果买新的,有部署 LLM 的需求,最好把内存拉满,由于模型文件也相对较大,有条件的可以把 SSD 也拉满。希望后续大模型的推理能够进一步轻量化,效果也更好,真正实现人人都能使用。

  自 GPT-3 出现以来,也就短短 4 年不到,从大规模的高性能 GPU 集群到单机部署,从胡言乱语到精准命中,各行各业都迎来了 LLM 的冲击,在可预见的未来,LLM 将深刻影响到每一个人,这种影响无关好坏,单纯只是时代的浪潮,LLM 将和操作系统,数据库一样,成为整个 IT 行业的基础设施,就 Shaun 而言,应该很难亲自动手去开发优化 LLM,能做的也就是尽可能的熟练使用。


20260121更新:

  • OpenCode 是真牛逼,需求写好,摸会儿鱼,代码就写完了,编码替身。

参考资料

1、基于Ollama+AnythingLLM搭建本地私有知识库系统

2、ollama搭建本地个人知识库

附录

OpenRouter 白嫖说明

  先随便找个邮箱注册账号,创建 API key,即可白嫖免费模型,当前(2026-04-09)是每天 50 次的免费调用额度,对于实验/测试使用也够了,如果不够,可以多注册几个账号 :P。OpenCode/Claude Code/Codex 接入 OpenRouter 都很方便:

  • OpenCode 接入最简单,直接输入命令 /connect,选择 OpenRouter,再填上创建的 API key,之后即可自由的选择免费模型使用。

  • Claude Code 接入,需设置配置文件,vi ~/.claude/settings.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    "env": {
    "ANTHROPIC_BASE_URL": "https://openrouter.ai/api",
    "ANTHROPIC_AUTH_TOKEN": "sk-or-...",
    "ANTHROPIC_API_KEY": "",
    "ANTHROPIC_MODEL": "minimax/minimax-m2.5:free",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "nvidia/nemotron-3-super-120b-a12b:free",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "stepfun/step-3.5-flash:free",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "stepfun/step-3.5-flash:free",
    "CLAUDE_CODE_SUBAGENT_MODEL": "stepfun/step-3.5-flash:free"
    },
    "model": "minimax/minimax-m2.5:free",
    "availableModels": [
    "nvidia/nemotron-3-super-120b-a12b:free",
    "stepfun/step-3.5-flash:free",
    "minimax/minimax-m2.5:free"
    ]
    }
  • Codex 接入,需要先增加环境变量 export OPENROUTER_API_KEY="sk-or-...",再修改配置文件 vi ~/.codex/config.toml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    model_provider="openrouter"
    model_reasoning_effort="high"
    #model="nvidia/nemotron-3-super-120b-a12b:free"
    model="minimax/minimax-m2.5:free"

    [model_providers.openrouter]
    name="openrouter"
    base_url="https://openrouter.ai/api/v1"
    env_key="OPENROUTER_API_KEY"

    [profiles.minimax-m2.5-free]
    model_provider = "openrouter"
    model = "minimax/minimax-m2.5:free"

    如果是 Codex App,启动时可能还需要选择 API Key 登录,输入创建的 OPENROUTER_API_KEY。

  • Pi 接入,先安装 npm install -g @mariozechner/pi-coding-agent --registry https://registry.npmmirror.com,启动后输入 /login -> Use an API key -> OpenRouter -> [输入apikey] Pi 启动时可能报错 Failed to download fd: The operation was aborted due to timeout 或者 Failed to download fd: fetch failed,可以使用 brew install fd 安装 fd。

Docker 部署 embed 和 rerank 模型

  在部署使用 WeKnora 的时候需要 embed 和 rerank 模型,但 Ollama 至今(20260616)还不支持 rerank,而且消耗系统资源较多,llama.cpp 消耗资源较小,且支持 OpenAI 兼容 api,所以直接用 llama.cpp 部署更合适。

目录结构为:

1
2
3
4
5
6
7
8
./
├── Dockerfile
├── build.sh
├── entrypoint.sh
├── models/
│ ├── Qwen3-Embedding-0.6B-Q8_0.gguf
│ └── qwen3-reranker-0.6b-q8_0.gguf
└── nginx.conf

两个模型可以去 https://hf-mirror.com 下载, qwen3-reranker-0.6b-q8_0.ggufQwen3-Embedding-0.6B-Q8_0.gguf

Dockerfile 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM ghcr.io/ggml-org/llama.cpp:server

# 安装 nginx 反向代理(统一 embed + rerank 到单端口)
RUN apt-get update && apt-get install -y --no-install-recommends nginx \
&& rm -rf /var/lib/apt/lists/*

# 复制模型
COPY models/ /models/

# 复制配置和启动脚本
COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 11216

ENTRYPOINT ["/entrypoint.sh"]

国内的 docker 镜像仓库配置:

1
2
3
4
5
6
7
8
9
10
11
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn",
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
]
}

build.sh 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
set -e

IMAGE="llama-cpp-embed-rerank"
TAG="latest"
TAR="llama-cpp-embed-rerank.tar.gz"
PLATFORM="linux/amd64"
# PLATFORM="linux/arm64"

echo "=== 1/2 Build image (linux/amd64) ==="
docker build --platform "${PLATFORM}" -t "${IMAGE}:${TAG}" "$(dirname "$0")"

echo "=== 2/2 Export compressed tar ==="
docker save "${IMAGE}:${TAG}" | gzip > "${TAR}"

echo "=== Done ==="
ls -lh "${TAR}"
echo ""
echo "传输到目标机器后:"
echo " docker load -i ${TAR}"
echo " docker run -d --name llama-cpp -p 11216:11216 --restart always ${IMAGE}:${TAG}"
echo ""
echo "测试 API:"
echo " # Embedding"
echo " curl http://localhost:11216/v1/embeddings -H 'Content-Type: application/json' -d '{\"model\":\"Qwen3-Embedding-0.6B-Q8_0\",\"input\":\"hello world\"}'"
echo " # Rerank"
echo " curl http://localhost:11216/v1/rerank -H 'Content-Type: application/json' -d '{\"model\":\"qwen3-reranker-0.6b-q8_0\",\"query\":\"什么是AI\",\"documents\":[\"人工智能是...\",\"今天是晴天\"]}'"

entrypoint.sh 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash
set -e

EMBED_MODEL="/models/Qwen3-Embedding-0.6B-Q8_0.gguf"
RERANK_MODEL="/models/qwen3-reranker-0.6b-q8_0.gguf"

echo "=== Starting llama.cpp servers ==="

# Embedding server (内部端口 8081)
echo " Embedding: ${EMBED_MODEL} → :8081"
/app/llama-server \
--model "${EMBED_MODEL}" \
--alias "Qwen3-Embedding-0.6B-Q8_0" \
--embedding \
--port 8081 \
--host 127.0.0.1 \
&
# parallel 和 ctx-size 不建议显式指定,通过环境变量配置更好 LLAMA_ARG_N_PARALLEL=4 和 LLAMA_ARG_CTX_SIZE=4096
# --parallel 4 \
# --ctx-size 4096 \
# &

# Reranker server (内部端口 8082)
echo " Reranker: ${RERANK_MODEL} → :8082"
/app/llama-server \
--model "${RERANK_MODEL}" \
--alias "qwen3-reranker-0.6b-q8_0" \
--rerank \
--port 8082 \
--host 127.0.0.1 \
&
# parallel 和 ctx-size 不建议显式指定,通过环境变量配置更好 LLAMA_ARG_N_PARALLEL=4 和 LLAMA_ARG_CTX_SIZE=4096
# --parallel 4 \
# --ctx-size 4096 \
# &

# 等待服务就绪
echo " Waiting for services..."
for port in 8081 8082; do
for i in $(seq 1 60); do
if curl -sf http://127.0.0.1:${port}/health > /dev/null 2>&1; then
echo " Port ${port} ready"
break
fi
sleep 1
done
done

# 启动 nginx 反向代理(前台运行)
echo " Starting nginx on :11216"
echo ""
echo "=== Endpoints ==="
echo " Embedding: POST http://localhost:11216/v1/embeddings"
echo " Rerank: POST http://localhost:11216/v1/rerank"
echo " Models: GET http://localhost:11216/v1/models"
echo " Health: GET http://localhost:11216/health"
echo ""

exec nginx -g 'daemon off;'

nginx.conf 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
events {
worker_connections 1024;
}

http {
server {
listen 11216;

# Embedding API
location /v1/embeddings {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_read_timeout 300s;
}

# Rerank API
location /v1/rerank {
proxy_pass http://127.0.0.1:8082;
proxy_set_header Host $host;
proxy_read_timeout 300s;
}

# Score API (reranker 的另一种调用方式)
location /v1/score {
proxy_pass http://127.0.0.1:8082;
proxy_set_header Host $host;
proxy_read_timeout 300s;
}

# Models API → embed server (至少返回一个可用模型列表)
location /v1/models {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
}

# 健康检查
location /health {
proxy_pass http://127.0.0.1:8081/health;
}
}
}

之后直接执行 build.sh 即可创建并打包所需镜像。