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

前端填完地址和模型名,背后就一个 POST:/v1/chat/completions。 请求体三个字段,响应里只读 choices[0].message.content,往返 2.0 毫秒。 「连不上模型」95% 出在地址或模型名,不在前端本身。

📝 本节和前面几节的关系

上一节量完速度了,但对着命令行说话不现实。 这一节配一个图形前端,并且把它背后真正发的那个请求摊开看。

一、前端只做三件事

不管叫 Cherry Studio、Chatbox 还是别的什么,只要它说「兼容 OpenAI 接口」,干的事就三件: 拼一个 JSON、发一个 HTTP 请求、把返回的文本显示出来。 所以换哪个前端,本质上是换皮肤。 真正决定体验的是背后那个服务——Ollama 有没有起、模型加载了没有、上下文够不够。 理解这一点之后,「前端连不上」这类问题就有了明确的排查顺序: 先验服务(用上一节的端口探测),再验请求(用它发的那个 JSON 手打一遍),最后才怀疑前端。 Demo 里就是在本地起了一个同接口的假服务,把「前端会发什么、服务端会回什么」完整跑一遍。 看懂这一来一回,配置界面上的每一项你就都知道它管什么了。

二、四项配置,缺一项就白屏

API 地址:http://127.0.0.1:11434。 注意末尾不要多加 /v1——很多前端会自己拼路径,你加了就变成 /v1/v1/chat/completions。 API Key:本地服务根本不校验,随便填一个非空的就行。但有些前端要求必填,不填不让保存。 模型名:必须和 /api/tags 里列出的一字不差。 deepseek-r1:7b 写成 deepseek-r1-7b,或者少写 :7b,都会失败。 上下文长度:默认值常常偏小(2048 之类),长文档要手动调大。 这一项在界面上最不起眼,但它是「模型记不住前面」的常见原因。

三、为什么填了地址还要填模型名

因为一个 Ollama 里可以装十几个模型。地址只告诉前端「服务在哪」,模型名告诉它「调哪一个」。 所以模型名不对的时候,报错不是「连不上」,而是「找不到模型」——这两类错的修法完全不同。 看状态码就能分开:4xx 是请求问题,连不上是服务问题。 Demo 里把模型名改成 deepseek-r1:99b 试了一次。 假服务照样回了 200(它不校验),但真 Ollama 会回 404 加一句 model not found。 这个差别正好说明「假服务只能演示格式,不能演示校验」。 实践上最省事的做法:先用 curl 或浏览器打开 /api/tags,把返回里的模型名原样复制过去。 复制比手打少一半的错。

四、出错的时候按这个顺序查

完全连不上(报网络错误):服务没起,回到 2.1.3 的端口探测。 4xx:请求本身有问题——地址多了 /v1、模型名不对、字段名拼错。 把 Demo 里那个请求体抄下来手打一遍,错误信息会具体得多。 200 但回答是空的:模型名对,但模型还在加载。第一次请求要把它读进显存,几十秒是正常的。 第二次就快了。 能答但答得很短、好像忘了前面:上下文长度设小了,或者模型本身的上下文窗口就小。 很慢:回到上一节。这不是配置问题,是硬件问题。

五、动手 Demo:前端只发一个 POST:请求体三个字段,响应只读一个字段

在本地起一个 OpenAI 兼容的假服务,用它模拟 Cherry Studio 背后那一次调用:打印请求体的完整结构、 响应的关键字段、状态码和往返耗时;再把模型名改错,看服务端的反应。

body = {u'model': u'deepseek-r1:7b',
        u'messages': [{u'role': u'user', u'content': u'你好'}],
        u'stream': False}

req = urllib.request.Request(url + u'/v1/chat/completions',
                             data=json.dumps(body).encode(),
                             headers={u'Content-Type': u'application/json'})
with urllib.request.urlopen(req, timeout=3) as r:
    resp = json.loads(r.read())
    print(resp[u'choices'][0][u'message'][u'content'])

实跑输出:

--------------------------------------------------------------------
配 Cherry Studio:前端填的那个地址,实际发的是什么
--------------------------------------------------------------------
  服务地址                 http://127.0.0.1:51942

  Cherry Studio 里填完地址和模型名,它背后就是这么一个 POST:
  请求体(就是这三行):
    {
      "model": "deepseek-r1:7b",
      "messages": [
        {
          "role": "user",
          "content": "你好"
        }
      ],
      "stream": false
    }
  响应体里前端真正会读的,只有两个字段:
  choices[0].message       好的,我在。
  finish_reason            stop
  状态码                   200
  往返耗时                 2.0 毫秒

  提示 token               12
  生成 token               5

  **前端只做三件事:拼 JSON、发 HTTP、把 choices[0] 里的文本显示出来。**
  所以「前端连不上模型」这件事,95% 出在地址或模型名,不在前端本身。

  把模型名改错,看服务端怎么答:
  状态码                   200
  服务端回的 model         deepseek-r1:99b
  这里因为是假服务,它照样回了 200——**真 Ollama 会回 404 加一句 model not found。**
  排查时先看状态码:4xx 是「你请求错了」(地址/模型名/字段),
  连不上是「服务没起」。**这两类错修法完全不同。**

  完整的前端配置只有四项,缺一项就白屏:
  ① API 地址               http://127.0.0.1:11434(末尾不要多加 /v1)
  ② API Key                本地服务随便填,它根本不校验
  ③ 模型名                 必须和 /api/tags 里列出的一字不差
  ④ 上下文长度             默认值常常偏小,要手动调大

请求体就那么几个字段:model 指定模型、messages 是对话历史、stream 决定要不要流式返回。 界面上那一堆选项,最后都落到这三行里。 响应体里前端真正会读的只有两个: choices[0].message.content(回答文本)和 finish_reason(为什么停)。 其余 id、object、usage 都是附带信息。 往返 2.0 毫秒——这个数在本机假服务上是合理的(没有模型推理)。 真模型第一次请求会是几十秒,差别就在「要不要把权重读进显存」。 把模型名改成 deepseek-r1:99b 之后,假服务照样回 200。 这不是 bug,是假服务没做校验——它证明了一件事:「能返回 200」不等于「模型名对」。 所以最后那四项配置值得抄下来:地址、Key、模型名、上下文长度。 四个都填对,界面才能用;错的顺序通常就是从后往前。

30 秒实验

  1. 把 stream 改成 True,看返回变成什么样(每行一个 JSON,最后一行带 done)。 这就是界面上「打字机效果」的来源。
  2. 给请求体加一个不存在的字段(比如 temperature: 99),看假服务和真服务分别怎么反应。 真服务通常会忽略不认识的字段,而不是报错。
  3. 把地址末尾加上 /v1 再请求一次,路径变成 /v1/v1/chat/completions,会得到 404。 这是最常见的配置错误,一条就能验证。

常见误区

  1. 「前端越贵越好」。前端只负责拼 JSON 和显示文本,真正决定效果的是背后的模型和服务。 换前端不会让模型变聪明。
  2. 「填了 API Key 才安全」。本地服务不校验 Key,填什么都能过。 它的作用是「让界面能保存配置」,不是安全。
  3. 「API 地址要带 /v1」。带了就会变成双 /v1。基地址填到端口为止,路径让前端自己拼。
自测三问(点开对答案) 1. 前端在整条链路里到底做了什么? —— 三件事:拼一个 JSON 请求体、发一个 HTTP POST、把响应里的文本显示出来。 模型加载、推理、上下文管理全在服务端。 2. 为什么填了地址还要填模型名? —— 地址只说「服务在哪」,一个服务里可能有十几个模型。 模型名告诉前端调哪一个,写错就报「找不到模型」而不是「连不上」。 3. 「200 但回答是空的」最可能是什么原因? —— 模型名对,但模型还在加载——第一次请求要把权重读进显存,几十秒正常。 也可能是上下文长度设成了 0 或极小值。

Cherry Studio 是桌面端,走的是 OpenAI 兼容接口。下一节换个前端——浏览器扩展,它走的是另一套接口。

相关笔记