Skip to content

feat(search-index): 远端向量推理改用 OpenAI 兼容协议,支持 Ollama / WeMM 等服务 - #184

Merged
2977094657 merged 5 commits into
LifeArchiveProject:mainfrom
taosiuman:feat/remote-wemm-embedding-2b
Oct 10, 2026
Merged

2977094657 merged 5 commits into
LifeArchiveProject:mainfrom
taosiuman:feat/remote-wemm-embedding-2b

Conversation

@taosiuman

@taosiuman taosiuman commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

背景

本地语义检索原先只有三个 ONNX 编码器模型(BGE Small / BGE Base / Multilingual E5 Small):权重必须下载到本机,向量也只能在本机算。想用更大的向量模型(例如 WeMM-Embedding-2B)就走不通:它是解码器结构的多模态模型,官方只提供 GGUF,套不进现有的 ONNX + tokenizer.json 通路。

最初的实现自定义了一套 POST /embed 协议。按 review 意见改为通用的 OpenAI 兼容接口:文本仍在本机切分,向量交给远端服务计算,服务方不需要为本工具另写适配层。

改动

文件 说明
local_search/catalog.py remote_spec(id, endpoint, model, api_key, allow_self_signed, dimension):地址/模型名/密钥由账号配置覆盖目录预设;新增 remote_identity() 索引身份指纹
local_search/inference.py 远端推理改为 POST {base}/v1/embeddings,按 data[].index 取向量、兼容 base64、请求 encoding_format: "float";新增 remote_api_base() / parse_remote_vectors() / decode_remote_vector();is_lan_endpoint() 扩展为覆盖网络直连
local_search/service.py 账号配置新增 remote_model / remote_api_key / remote_allow_self_signed / remote_dimension;保存时探测维度;generation 由身份指纹决定是否重建;ensure_tokenizer() 取不到时返回 None 而不是中断建索引;新增 remote_probe()
local_search/index.py make_chunks(..., max_chars=None):没有 tokenizer 时按字符上界切块
local_search/downloads.py 远端模型在列表里直接标记为已就绪,不产生下载任务
resources/local_search_models.json 目录新增 remote-openai(协议 openai、维度由探测决定、max_chars: 512)
routers/local_search.py Settings 增加远端模型名/密钥/自签名开关;新增 POST /remote/test 供前端「测试连接」
ai/providers.py 新增 PROXY_BYPASS_NETWORKS / is_proxy_bypass_host():100.64.0.0/10(Tailscale 等)与链路本地也要直连
frontend/components/LocalSearchSettings.vue 远端卡片:服务地址、模型名、可选 API Key、允许自签名证书、快速预设、「测试连接」;底部隐私说明按模型类型切换
frontend/components/AiSettings.vue 标签从「搜索在本机运行」改为「本机或你指定的服务」

远端服务约定

  • POST {base}/v1/embeddings,请求 {"model": "...", "input": ["..."], "encoding_format": "float"},响应 {"data": [{"index": 0, "embedding": [...]}]}
  • 地址填到 /v1 即可,也接受完整的 /v1/embeddings;裸主机地址会自动补 /v1
  • GET {base}/v1/models:可选,只用于「测试连接」列出服务上的模型
  • GET {endpoint}/tokenizer.json:可选,没有就按字符上界切块
  • API Key 可选(Authorization: Bearer);HTTPS 使用自签名证书时需要显式勾选

Ollama、LM Studio、vLLM、TEI 等都自带这个入口,所以「支持 OpenAI 兼容」即「支持 Ollama」,不需要第二套协议。

四个容易被忽略的点

  1. 索引一致性。 模型 id 不变但地址/模型名变了,旧向量与新查询会落在不同向量空间,余弦距离仍然算得出数,只是结果无意义。因此用「协议 + 地址 + 模型名 + 维度」的指纹决定是否重建 generation。
  2. 维度必须探测。 远端模型维度不可预知,保存时探一次、每次响应再校验,不一致直接报错。
  3. 模型名服务端通常不校验。 实测 vLLM 对不存在的模型名照样返回向量,所以「测试连接」用 /v1/models 交叉核对并提示。
  4. 代理绕过要覆盖 Tailscale 段。 100.64.0.0/10 不在 RFC 1918 里;实测环境里存在不可达代理时请求会直接失败。该判定与明文 HTTP 的允许范围分开,后者保持原样。

验证

在隔离进程里对接一台真实部署的 vLLM(WeMM-Embedding-2B,HTTPS 自签名证书):

  • 向量维度 2048、L2 范数 1.000000、单条 0.51s
  • 3 条消息 → 5 个字符切块 → 建索引 → 2 次查询均命中(sqlite-vec 余弦距离)
  • 维度不一致、自签名未勾选、模型名写错三条错误路径都给出可照做的提示
  • 保存时探测到维度 2048;改模型名/地址后 generation 重建,切回原配置复用原 generation;清空地址或模型名被拦下
  • 本地三个模型的行为不变;tests/test_local_search*.py 83 passed

限制

  • 吞吐约 2.9 条/秒(128 条 44.3s):10 万条消息全量建索引约 9.6 小时。暂停在批次边界生效,单个请求不可中断。
  • 服务端 usage.prompt_tokens 恒为 0,不依赖它做 token 计量。
  • 本次不做:Azure 的 deployments URL 与 api-key 头、dimensions 截断参数、rerank、Ollama 原生 /api/embed 协议。

本地语义检索原先只有三个 ONNX 编码器模型:权重要下载到本机,推理也只能在本机跑。WeMM-Embedding-2B 是解码器结构的多模态向量模型,官方只提供 GGUF,走不通现有的 ONNX + tokenizer.json 通路。

新增 backend 为 remote 的模型规格:文本仍在本机切分,向量交给局域网内的服务计算。模型目录加入 wemm-2b-remote(2048 维),在下载列表里直接标记为已就绪,不下载任何文件;远端服务需要 tokenizer 才能把文本切成模型认得的形式,首次使用时从服务端拉取 tokenizer.json 并缓存到模型目录,之后直接用缓存。
目录里的地址只是默认值,换机器或换端口就只能改仓库文件。把 remote_endpoint 补进账号设置:留空沿用目录默认地址,非空则覆盖,前端在远端模型的卡片里提供输入框。

地址必须列进 DEFAULTS:_configure 只比较 DEFAULTS 里的键,否则「只改地址」会被当成没有改动而直接返回旧配置。
Windows 上代理客户端会把代理写进注册表,httpx 默认会读取并使用它。向量服务跑在同一台机器或局域网内时,请求会被代理拦下,报错表现为 502 或连接失败:实测把地址指向 127.0.0.1 上已关闭的端口,拿到的是代理的 502,而不是连接被拒绝,完全看不出真正原因。

新增 is_lan_endpoint() 判断是否需要直连:回环、.local、单标签主机名,以及 ai.providers.is_lan_address 认的 RFC 1918 / IPv6 ULA 网段;局域网定义直接复用该函数,与「允许局域网 IP 的模型服务使用 HTTP」保持同一套口径。远端 tokenizer 的拉取同样处理。
@taosiuman
taosiuman force-pushed the feat/remote-wemm-embedding-2b branch from c0e8cdb to f8ea1c4 Compare October 9, 2026 11:10
@taosiuman taosiuman changed the title Add remote embedding backend for WeMM-Embedding-2B feat(search-index): 新增远端向量推理后端并接入 WeMM-Embedding-2B Oct 9, 2026
@xiaoshengbao

Copy link
Copy Markdown
Contributor

感谢这个 PR!把向量推理交给独立服务,能让更多用户用上较大的模型,这个方向很有价值。
有个建议想和你讨论:是否可以把远端接口做得更通用一些,默认支持 OpenAI 兼容的 /v1/embeddings?用户填写服务地址、模型名和可选 API Key 就能接入,WeMM 则保留为一个预设。
也希望第一版就能兼容 Ollama,方便用户直接使用已有服务,不用额外实现 /embed。Ollama 已提供对应接口,可以参考:Ollama OpenAI 兼容文档。如果能用 Ollama 的向量模型跑通建索引和查询流程,应该能覆盖不少实际使用场景。
tokenizer 是否也可以改成可选?默认采用保守的文本切块方式,有对应 tokenizer 时再精确计数,避免要求每个服务都额外提供 /tokenizer.json。
另外,更换服务后的索引一致性、远端请求的暂停,以及页面“不上传聊天”的提示,也建议一起调整,避免用户遇到困惑。
不一定要一次支持所有服务,可以先以 OpenAI 兼容接口和 Ollama 为目标,其他协议后续再补。你觉得这样调整是否合适?

@2977094657

Copy link
Copy Markdown
Member

@taosiuman 按照上述要求改了就行

@taosiuman taosiuman closed this Oct 9, 2026
@taosiuman

Copy link
Copy Markdown
Contributor Author

其实想引入WeMM这个向量模型的初衷就是看到了这个模型通吃文本、音频、视频、图片的潜质,对于处理微信的数据库信息来说再合适不过,我继续再跟进这个PR

@taosiuman taosiuman reopened this Oct 9, 2026
- 远端推理固定走 POST {base}/v1/embeddings({model,input,encoding_format}),
  按 data[].index 取向量并兼容 base64 编码;Ollama、LM Studio、vLLM 等可直接
  接入,WeMM 等退化为目录预设,不再要求服务方实现本工具自有接口。
- 账号配置新增远端模型名、可选 API Key、允许自签名证书;保存时探测向量维度
  并在每次响应校验,维度不一致直接报错而不是给出无意义结果。
- 索引身份指纹(协议+地址+模型名+维度)变化时重建 generation,避免换服务后
  旧向量与新查询落在不同向量空间却继续复用。
- 远端不提供 tokenizer.json 时按字符上界切块,不再中断整轮建索引;查询串按
  字符上限收口。
- 代理绕过扩展到 100.64.0.0/10(Tailscale 等覆盖网络)与链路本地地址,明文
  HTTP 的允许范围保持不变。
- 新增 POST /api/ai/local-search/remote/test,供前端「测试连接」列出服务模型
  并探测维度;模型名不被服务端校验,因此清单里没有时给出明确提示。
- 远端模型卡片提供服务地址、模型名、可选 API Key、允许自签名证书,以及
  WeMM 2B / Ollama / LM Studio 快速预设与「测试连接」(列出服务上的模型并
  探测维度,模型名对不上时直接提示)。
- 底部隐私说明按当前模型是否为远端切换措辞:远端检索会把文本发送到该服务,
  本机模型仍是「不上传聊天」。
- AI 服务页标签改为「本机或你指定的服务」,不再笼统承诺检索只在本机运行。
@taosiuman taosiuman changed the title feat(search-index): 新增远端向量推理后端并接入 WeMM-Embedding-2B feat(search-index): 远端向量推理改用 OpenAI 兼容协议,支持 Ollama / WeMM 等服务 Oct 9, 2026
@taosiuman

Copy link
Copy Markdown
Contributor Author

按 review 意见改完了(3c1c2ff、6cac45d),逐条回复:

  1. OpenAI 兼容 /v1/embeddings — 采纳,而且只保留这一种协议。原先自定义的 /embed 已删除;WeMM 退化为预设,前端提供 WeMM 2B / Ollama / LM Studio 三个预设按钮直接把地址和模型名填好。
  2. Ollama — 不需要额外实现:Ollama 自带 /v1/embeddings,所以「支持 OpenAI 兼容」本身就覆盖了它。预设把地址填成 http://127.0.0.1:11434/v1,并用 /v1/models 列出服务上的模型供参考;原生 /api/embed 不进 v1。
  3. tokenizer 可选 — 采纳。取不到 tokenizer.json 时按字符上界切块(默认 512 字符;中文 1 字 ≈ 1 token,字符数是 token 数的上界,只会切多不会溢出上下文),max_chars 放在模型目录里可调。
  4. 索引一致性 / 暂停 / 文案 — 都改了:索引身份指纹(协议 + 地址 + 模型名 + 维度)变化时重建 generation;维度在保存时探测、每次响应校验;远端请求按批检查取消标志并区分超时 / 4xx / 5xx;底部隐私说明与 AI 服务页标签按「当前模型是不是远端」切换措辞。
  5. 范围 — v1 只做 OpenAI 兼容;Azure deployments URL 与 api-key 头、dimensions 截断、rerank、Ollama 原生协议留待后续。

另外三点是实测才发现的,建议一并考虑:

  • 响应必须按 data[].index 排序,不能假设与请求同序;
  • 部分服务默认返回 base64 向量,需要显式 encoding_format: "float" 并兼容 base64;
  • vLLM 对不存在的模型名照样返回向量(不报错),模型名只能靠 /v1/models 交叉核对,所以「测试连接」会明确提示。

还有一个与 review 无关但影响可用性的点:100.64.0.0/10(Tailscale 等覆盖网络)不在 RFC 1918 内,原白名单不覆盖,环境里存在不可达代理时请求会直接失败,已加入直连列表。

验证方式见 PR 描述——这次是接一台真实部署的 vLLM + WeMM-Embedding-2B(HTTPS 自签名)跑通了建索引与检索,不再只是假服务。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants