Cherry Studio 知识库配嵌入:Ollama 实测与四个坑

结论放在最前。16GB 统一内存的机器上,Cherry Studio 1.9.13 接本地 Ollama 做知识库嵌入,选 qwen3-embedding:0.6b 就够用。1024 维,常驻显存 1.24GB,261 汉字的片段每块 0.1 秒,一本 200 页的 PDF 入库约 73 秒。8B 那个版本 4096 维、5.73GB、每块 0.87 秒,中文场景的检索收益不明显,开销涨 12 倍。整套配通 10 分钟,卡人的地方不在安装,在四个不报错的失败点上。

一、先把嵌入模型拉到本地

Ollama 0.40.14 已经有 qwen3-embedding 系列,一条命令就能拿下来。0.6B 落盘 639MB,8B 是 4.7GB,模型默认落在系统盘,C 盘吃紧的机器先看一眼模型目录迁移那篇再动手。

ollama pull qwen3-embedding:0.6b
ollama list

第二行输出的 NAME 列要记住。Cherry Studio 里填的模型 ID 必须与它逐字相同,手写 qwen3-embedding 漏掉 :0.6b 后缀,服务端会直接回 404 加一句 model not found。

命令行先打一次接口,确认这台机器的向量服务是通的。这一步只要十秒,能省掉后面在图形界面里反复点重试的时间。

curl -s http://127.0.0.1:11434/api/embed \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-embedding:0.6b","input":"测试文本"}'

返回里的 embeddings 是二维数组,内层长度就是维度。0.6B 给 1024,8B 给 4096。这两个数字决定了你以后换嵌入模型要不要重建知识库。

二、Cherry Studio 里的五步配置

第一步,打开设置,进模型服务,服务商列表里选 Ollama。地址填 http://127.0.0.1:11434。写 localhost 大多数时候也能通,但本机装了代理软件时,localhost 可能被代理规则接管,走回环地址更省事。

第二步,在该服务商下点管理,把 qwen3-embedding:0.6b 加进模型清单,类型选嵌入。列表上那个加号的作用是让 Ollama 重新拉一次模型,不是绑定开关,模型已经在本机就不要点它,点了会触发一次没必要的下载。

第三步,回到左侧知识库,新建一个库。弹窗里的嵌入模型下拉会列出刚才加过的模型。这里如果是空的,说明 Ollama 那一侧没连通,回头看地址和端口,别在这里反复切换模型。

第四步,把文档拖进去。右下角会出处理进度,显示处理中不代表完成,等状态变成已完成再去提问。中途关窗口会让索引停在半路,回来得重新索引。

第五步,问一个只有文档里才有的问题验证。答案能命中原文的细节,说明嵌入和检索都通了;答案通顺但内容和文档无关,多半是检索命中了空片段,回去看第六节的截断问题。

三、三种接口的返回结构完全不同

同一个 Ollama 服务,三个端点算出来的向量数值一样,包装结构差很远。客户端按哪种结构解析,决定了它对不对得上。

接口 请求体关键字段 向量所在字段 能否一次传多条
/api/embed input,字符串或字符串数组 embeddings,二维数组 能,数组长度即条数
/api/embeddings prompt,只能是单个字符串 embedding,一维数组 不能
/v1/embeddings input,字符串或字符串数组 data 里每条的 embedding 能

Cherry Studio 1.9.13 的主进程包里,Ollama 这条链路优先打 /api/embed,只有收到 404 或 405 才退回 /api/embeddings。所以老版本 Ollama 缺 /api/embed 时会自动降级,用户感觉不到。反过来,你要是把 Ollama 填成 OpenAI 兼容服务商,它就走 /v1/embeddings,地址必须带 /v1 后缀,少写一段会拿到 404 page not found。

第三个字段差异最坑人。/api/embed 从 embeddings 取,OpenAI 兼容口径从 data 数组第一条的 embedding 取。早期版本在这条分支上没做兜底,取不到就抛 TypeError,界面只剩一句知识库向量化失败。看到这类报错,先判断走的是哪条链路,再去改模型。

四、实测:0.6B 与 8B 的真实开销

同一台机器,同一批中文段落,单块 261 汉字,跑出来的数字如下。耗时都是预热之后的稳态值,首次调用含读盘,单独标注。

项目 qwen3-embedding:0.6b qwen3-embedding:8b
向量维度 1024 4096
常驻显存(上下文 4096) 1.24GB 5.73GB
首次加载耗时 0.82 秒 3.66 秒
短句单条(49 token) 0.03 秒 0.30 秒
261 汉字单块 0.10 秒 0.87 秒
8 块一次提交 0.58 秒 6.94 秒
1000 块入库估算 约 73 秒 约 14 分 30 秒

批量提交这件事要泼点冷水。8 块一起送,0.6B 用了 0.58 秒,拆成 8 次单条是 0.8 秒,省掉约四分之一。8B 一起送 6.94 秒,拆开是 6.96 秒,基本没差别。嵌入是纯前向计算,批处理能摊薄调度开销,摊不掉算力开销。

显存那一栏别看错。这里的 5.73GB 是模型加上下文缓存的总占用,8B 权重本身约 4.7GB。想精确算一台机器能塞几个嵌入模型,方法跟算大模型一样,显存分项估算那里有公式,嵌入模型只是把 KV 部分换成短上下文。

模型加载完会常驻五分钟,没人用才退出。要是发现嵌入模型一直占着显存不放,处理方式跟聊天模型一样,keep_alive 的四种写法那篇讲得比较细。

五、坑一:接口回 200,向量是空的

这是最难查的一类。参数名写错,Ollama 不报错,回 200,向量是空数组。下面两个请求都能拿到成功状态码。

# 用旧参数名打新端点
curl -s http://127.0.0.1:11434/api/embed -H "Content-Type: application/json" \
  -d '{"model":"qwen3-embedding:0.6b","prompt":"测试"}'
# 返回 {"model":"qwen3-embedding:0.6b","embeddings":[]}

# 用新参数名打旧端点
curl -s http://127.0.0.1:11434/api/embeddings -H "Content-Type: application/json" \
  -d '{"model":"qwen3-embedding:0.6b","input":["a","b"]}'
# 返回 {"embedding":[]}

客户端拿到空数组,要么当成零条结果,入库了 0 个片段;要么直接对第一项取下标,抛出读取 0 属性失败。两种表现都指向同一个原因:字段名和端点不匹配。判断方法很简单,看返回体的键名,embeddings 对应新端点,embedding 对应旧端点,多一个 s 就是另一个接口。

空字符串输入也是同样的结果。传一条空 text 进去,回 200,embeddings 为空数组,那条片段就被静默丢掉。批量入库前把空行和纯空白段落过滤掉,能省掉后面查不出来的困惑。

六、坑二:拿聊天模型当嵌入模型,服务端回 501

模型下拉框里什么都能选,选错了不会在界面上先拦住你。请求发出去之后服务端才拦:

{"error":"This server does not support embeddings. Start it with `--embeddings`"}

状态码 501。这句提示有点误导,它让你给服务加 –embeddings 参数,实际上 0.40 的服务端没有这个开关,问题出在模型本身。qwen3:8b 这类生成模型没有池化层,算不出句向量,换回带 embedding 名字的模型才有意义。

判断一个模型能不能做嵌入,最快的办法是直接打接口看返回。ollama list 里能跑对话的模型全都可以先当成不能做嵌入,名字里带 embedding 或 bge 的先试。

七、坑三:片段太长被截到 4096

我用四万个汉字当一条输入,服务端回 200,向量正常,但 prompt_eval_count 只有 4095。也就是说后面三万多个字压根没进模型,向量只代表开头那一小段。这类静默截断不会报错,检索时表现为整篇文档的后半部分永远查不到。

上下文设置 实际吃进的 token 耗时 嵌入模型占用
默认(4096) 4095 2.65 秒 1.24GB
8192 8191 6.29 秒 1.74GB
32768 32767 63.16 秒 4.76GB

把上下文拉到 32768 能让 4 万字整段进模型,代价是单条耗时从 2.65 秒涨到 63 秒,常驻显存从 1.24GB 涨到 4.76GB。为一个知识库这么干不值。真正的解法是别让单块超过 4000 token,分块切小。

上下文参数的作用机制和聊天模型是同一套,num_ctx 与可用输入长度那篇里有一张按档位的实测表,嵌入模型只是在末尾少了一次生成。

要验证自己的库有没有被截断,把库里任意一段的字符数除以 1.37 估一下 token 数,超过 4000 就有风险。中文按 qwen3 的分词器,261 汉字正好 191 token,比值稳定在 1.37 上下。

八、坑四:维度改过一次,旧库就废了

Cherry Studio 的知识库设置里有个嵌入维度输入框,留空表示按模型默认走。这个框一旦填了数字,请求会带 dimensions 参数,Ollama 会按你给的数截断输出。实测填 512 得 512 维,填 600 得 600 维,服务端不校验是不是二的幂。

问题在于已经入库的向量不会被重算。同一个库里,前一百条是 1024 维,后一百条是 600 维,算余弦相似度时维度对不上,要么直接抛错,要么把短的那条补齐后算出没有意义的分数。表现是检索结果忽好忽坏,而且看不出规律。

稳妥做法是这个框一直留空。换了嵌入模型就建新库,把文档重新索引一遍,别在原库里改设置。删库重建比事后查错便宜得多。

九、分块怎么切才不丢上下文

Cherry Studio 的分块大小和重叠长度都有输入框,默认值写的是不建议修改。包内的文本加载器在没被显式指定时,纯文本按 300 字符切,Word 与 Excel 按 1000 字符切。300 字符大约 220 token,离 4096 的上限很远,正常文档不会触发截断。

真正的取舍在检索质量上。按 300 字符切,一个片段讲不完一个完整条款,回答时会缺前后条件;按 1000 字符切,中文一千字往往横跨两三个小标题,命中的片段里有一半内容和问题无关。条款类、参数手册类文档适合小段,叙述类文档适合大段。

重叠长度给到块长的十分之一左右比较合适,300 字符的块配 30,1000 字符的块配 100。重叠是为了不让一句话被切断,给太多只是让同一段文本被重复嵌入,白花时间。

匹配度阈值那个选项填 0 到 1,用来筛掉相关性低的结果。默认不设置时所有召回都会进上下文,文档一多就容易把不相关的内容塞进提示词。先观察几次问答的命中片段,再决定要不要设,设太高会让本该命中的片段被过滤掉。

十、什么时候才值得上 8B

维度高不等于检索准。1024 维的 0.6B 在这台机器上跑,中文段落的召回已经够用,判断依据是我用同一批文档做对照,问题里换用同义说法也能命中原文。8B 拉开差距的地方是中英混排的文档和跨语言提问,比如用中文问一份英文手册里的参数,4096 维的表达空间更宽。

另一个判断维度是入库时间。一万个片段的库,0.6B 十二分钟能跑完,8B 要两个半小时。知识库需要频繁增删文档时,这个差距会一直存在,不是一次性的。

显存也要算进去。8B 常驻 5.73GB,同一台机器上再开一个 8B 的对话模型,16GB 的机器就开始互相踢模型,每次切换都要重新加载,等三秒起步。0.6B 只占 1.24GB,和对话模型共存没有压力。

十一、常见问题

嵌入模型能不能直接用聊天模型代替?
不能。聊天模型没有池化输出,请求会返回 501 和一句 This server does not support embeddings。必须在 ollama list 里挑名字带 embedding 的模型,加到 Cherry Studio 的嵌入类型里。

知识库建好之后换嵌入模型会怎样?
旧向量不会自动重算,新旧向量维度不同,检索会失效或给出乱序结果。正确做法是新建一个库重新索引,把旧库删掉,别在原库里替换模型了事。

这套流程要不要联网,文档会不会外传?
模型在本机跑,嵌入和检索都不出网,断网也能用。只有当你把模型服务换成云端服务商时,文档片段才会发到对方服务器,本地 Ollama 这条路径没有这个问题。