DeepSeek多端部署:Ollama、Termux与Open WebUI实践

发布时间:2026/10/24 14:13:25
DeepSeek多端部署:Ollama、Termux与Open WebUI实践

简介:这份15KB的docx文档面向希望在个人电脑、手机和浏览器中部署DeepSeek大模型的研究人员、开发者与技术爱好者,围绕Ollama本地运行、移动端接入和Open WebUI可视化使用三条路线,整理跨平台部署方案与关键操作命令。文档按macOS、Linux、Windows、iPhone与Android等环境展开,包含模型拉取、终端交互、快捷指令与API Key配置、Termux依赖安装及Ollama编译启动、Docker Desktop与Open WebUI集成管理等内容,并附官方网址便于核对最新版本与支持资料。包内共1个docx文件,压缩包约15KB,篇幅精简但覆盖本地、移动端与Web端的主要落地路径。已有3771人学习下载,适合需要快速搭建DeepSeek实验环境、对照命令排查部署问题,或把NLP对话能力接入个人设备与业务流程的读者参考。

1. 三端并存的需求,先想清楚模型到底跑在哪

上周有个做后端的朋友问我,能不能在那台 16G 内存的笔记本上跑一个能离线对话的模型,不联网、数据不出本机。这个需求往深了说,其实是一整套多平台 DeepSeek 部署方案:桌面端用 Ollama 拉模型起服务,手机端用 Termux 或者系统快捷指令兜底,浏览器端再套一层 Open WebUI 做成团队都能点开就用的界面。三条路线的差别不在模型本身,而在推理跑在哪里、请求从哪发出去。搞不清这一点,你会在手机上装完一堆东西,最后发现跑的其实是别人的服务器。这篇按「先选型、再跑通、最后改」的顺序,把这套 AI 模型部署的落地路径拆开讲,新手照着能跑,熟手能看到进程边界和接口设计。

2. 三条路线的架构分层与选型账本

2.1 从进程边界看:谁在真正做推理

把三条路线摆在一起看,最容易混淆的是「客户端」和「推理端」的位置。桌面方案里,Ollama 是一个常驻后台的服务进程,默认监听127.0.0.1:11434,命令行里敲的ollama run只是一个 HTTP 客户端,它把对话内容 POST 给本地服务,服务加载模型权重做前向计算再返回 token。模型和算力都在你自己的机器上,断网照样能用。

移动端要分成两种完全不同的东西。iPhone 上的快捷指令,本质是一个图形化的 HTTP 请求构造器,它自己不加载任何模型权重,只负责把「你说的话」打包成 JSON,发到一个兼容 OpenAI 格式的远程接口上,再把返回的文本念出来或显示出来。它解决的是「手机上有个顺手的入口」,而不是「手机上跑推理」。Android 上的 Termux 则相反,它给你一个接近 Linux 的终端环境,你可以把 Ollama 源码拉下来编译成可执行文件,真正在 ARM 芯片上跑推理,代价是编译门槛和内存天花板。

Web 端是第三种形态。Open WebUI 是一个前后端一体的应用,后端用 Python 写,前端是浏览器页面,中间通过 HTTP 去调 Ollama 的接口。它自己也不推理,价值在于多会话管理、模型切换、提示词预设、多用户登录这些产品化能力。理解了这三层的分工,后面每一步命令你都知道自己在动哪一块。

提示:判断一个「手机跑大模型」的教程靠不靠谱,就看它有没有说清楚推理发生在本地还是远端。只让你填 API Key 的,都是远端。

2.2 模型规格与硬件账本

Ollama 的模型名带 tag,冒号后面是规格,比如deepseek-r1:7b里的7b指 70 亿参数量级。参数量直接决定内存占用,而内存是这三条路线里最先撞墙的资源。下面这张表是我在几种设备上实测后的粗略量级,具体数值会随量化方式和上下文长度浮动,但用来做选型判断够用。

模型规格量化后权重大致体积建议可用内存典型落地设备
1.5b1~2 GB4 GB 以上手机 Termux、低配轻薄本
7b / 8b4~6 GB8~12 GB主流笔记本、台式机
14b8~10 GB16 GB独显机器或大内存工作站
32b 及以上18 GB 起32 GB 以上服务器、多卡环境

选型逻辑很简单:手机端不要碰 7b 以上,Android 系统对单个应用的内存限制和后台杀进程机制会让你跑到一半被 OOM 干掉;桌面端 16G 内存跑 7b 是甜点区,再大就要看有没有独立显卡;如果是团队共用,模型放在一台常开的机器上,其他设备全部走 HTTP 调用,这才是 AI 模型部署里最省资源的拓扑。

还有一点容易被忽略:上下文长度也吃内存。Ollama 默认的上下文窗口不算大,如果你在 WebUI 里把上下文调到很长,同样的 7b 模型内存占用可能翻倍。显存不够时它会退到 CPU 推理,速度掉一个数量级,这个现象后面避坑章节会专门讲。

2.3 安装 Ollama 并跑通第一句回复

桌面端是所有路线的基准,先把它跑通,后面手机和 WebUI 才有东西可连。macOS 和 Linux 用官方脚本装最省事,Windows 直接下安装包双击。

# macOS / Linux:一行脚本完成安装,会自动注册系统服务 curl -fsSL https://ollama.com/install.sh | sh # 验证安装,输出类似 ollama version 0.x.x ollama --version # 拉取并进入交互式对话,第一次会下载权重文件 ollama run deepseek-r1:7b

ollama run这个命令做了两件事:先检查本地有没有对应 tag 的模型,没有就自动pull;下载完成后启动推理并进入 REPL。第一次执行会看到分层的下载进度条,每一层是模型文件的一个分片,中断了重跑会续传已经下好的层,不用从头再来。

装完之后要确认服务状态。Linux 下安装脚本会创建 systemd 单元,用systemctl status ollama看;macOS 是 launchd 托管的菜单栏应用,ps aux | grep ollama能看到ollama serve进程。如果你想让局域网内其他设备也能连,需要让服务监听所有网卡:

# 仅限可信内网使用,暴露到公网等于把模型算力白送人 OLLAMA_HOST=0.0.0.0:11434 ollama serve

这个环境变量是很多教程漏掉的一步。默认只绑127.0.0.1,你在另一台机器上curl会直接连接被拒,然后误以为是防火墙问题。改完之后用curl http://<主机IP>:11434/api/tags验证,能返回 JSON 列表说明服务通了。这一步跑通,桌面端的基础就打好了,接下来两条路都是在它的接口之上做文章。

3. 移动端落地:iPhone 快捷指令与 Android Termux 编译

3.1 iPhone 快捷指令:把 HTTP 请求包成系统级入口

捷径(Shortcuts)这个路子被很多教程写得很玄,其实拆开就三个动作:接收输入、发请求、展示结果。你在 Safari 里导入一个现成的快捷指令,它内部是一个「获取 URL 内容」动作,方法选 POST,请求体是一段 JSON,结构跟 OpenAI 的 Chat Completions 接口一致。

{ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "你是一个简洁的中文助手"}, {"role": "user", "content": "把这段话翻译成英文"} ], "temperature": 0.7, "stream": false }

model字段决定服务端用哪个模型,messages是标准的多轮对话数组,stream设成false是为了让快捷指令一次性拿到完整文本,因为流式返回在捷径里处理起来很别扭。请求头里必须带Authorization: Bearer <你的Key>和Content-Type: application/json,少一个就是 401 或 415。

这里要说清楚一件事:快捷指令的便利性建立在「远端有服务」的前提上,它跟本地部署是两条路。如果你确实需要离线,就别在 iPhone 上折腾这条路,直接把模型跑在桌面机上,手机通过局域网访问那个 11434 端口,虽然要保证同一网段,但数据不出内网。

注意:API Key 写死在快捷指令里,任何拿到你手机或者你分享出去的快捷指令的人都能直接看到明文。别用主账号的 Key,单独申请一个限额的子 Key,用完随时吊销。

首次运行时系统会弹权限确认,问你是否允许访问某个域名,必须点允许,否则请求会被静默拦截,表现就是「点了没反应」。这个交互坑我在第一次配的时候卡了十几分钟,最后是靠捷径的「显示结果」动作把错误码打出来才定位到的。建议你在调试阶段加一个显示返回内容的动作,把 HTTP 状态码一起打出来。

3.2 Android Termux:从装依赖到编译出可执行文件

Termux 是 Android 上最接近 Linux 的环境,它不走 proot 那一套虚拟化,而是直接跑在 Android 的 Linux 内核上,所以编译出来的二进制能原生执行。整套流程分三步:配环境、拉源码、编译。

# 1. 更新包管理器并装编译工具链 pkg update && pkg upgrade -y pkg install git cmake golang libjpeg-turbo -y # 2. 申请共享存储权限,方便把模型文件放外置卡 termux-setup-storage # 3. 拉 Ollama 源码,depth 1 只取最新一次提交,省流量省时间 git clone --depth 1 https://github.com/ollama/ollama.git cd ollama # 4. 生成版本信息文件,再编译出单文件二进制 go generate ./... go build .

go generate这一步不能省,它会跑//go:generate注释标记的脚本,把版本号、构建时间之类的信息注入到源码里,跳过它编译出来的程序在--version时会显示 dev 或者干脆报错。go build .在当前目录产出名为ollama的可执行文件,第一次编译在手机上大概十几到三十分钟,取决于芯片性能,中途锁屏可能被系统挂起,建议先执行termux-wake-lock拿一个唤醒锁。

依赖里libjpeg-turbo容易被漏掉,它是图像处理相关的库,Ollama 的多模态能力会用到。缺了它编译过程不一定立刻报错,但运行时加载模型可能崩,这种延迟暴露的问题最难查。装依赖时留意终端有没有红色的E:开头错误,有的话先解决依赖再往下走。

3.3 启动服务与模型拉取验证

编译完成后不要急着run,先把服务单独起起来验证端口。

# 后台启动服务,& 让它脱离当前终端 ./ollama serve & # 验证服务是否在监听,正常返回版本信息的 JSON curl http://127.0.0.1:11434 # 手机内存有限,先拉最小规格的模型试水 ./ollama pull deepseek-r1:1.5b # 跑起来对话 ./ollama run deepseek-r1:1.5b

curl这一步是整个移动端部署里最重要的验证点。返回Ollama is running或者一段版本 JSON,说明服务活着,后面拉模型失败就只可能是网络或磁盘问题;如果 curl 直接连接失败,那就别往下走,先看serve进程是不是被系统杀了。Android 的后台管理很激进,切到别的应用几分钟,Termux 的进程就可能被回收,表现是「刚才还能用,现在连不上」。长期使用的话,在系统设置里给 Termux 加电池优化白名单。

模型规格上老老实实选 1.5b。7b 在旗舰机上勉强能加载,但一旦上下文变长就会触发内存回收,回应到一半断掉,这个体验比慢更糟。真要在手机上用大模型,正确姿势是手机当客户端,桌面机当推理端,中间走局域网。

4. Open WebUI 与 Docker:浏览器里的模型工作台

4.1 容器网络:两个服务怎么互相看见

Open WebUI 官方推荐用 Docker 跑,因为它的依赖链比较长,直接装 Python 环境容易和系统包打架。容器化之后有个关键问题:WebUI 容器怎么访问 Ollama。有两条路。

第一条是 Ollama 跑在宿主机上,容器通过特殊域名回连宿主机。macOS 和 Windows 的 Docker Desktop 提供host.docker.internal,Linux 上要用宿主机的实际内网 IP 或者172.17.0.1这个网桥地址。第二条是两个都放容器里,用 Docker 的自定义网络让它们通过服务名互访,这种方式最干净,也方便整体迁移。

services: ollama: image: ollama/ollama:latest ports: - "11434:11434" volumes: - ollama:/root/.ollama # 模型权重持久化,删容器不丢模型 open-webui: image: ghcr.io/open-webui/open-webui:main ports: - "3000:8080" # 左边是宿主机端口,右边是容器内端口 environment: - OLLAMA_BASE_URL=http://ollama:11434 volumes: - open-webui:/app/backend/data # 会话记录、用户、配置都在这里 depends_on: - ollama volumes: ollama: open-webui:

OLLAMA_BASE_URL里写的是服务名ollama,这是 Docker 内置 DNS 解析的结果,两个容器必须处在同一个 compose 网络里才生效。你要是把它改成localhost:11434,容器里的 localhost 指的是容器自己,会一直连不上,这是最高频的翻车点。端口映射3000:8080里的 8080 是 WebUI 容器内监听的真实端口,改成别的宿主机端口不影响容器内部。

depends_on只保证启动顺序,不保证 Ollama 已经准备好接受请求,所以第一次打开页面时如果模型列表是空的,等十几秒刷新一下就好,不是配置错了。

4.2 启动、初始化与模型拉取

# 在 compose 文件所在目录执行,-d 后台运行 docker compose up -d # 看两个容器的状态,都应该是 running docker compose ps # 如果模型列表为空,进容器手动拉一个 docker compose exec ollama ollama pull deepseek-r1:7b

首次访问http://localhost:3000会引导你创建管理员账号,账号信息存在/app/backend/data下的 SQLite 里,所以这个目录必须挂卷,否则重建容器就全丢了。登录之后进「设置 → 模型」,能看到 Ollama 里已有的模型列表,也可以直接在界面上填模型名拉取,效果等同于命令行ollama pull。

在界面里点「新建对话」,选模型,发一句话测试。如果转圈很久最后报连接错误,八成是OLLAMA_BASE_URL配错了,用docker compose logs open-webui看日志里那一行实际请求的地址。如果报模型不存在,就是拉取没完成或者 tag 拼错,注意deepseek-r1和deepseek-r1:7b是两个不同的引用,前者会拉默认 tag。

5. 避坑与排查:五个真实翻车现场

5.1 现象:局域网设备连不上 11434,宿主机自己能通

原因:Ollama 默认只绑127.0.0.1,这是出于安全考虑的设计,不是 bug。很多人以为装完就能被外部访问,结果在另一台机器上 curl 一直是 connection refused,转而去查防火墙,白折腾。

解决:用OLLAMA_HOST=0.0.0.0:11434重启服务。Linux 下如果是 systemd 托管的,要改 unit 文件里的Environment再systemctl daemon-reload,直接在 shell 里 export 对已经跑起来的服务无效。改完记得确认这台机器的 11434 没有暴露到公网,Ollama 本身没有鉴权。

5.2 现象:容器里 WebUI 一直提示连不上模型服务

原因:OLLAMA_BASE_URL写成了localhost或127.0.0.1。容器有自己的网络命名空间,localhost 指向容器自身,而 Ollama 根本没跑在这个容器里。

解决:两个服务在同一 compose 网络时用服务名;Ollama 在宿主机时,macOS/Windows 用host.docker.internal,Linux 用宿主机的内网 IP。改完docker compose up -d重建容器才生效,只改 yml 不重启是没用的。

5.3 现象:Termux 里go build报错,提示找不到某个 C 库

原因:Ollama 的部分依赖用到了 CGO,需要系统里有对应的开发库。只装了 golang 没装cmake和libjpeg-turbo,编译到链接阶段就断。

解决:pkg install cmake libjpeg-turbo -y补齐,如果还报错,看错误信息里cannot find -lxxx的库名,pkg search xxx找对应的包。别去网上抄别人的完整依赖列表,Termux 的包名和标准 Linux 发行版不一样,抄错了会装一堆用不上的东西。

5.4 现象:模型拉到 90% 卡住或者报校验失败

原因:权重文件按层分片下载,网络抖动会让某一层中断。Ollama 有续传机制,但磁盘空间不足时它会先写后失败,表现就是进度条卡死。

解决:先df -h看剩余空间,7b 模型至少要留 10 GB 余量。空间够的话 Ctrl+C 中断重跑ollama pull,已完成的层不会重下。反复失败就换ollama run直接触发拉取,有时比单独 pull 的容错好一些。

5.5 现象:手机端请求返回 401 或 403

原因:Key 失效、额度耗尽,或者请求头里的Authorization格式不对。Bearer 后面必须有一个空格,少这个空格服务端解析不到 token。

解决:先用 curl 在电脑上直接打那个接口,把请求头和请求体都带上,确认 Key 本身有效。电脑能通手机不通,问题就在快捷指令的变量替换上,检查 Key 是不是被当成文本而不是变量填进了请求头。

6. 二次开发:把模型能力接进自己的业务逻辑

跑通之后真正有价值的部分才开始。Ollama 暴露的/api/chat接口是最好用的接入点,它跟 OpenAI 的格式基本兼容,你原有的 NLP 应用代码几乎不用改,只换 base_url 和模型名。

import requests, json def chat(messages, model="deepseek-r1:7b", host="http://127.0.0.1:11434"): resp = requests.post( f"{host}/api/chat", json={ "model": model, "messages": messages, "stream": False, # 关掉流式,方便一次性拿完整结果 "options": {"temperature": 0.3, "num_ctx": 4096} # 上下文别开太大,吃内存 }, timeout=120 # 本地推理慢,超时要给足 ) resp.raise_for_status() return resp.json()["message"]["content"] print(chat([{"role": "user", "content": "用三句话说明什么是向量检索"}]))

options里的num_ctx是最值得调的参数,它决定模型能看多长的上下文。默认值通常够用,调大到 8192 以上会显著增加内存占用,7b 模型在 16G 机器上很容易因此退到 CPU 推理。temperature做分类、抽取这类任务时压到 0.1~0.3 更稳,做创意生成再拉到 0.8。

改造时我一般会包一层自己的服务,把模型调用、超时重试、日志记录收在一处,业务代码只依赖这个包装层。好处是换模型只改一个地方,而且能做降级:大模型超时就切到 1.5b 的小模型先返回,用户体验比直接报错好得多。验证方法很朴素,写个脚本连续打 20 次接口,记录 P95 延迟和失败率,再把num_ctx和模型规格分别调一档对比,数据会告诉你这台机器适合跑多大的模型。

WebUI 那一层也有改造空间,它自带 OpenAI 兼容的/api/v1/chat/completions,意味着任何支持自定义 base_url 的第三方工具都能接进来。我在一个内部工具里就是这么做的,前端不动,后端把 base_url 从云端服务改成局域网地址,数据不出内网这件事就自然满足了。

踩过最疼的一次是没做超时控制,一个批量任务把模型服务打满,后面所有请求排队,前端全部转圈,看起来像服务挂了。从那以后我每次接模型接口,都强制走一遍「单请求 → 压测 → 加超时和降级」这三步,不管多急都不跳。希望帮到你。

本文还有配套的精品资源,点击获取