🧠 第二章 模型部署:在本地使用DeepSeek
📄 一句话概括

探测 127.0.0.1:11434,结果是 TimeoutError,1001.6 毫秒——沙箱里没装 Ollama,端口是关的。 这一节真正要学的不是「怎么装」,是怎么判断装成了:端口通了不算, GET /api/tags 能回 JSON 才算。

📝 本节和前面几节的关系

上一节把 Python 环境备好了。 这一节装 Ollama——它是整套本地部署的中枢,前端、脚本、插件全都通过它说话。

一、Ollama 到底替你做了什么

如果直接跑 llama.cpp,你要自己下模型文件、自己转格式、自己算显存够不够、自己写 HTTP 服务、 自己处理并发。Ollama 把这五件事打包成了一个可执行文件。 装完之后它做两件事:在后台常驻一个服务,监听 11434 端口; 提供一个 HTTP 接口,让任何前端都能调模型。 所以「装 Ollama」的真正含义是装一个后台服务,不是一个命令行工具。 这一点理解错了,后面所有排查都会走偏——你会去查「命令为什么找不到」, 而真正的问题在「服务有没有起来」。 这也是为什么下一节的 Cherry Studio、再下一节的 Page Assist,配置时填的都是一个地址加一个端口, 而不是一个程序路径。它们连的是服务,不是程序。

二、装:三个平台,三条路

Windows:官网下载安装包,双击,下一步到底。装完托盘区会多一个羊驼图标,服务自动起来。 macOS:下载 dmg 拖进应用程序,或者用包管理器一条命令。 Linux:官网给了一条 curl 脚本,跑完自动装成系统服务。 三个平台装法不同,但装完的状态是一样的:一个监听 11434 的后台服务。所以后面所有判断和排查, 三个平台通用。 有一点各平台都一样:装完要重开终端。 PATH 是终端启动时读的,装完不重开,敲 ollama 会说找不到命令——这不是没装好,是终端还不知道。

三、判断装成了没有:两级判据

一级:TCP 端口 11434 能不能连上。 能连上,说明有东西在监听。 二级:GET /api/tags 能不能返回 JSON。 能返回,说明监听的这个东西确实是 Ollama, 而且它在正常工作。 为什么要两级?因为只做一级会误判。 如果 11434 被别的程序占了(或者你自己起了一个测试服务),端口照样能连上,但那个东西不是 Ollama。 Demo 里把这两级都跑了一遍。第一级在沙箱里是失败的——TimeoutError,等了 1001.6 毫秒。 第二级换成本地起的一个同接口假服务,GET /api/tags 返回 200,列出了 1 个模型。 这里要诚实说清: 沙箱里没有 Ollama,所以第一级测的是「没有」这个事实。第二级测的是「有」的时候长什么样。 两个都跑,才叫把这件事讲完。

四、装完之后卡住的三种情况

服务没起。 托盘图标在,但进程挂了。判据就是一级探测——端口连不上。 重启用服务,或者在终端里手动跑一次前台模式看报什么错。 端口被占。 11434 被别的程序用了,Ollama 起不来但你不知道。 判据是二级探测——端口通,但 /api/tags 返回的不是 Ollama 的格式。 防火墙拦了。 只影响「别的机器访问你这台」,本机访问不受影响。 所以如果你只是自己用,这一条可以先不管。 还有个隐性情况:装在用户目录,用管理员权限的终端找不到命令。两套 PATH 是分开的。 判断方法很简单:普通终端能跑、管理员终端不能跑,就是这一条。

五、动手 Demo:端口探测 1001.6 毫秒超时;换成假服务,/api/tags 才回 200

先用一个 TCP 连接探 11434 端口,看有没有人在监听、等了多久。 再在本地起一个同接口的假服务,请求 /api/tags,看「装好之后」这个接口应该返回什么。

host, port = u'127.0.0.1', 11434
s = socket.socket()
s.settimeout(1.0)
try:
    s.connect((host, port))
    alive = True
except Exception as e:
    alive, why = False, type(e).__name__

# 第二级:接口答不答话
req = urllib.request.Request(url + u'/api/tags')
print(json.loads(urllib.request.urlopen(req).read()))

实跑输出:

--------------------------------------------------------------------
装 Ollama:装没装成,用一个端口探测就能判断
--------------------------------------------------------------------
  目标                     127.0.0.1:11434
  结果                     连不上(TimeoutError)
  耗时                     1001.6 毫秒

  这一步是诚实的:**沙箱里没有装 Ollama**,所以端口是关的。
  报 TimeoutError,意思是「发出去的连接没有任何回应」。
  它比 ConnectionRefused 更含糊:可能是没服务,也可能被防火墙拦了。
  装上 Ollama 之后,同一个探测会变成「服务在」,毫秒数会掉到 1 以下。

  再往上一层:不光端口要通,接口还要能回话。
  为了演示「装好之后长什么样」,这里在本地起了一个同接口的假服务。
  GET /api/tags            200,列出 1 个模型
  第一个模型               deepseek-r1:7b(Q4_K_M)

  所以「装好了没有」有两个判据,缺一不可:
  ① 端口在不在             TCP 连 11434,能连上才算服务起来了
  ② 接口答不答             GET /api/tags 返回 JSON,才算这个服务是 Ollama
  只做第一条,遇到「别的程序占了 11434」会误判成装好了。

结果:连不上(TimeoutError),耗时 1001.6 毫秒。 这个 1001.6 就是 settimeout(1.0) 那个 1 秒——它把超时等满了。 这里要分清两种异常,它们说的不是一件事:ConnectionRefusedError 意思是「这个端口上没人监听」, 对方主机明确拒绝了你。 TimeoutError 意思是「发出去没有任何回应」,对方可能不在、可能被防火墙丢了、 也可能压根没这个地址。 沙箱里报的是后者,比前者更含糊。 所以脚本里不能只判「成没成」,要把异常类型打出来——不同的类型对应不同的排查方向。 第二级换成本地假服务之后就通了:GET /api/tags 返回 200,列出 1 个模型 deepseek-r1:7b, 量化档位 Q4_K_M。这就是真 Ollama 装好、模型也下完之后,这个接口该有的样子。 最后那两句是这一节的结论:端口在不在、接口答不答,两条都要过。 只做第一条,遇到「别的程序占了 11434」就会误判成装好了——然后你会花两小时去查模型为什么下不动。

30 秒实验

  1. 换一个端口探测(比如 8080、22),看结果有什么区别。 同一个脚本、不同端口,能立刻分辨「服务没起」和「端口被占」。
  2. 把 settimeout 从 1.0 改成 0.1,看耗时怎么变。 超时值不是随便设的:设太小会把「慢」误判成「没有」,跨机器探测时尤其明显。
  3. 把 /api/tags 改成 /api/version 再请求一次。 真 Ollama 也有这个接口——多探一个接口,就多一条「它确实是 Ollama」的证据。

常见误区

  1. 「装了就有命令」。PATH 在终端启动时读入,装完不重开终端就是找不到命令。这跟装没装好是两件事。
  2. 「能打开界面就是服务在」。界面可能连的是远程地址,也可能自己缓了一份。 判断服务在不在,只能靠端口和接口。
  3. 「端口通了就装好了」。别人占了 11434 也通。 必须再请求一次 /api/tags,看返回的是不是 Ollama 的格式。
自测三问(点开对答案) 1. 为什么用端口探测来判断「装成了没有」? —— 因为 Ollama 装完的本质是「起了一个监听 11434 的后台服务」。服务在不在,端口最直接。 命令在不在、界面能不能开,都绕了一圈。 2. TimeoutError 和 ConnectionRefusedError 有什么区别? —— Refused 是对方明确拒绝——端口上确实没人监听。 Timeout 是压根没有回应——可能没服务,也可能被防火墙丢了包。后者更含糊,排查方向更多。 3. 为什么还需要第二级判据? —— 因为端口可能是别的程序占的。 只有 `/api/tags` 返回 Ollama 格式的 JSON,才能确认「监听 11434 的这个东西是 Ollama, 而且在正常工作」。

服务起来了,但它里面一个模型都没有。下一节看模型怎么下、下哪个档、要下多久。

相关笔记