5.9 KiB
local-remarker
本地重排(rerank)服务。零 API Key、文档不出内网,对外提供 SiliconFlow 兼容接口。
起因是在给 PandaWiki 做本地化部署时, 「重排序模型」被卡住了,一共有四个独立的阻塞点:
| # | 阻塞点 | 实测结论 |
|---|---|---|
| 1 | Ollama 不提供重排 | Ollama 0.40.8 的 /api/rerank、/v1/rerank 都是纯 404 |
| 2 | 调用方把路径写死 | PandaWiki 的 raglite 只认 <base_url>/v1/services/rerank/text(SilhouFlow 格式) |
| 3 | 装不上 torch | download.pytorch.org/whl/cpu 与阿里云 pytorch-wheels 下载均 20 秒零进度 |
| 4 | 官方模型站不可达 | registry.ollama.ai 的 blob 下载完全卡死,hf-mirror.com 连不上 |
最终方案是用 ONNX Runtime,绕开 torch。ModelScope 上 Xenova/bge-reranker-base
自带量化 ONNX,于是第 3、4 两个阻塞点同时消失 —— 整条链路只依赖
ModelScope 一个国内源,实测 15~25 MB/s。
快速开始
docker compose up -d
首次启动会下载约 290MB 模型到 ./models,日志形如:
[local-remarker] 模型目录: /models
[model_int8.onnx] 需要下载 ...
[model_int8.onnx] 266.0MB / 266.0MB (100%)
[model_int8.onnx] 校验通过
[local-remarker] 模型准备完成
之后无论重启还是重建容器,都不会再产生下载流量。
服务监听 63001:
curl -s localhost:63001/health
# {"status":"ok","model":"bge-reranker-base"}
curl -s -X POST localhost:63001/v1/services/rerank/text \
-H 'Content-Type: application/json' \
-d '{
"model": "bge-reranker-base",
"query": "如何部署 PandaWiki 知识库",
"documents": [
"PandaWiki 支持通过 Docker 部署,数据保存在 postgres 与 minio",
"今天股市行情如何,上证指数涨跌",
"部署时需要先配置 embedding 模型,否则无法创建知识库"
],
"top_n": 3,
"return_documents": true
}'
设计要点
模型不在镜像里,启动时按需下载
download_model.py 在 entrypoint 里跑,做三件事:
- 检查 —— 每个文件比对
大小 + sha256,全部通过就立刻返回,零网络请求 - 续传 —— 下载落到
.part文件,重试时带Range头从断点继续 - 原子替换 —— 下载完先校验 sha256,通过才
os.replace成正式文件, 避免中断留下的半个文件被当成有效模型加载
模型仓库、版本、下载源都是环境变量,换模型不用重新 build。
路径兜底
调用方填的 base_url 可能是 http://host:8000,也可能是 http://host:8000/v1,
框架在其后拼接路径后可能出现 /v1/v1/services/rerank/text 之类的变体。所以除了
显式注册常见路径,还加了一个兜底路由:路径中含 rerank 或以 /text 结尾的
POST 一律按重排处理;无关路径照常返回 404,不会误吞流量。
打分逻辑
遵循 bge-reranker 系列的惯例:把 (query, document) 拼成句对批量推理,
取 logits[:, 0] 作为相关性分数 —— 用的是原始 logit,不过 sigmoid,
然后降序取 top_n。
为什么选 int8
宿主机是 i7-1165G7,带 AVX512-VNNI,int8 推理能直接吃到该指令集加速。 266MB 的 int8 vs 1061MB 的 fp32,精度损失很小但速度和体积都明显更优。
同仓库可选:model_quantized.onnx (266MB)、model_fp16.onnx (531MB)、
model.onnx (1061MB)。换法见 docker-compose.yml 里的注释。
接口
POST /v1/services/rerank/text
兼容 SiliconFlow,请求:
{
"model": "bge-reranker-base",
"query": "查询词",
"documents": ["片段1", "片段2"],
"top_n": 5,
"return_documents": false
}
响应:
{
"id": "…",
"object": "rerank.result",
"model": "bge-reranker-base",
"results": [
{"index": 0, "relevance_score": 6.8775}
],
"usage": {"total_tokens": 44},
"meta": {"latency_ms": 18}
}
relevance_score 是原始 logit,无固定取值范围,只保证相对大小关系正确。
需要 0~1 区间的话调用方自行过 sigmoid。
其它
GET /health—— 存活探测GET /info—— 当前模型、路径、输入张量名- 同样接受
POST /services/rerank/text、/v1/rerank、/rerank、/rank
配置
全部写在 docker-compose.yml 的 environment 里,改完 docker compose up -d 生效。常用的几个:
| 变量 | 默认 | 说明 |
|---|---|---|
PORT(宿主机侧) |
63001 |
compose 的 ports,映射到容器 8000 |
MODEL_DIR |
/models |
容器内模型目录 |
MODEL_REPO |
Xenova/bge-reranker-base |
换模型改这里 |
MODEL_BASE |
ModelScope | 换下载源改这里 |
ONNX_FILE |
model_int8.onnx |
换量化档位改这里 |
MAX_LEN |
512 |
截断长度,长文档可调大(吃内存) |
BATCH_SIZE |
16 |
每次推理的句对数 |
WORKERS |
1 |
调大前先看内存,每个 worker 各加载一份模型 |
实测性能
i7-1165G7 / 8 逻辑核 / 15GB 内存:
| 场景 | 耗时 |
|---|---|
| 3 个文档重排 | 14~18 ms |
| 镜像大小 | 401 MB |
| 模型大小 | 290 MB |
已知限制
- 单 worker:默认
--workers 1,每个 worker 会各自加载一份模型(约 400MB 内存)。 高并发场景应调WORKERS,同时注意内存翻倍。 - 不鉴权:不校验
api_key。放到非受信网络需要自行加反向代理。 - 仅 CPU:未做 GPU 加速,也没接 TensorRT/ONNXRuntime-GPU。
- 只支持 ONNX:换 safetensors 格式的模型需要调整
download_model.py的MANIFEST并改用 torch 运行时。
文件
local-remarker/
├── app.py FastAPI 服务本体
├── download_model.py 模型检查/下载/校验(幂等,支持续传)
├── entrypoint.sh 先下模型,再起 uvicorn
├── requirements.txt 运行时依赖(无 torch)
├── Dockerfile
├── docker-compose.yml 参数全部写在这里
└── .gitignore