177 lines
5.9 KiB
Markdown
177 lines
5.9 KiB
Markdown
# local-remarker
|
|
|
|
本地重排(rerank)服务。零 API Key、文档不出内网,对外提供 SiliconFlow 兼容接口。
|
|
|
|
起因是在给 [PandaWiki](https://github.com/chaitin/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。
|
|
|
|
## 快速开始
|
|
|
|
```bash
|
|
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**:
|
|
|
|
```bash
|
|
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 里跑,做三件事:
|
|
|
|
1. **检查** —— 每个文件比对 `大小 + sha256`,全部通过就立刻返回,零网络请求
|
|
2. **续传** —— 下载落到 `.part` 文件,重试时带 `Range` 头从断点继续
|
|
3. **原子替换** —— 下载完先校验 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,请求:
|
|
|
|
```json
|
|
{
|
|
"model": "bge-reranker-base",
|
|
"query": "查询词",
|
|
"documents": ["片段1", "片段2"],
|
|
"top_n": 5,
|
|
"return_documents": false
|
|
}
|
|
```
|
|
|
|
响应:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
``` |