前端填完地址和模型名,背后就一个 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 秒实验
- 把
stream改成 True,看返回变成什么样(每行一个 JSON,最后一行带 done)。 这就是界面上「打字机效果」的来源。 - 给请求体加一个不存在的字段(比如
temperature: 99),看假服务和真服务分别怎么反应。 真服务通常会忽略不认识的字段,而不是报错。 - 把地址末尾加上
/v1再请求一次,路径变成/v1/v1/chat/completions,会得到 404。 这是最常见的配置错误,一条就能验证。
常见误区
- 「前端越贵越好」。前端只负责拼 JSON 和显示文本,真正决定效果的是背后的模型和服务。 换前端不会让模型变聪明。
- 「填了 API Key 才安全」。本地服务不校验 Key,填什么都能过。 它的作用是「让界面能保存配置」,不是安全。
- 「API 地址要带 /v1」。带了就会变成双
/v1。基地址填到端口为止,路径让前端自己拼。
自测三问(点开对答案)
1. 前端在整条链路里到底做了什么? —— 三件事:拼一个 JSON 请求体、发一个 HTTP POST、把响应里的文本显示出来。 模型加载、推理、上下文管理全在服务端。 2. 为什么填了地址还要填模型名? —— 地址只说「服务在哪」,一个服务里可能有十几个模型。 模型名告诉前端调哪一个,写错就报「找不到模型」而不是「连不上」。 3. 「200 但回答是空的」最可能是什么原因? —— 模型名对,但模型还在加载——第一次请求要把权重读进显存,几十秒正常。 也可能是上下文长度设成了 0 或极小值。Cherry Studio 是桌面端,走的是 OpenAI 兼容接口。下一节换个前端——浏览器扩展,它走的是另一套接口。
- ☐ (本节完,下一节 2.2.1 安装Page Assist插件)