Commit bc089b431d6d5e7755f50855bf2888e54fee8b34

Authored by tangwang
1 parent 07cf5a93

refactor(reranker): 对齐 Qwen3-Reranker 官方实现

config/config.yaml:
- qwen3_vllm: enable_prefix_caching true(启用前缀缓存)
- qwen3_vllm: enforce_eager false(允许 CUDA graph 加速)

reranker/backends/qwen3_vllm.py:
- TokensPrompt 导入改为 vllm.inputs.data(官方路径,兼容性更好)
- 缺失 token 时使用 logprob=-10,与官方一致(原为 1e-10)
- 使用批量 apply_chat_template 替代逐条调用,提升效率
- logprobs 访问改为官方模式:token not in last 时 -10,否则 last[token].logprob

其他: docs、embeddings、README 等文档更新

Made-with: Cursor
@@ -82,6 +82,8 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh @@ -82,6 +82,8 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh
82 | 3. API 详细说明 | `docs/搜索API对接指南.md` | 82 | 3. API 详细说明 | `docs/搜索API对接指南.md` |
83 | 4. 快速参数速查 | `docs/搜索API速查表.md` | 83 | 4. 快速参数速查 | `docs/搜索API速查表.md` |
84 | 5. 首次环境搭建、生产凭证 | `docs/QUICKSTART.md` §1.4–1.8 | 84 | 5. 首次环境搭建、生产凭证 | `docs/QUICKSTART.md` §1.4–1.8 |
  85 +| 6. TEI 文本向量专项 | `docs/TEI_SERVICE说明文档.md` |
  86 +| 7. CN-CLIP 图片向量专项 | `docs/CNCLIP_SERVICE说明文档.md` |
85 87
86 --- 88 ---
87 89
config/config.yaml
@@ -204,8 +204,8 @@ services: @@ -204,8 +204,8 @@ services:
204 tensor_parallel_size: 1 204 tensor_parallel_size: 1
205 gpu_memory_utilization: 0.36 205 gpu_memory_utilization: 0.36
206 dtype: "float16" 206 dtype: "float16"
207 - enable_prefix_caching: false  
208 - enforce_eager: true 207 + enable_prefix_caching: true
  208 + enforce_eager: false
209 instruction: "Given a web search query, retrieve relevant passages that answer the query" 209 instruction: "Given a web search query, retrieve relevant passages that answer the query"
210 210
211 # SPU配置(已启用,使用嵌套skus) 211 # SPU配置(已启用,使用嵌套skus)
docs/CNCLIP_SERVICE说明文档.md
1 # CN-CLIP 服务(clip-as-service)说明 1 # CN-CLIP 服务(clip-as-service)说明
2 2
3 -> 本文是本仓库的 CN-CLIP 运行手册与约束说明。主流程仍是 `embedding` 服务(`6005`);当 `embeddings/config.py` 中 `USE_CLIP_AS_SERVICE=true` 时,`embedding` 会调用本 gRPC 服务(默认 `grpc://127.0.0.1:51000`)生成图片向量 3 +> 本文是本仓库 CN-CLIP(图片向量)专项运行手册。目标是让新同事在新机器上独立完成环境准备、部署、验收与排障
4 4
5 -## 1. 设计目标与官方对齐 5 +## 1. 作用与边界
6 6
7 -- 采用 `clip-as-service` 的标准拆分:`clip-server`(服务端)与 `clip-client`(客户端)可独立安装。  
8 -- 服务协议使用 gRPC,符合官方推荐与本项目现有调用链。  
9 -- 保持“主项目环境”和“CN-CLIP 专用环境”解耦,避免 `grpcio/jina/docarray` 与主项目依赖互相污染。 7 +- `cnclip` 是独立 gRPC 服务(默认 `grpc://127.0.0.1:51000`)。
  8 +- `embedding` 服务(6005)在 `USE_CLIP_AS_SERVICE=true` 时调用它完成 `/embed/image`。
  9 +- `cnclip` 不负责文本向量;文本向量由 TEI(8080)负责。
10 10
11 -官方仓库(安装方式、server/client 分离、基本使用示例):  
12 -[jina-ai/clip-as-service](https://github.com/jina-ai/clip-as-service) 11 +## 2. 代码与脚本入口
13 12
14 -## 2. 当前架构(本仓库) 13 +- 环境初始化:`scripts/setup_cnclip_venv.sh`
  14 +- 启动:`scripts/start_cnclip_service.sh`
  15 +- 停止:`scripts/stop_cnclip_service.sh`
  16 +- 统一编排:`scripts/service_ctl.sh`
15 17
16 -- **服务启动脚本**:`scripts/start_cnclip_service.sh`  
17 -- **服务停止脚本**:`scripts/stop_cnclip_service.sh`  
18 -- **环境初始化脚本**:`scripts/setup_cnclip_venv.sh`  
19 -- **统一编排入口**:`scripts/service_ctl.sh`(`restart.sh` 调用它)  
20 -- **默认端口**:`51000`  
21 -- **默认模型**:`CN-CLIP/ViT-H-14`  
22 -- **默认协议**:gRPC 18 +## 3. 前置条件
23 19
24 -## 3. 环境准备策略(推荐做法) 20 +### 3.1 必需软件
25 21
26 -### 3.1 推荐:专用 venv(`.venv-cnclip`) 22 +- Python 3.12(由仓库脚本创建虚拟环境)
  23 +- 可访问模型下载源(Hugging Face / 镜像源)
  24 +
  25 +### 3.2 GPU 模式额外要求
  26 +
  27 +- NVIDIA 驱动可用(`nvidia-smi` 可执行)
  28 +- `cnclip` 设备设为 `cuda`
  29 +
  30 +检查命令:
27 31
28 ```bash 32 ```bash
29 -./scripts/setup_cnclip_venv.sh 33 +nvidia-smi
30 ``` 34 ```
31 35
32 -脚本会创建 `.venv-cnclip`,并处理已知兼容性问题(`grpcio`、`jina`、`docarray`、`pkg_resources` 等),避免在主 `.venv` 中反复冲突。 36 +## 4. 环境安装(可复现)
33 37
34 -### 3.2 启动时的环境策略(严格) 38 +在仓库根目录执行:
  39 +
  40 +```bash
  41 +cd /data/saas-search
  42 +./scripts/setup_cnclip_venv.sh
  43 +```
35 44
36 -`start_cnclip_service.sh` 只允许使用 `.venv-cnclip`。  
37 -若 `.venv-cnclip` 不存在,脚本会直接失败并提示先执行 `./scripts/setup_cnclip_venv.sh`。 45 +脚本会创建 `.venv-cnclip`,并安装 `clip-as-service` 服务端所需依赖。
  46 +`start_cnclip_service.sh` 只使用这个专用环境,不会污染主 `.venv`。
38 47
39 -## 4. 服务管理方式(推荐) 48 +## 5. 启动与停止
40 49
41 -### 4.1 单独启动/停止 50 +### 5.1 GPU 模式(推荐)
42 51
43 ```bash 52 ```bash
44 ./scripts/start_cnclip_service.sh --device cuda 53 ./scripts/start_cnclip_service.sh --device cuda
  54 +```
  55 +
  56 +### 5.2 CPU 模式(显式)
  57 +
  58 +```bash
  59 +./scripts/start_cnclip_service.sh --device cpu
  60 +```
  61 +
  62 +### 5.3 停止服务
  63 +
  64 +```bash
45 ./scripts/stop_cnclip_service.sh 65 ./scripts/stop_cnclip_service.sh
46 ``` 66 ```
47 67
48 -### 4.2 统一编排(推荐日常用法) 68 +## 6. service_ctl 的行为与约定
  69 +
  70 +### 6.1 通过统一编排启动
49 71
50 ```bash 72 ```bash
51 -./scripts/service_ctl.sh restart  
52 -# 或  
53 -./restart.sh 73 +START_EMBEDDING=1 START_TEI=1 ./scripts/service_ctl.sh start
54 ``` 74 ```
55 75
56 -`service_ctl.sh` 在启动 `cnclip` 时默认注入 `CNCLIP_DEVICE=cuda`。  
57 -若机器无 GPU 或希望改用 CPU,可在 `.env` 设置: 76 +当 `USE_CLIP_AS_SERVICE=true` 且 `START_EMBEDDING=1` 时,`service_ctl` 会自动拉起 `cnclip`。
  77 +
  78 +### 6.2 设备选择优先级
  79 +
  80 +- 显式传入 `CNCLIP_DEVICE` 时,以该值为准:
58 81
59 ```bash 82 ```bash
60 -CNCLIP_DEVICE=cpu 83 +CNCLIP_DEVICE=cuda ./scripts/service_ctl.sh start cnclip
  84 +CNCLIP_DEVICE=cpu ./scripts/service_ctl.sh start cnclip
61 ``` 85 ```
62 86
63 -## 5. GPU 使用与验证 87 +- 不传 `CNCLIP_DEVICE` 时,默认 `cuda`。
  88 +
  89 +### 6.3 运行中模式校验(重要)
64 90
65 -### 5.1 必须点 91 +`service_ctl` 会在“已运行”场景下检查当前 `cnclip` 的 device 与期望是否一致。
  92 +若不一致会直接报错,防止“服务已启动但模式错了”的静默问题。
66 93
67 -- 启动日志显示 `device: cuda` 仅代表配置传入成功;  
68 -- 只有在**首次编码请求触发模型加载后**,`nvidia-smi` 才一定能看到显存占用。 94 +例如:当前运行是 CPU,但你期望 CUDA,会提示先 stop 再按 CUDA 重启。
69 95
70 -### 5.2 推荐验证步骤 96 +## 7. 验收步骤(必须做)
71 97
72 -1) 启动服务: 98 +### 7.1 基础状态
73 99
74 ```bash 100 ```bash
75 -./scripts/start_cnclip_service.sh --port 51000 --device cuda 101 +./scripts/service_ctl.sh status cnclip
76 ``` 102 ```
77 103
78 -2) 发送一次请求(触发模型加载): 104 +### 7.2 核对运行配置
  105 +
  106 +```bash
  107 +cat third-party/clip-as-service/server/torch-flow-temp.yml
  108 +```
  109 +
  110 +应看到:
  111 +
  112 +- GPU 模式:`device: 'cuda'`
  113 +- CPU 模式:`device: 'cpu'`
  114 +
  115 +### 7.3 发送一次编码请求(触发模型加载)
79 116
80 ```bash 117 ```bash
81 -PYTHONPATH="third-party/clip-as-service/client:${PYTHONPATH}" NO_VERSION_CHECK=1 .venv-cnclip/bin/python -c " 118 +PYTHONPATH="third-party/clip-as-service/client:${PYTHONPATH}" \
  119 +NO_VERSION_CHECK=1 \
  120 +.venv-cnclip/bin/python - <<'PY'
82 from clip_client import Client 121 from clip_client import Client
83 -c = Client('grpc://localhost:51000') 122 +c = Client('grpc://127.0.0.1:51000')
84 r = c.encode(['测试']) 123 r = c.encode(['测试'])
85 print('shape:', r.shape) 124 print('shape:', r.shape)
86 -" 125 +PY
87 ``` 126 ```
88 127
89 -3) 观察 GPU: 128 +预期 `shape` 为 `(1, 1024)`。
  129 +
  130 +### 7.4 GPU 验证
90 131
91 ```bash 132 ```bash
92 nvidia-smi 133 nvidia-smi
93 ``` 134 ```
94 135
95 -预期:  
96 -- `shape` 为 `(1, 1024)`;  
97 -- `nvidia-smi` 出现对应 `python`/`clip_server` 进程并有显存占用。 136 +GPU 模式下应出现 `clip_server` 相关 `python` 进程及显存占用。
98 137
99 -## 6. 使用方式(客户端) 138 +### 7.5 与 embedding 服务联调
100 139
101 -### 6.1 在本仓库中(推荐) 140 +```bash
  141 +./scripts/start_embedding_service.sh
102 142
103 -- 服务消费者一般是 `embedding` 服务,不建议业务侧直接连 `cnclip`。  
104 -- 若需手动调试,请使用隔离环境(推荐 `.venv-embedding` 或 `.venv-cnclip`),不要在主 `.venv` 安装旧依赖。 143 +curl -sS -X POST "http://127.0.0.1:6005/embed/image" \
  144 + -H "Content-Type: application/json" \
  145 + -d '["https://oss.essa.cn/98532128-cf8e-456c-9e30-6f2a5ea0c19f.jpg"]'
  146 +```
105 147
106 -示例: 148 +返回应为向量数组(非空)。
107 149
108 -```python  
109 -from clip_client import Client 150 +## 8. 常见问题与排障
110 151
111 -c = Client("grpc://127.0.0.1:51000")  
112 -vec = c.encode(["https://example.com/a.jpg", "测试文本"])  
113 -print(vec.shape) # (2, 1024)  
114 -``` 152 +### 8.1 “为什么 cnclip 没用 GPU?”
115 153
116 -### 6.2 常见误区 154 +先看启动命令是否显式写了:
117 155
118 -- ❌ 用 `http://localhost:51000` 当成 HTTP 服务访问;  
119 -- ❌ 只看“启动成功”就判断已用 GPU,不发请求不看 `nvidia-smi`;  
120 -- ❌ 在主 `.venv` 直接安装 server 依赖导致依赖树污染。 156 +```bash
  157 +CNCLIP_DEVICE=cpu ...
  158 +```
121 159
122 -## 7. 已知兼容性说明(关键信息) 160 +如果是,这就是预期行为,会强制 CPU。
123 161
124 -- `clip-as-service` 在本项目场景下依赖链较老,`grpcio`/`jina`/`docarray` 组合在 Python 3.12 上易触发源码构建问题。  
125 -- `setuptools>=82` 移除了 `pkg_resources`;而部分依赖链仍会导入它,因此专用脚本固定了兼容范围。  
126 -- `setup_cnclip_venv.sh` 中存在“为可运行性而做的约束收敛”,这是有意行为,不建议手动放开。 162 +### 8.2 “我要求 cuda,但 service_ctl 说 mode mismatch”
127 163
128 -## 8. 排障速查 164 +说明当前已有 CPU 模式实例在跑。按提示重启:
129 165
130 -### 8.1 启动失败 166 +```bash
  167 +./scripts/service_ctl.sh stop cnclip
  168 +CNCLIP_DEVICE=cuda ./scripts/service_ctl.sh start cnclip
  169 +```
131 170
132 -- 查看日志:`tail -f logs/cnclip_service.log`  
133 -- 检查端口占用:`lsof -i :51000`  
134 -- 重新构建环境:`rm -rf .venv-cnclip && ./scripts/setup_cnclip_venv.sh` 171 +### 8.3 进程被 `Killed`
135 172
136 -### 8.2 连接失败 173 +常见于资源竞争(尤其单卡同时运行 TEI/reranker/cnclip 时的启动峰值)。建议:
137 174
138 -- 确认客户端使用 `grpc://` 协议;  
139 -- 确认端口与服务端一致(默认 `51000`)。 175 +1. 先起 `tei` 与 `cnclip`,确认稳定后再起 `reranker`。
  176 +2. 适当下调 `reranker` 显存预算(`config/config.yaml` 的 `gpu_memory_utilization`)。
  177 +3. 必要时将 CN-CLIP 模型从 `ViT-H-14` 调整到更小规模。
140 178
141 -### 8.3 看不到 GPU 进程 179 +### 8.4 gRPC 连接失败(`Connection refused`)
142 180
143 -- 先发一次编码请求,再看 `nvidia-smi`;  
144 -- 确认启动参数或环境变量为 `cuda`(`--device cuda` 或 `CNCLIP_DEVICE=cuda`);  
145 -- 确认日志中无模型加载异常。 181 +- 检查端口:`lsof -i :51000`
  182 +- 看服务日志:`tail -f logs/cnclip_service.log`
  183 +- 确认客户端使用 `grpc://` 协议而非 `http://`
146 184
147 -## 9. 与其他文档的关系 185 +## 9. 相关文档
148 186
149 - 开发总览:`docs/QUICKSTART.md` 187 - 开发总览:`docs/QUICKSTART.md`
150 -- 系统架构:`docs/DEVELOPER_GUIDE.md`  
151 -- 向量服务说明:`embeddings/README.md` 188 +- TEI 专项:`docs/TEI_SERVICE说明文档.md`
  189 +- 体系规范:`docs/DEVELOPER_GUIDE.md`
  190 +- embedding 模块:`embeddings/README.md`
152 191
153 -本文件聚焦 CN-CLIP(clip-as-service)专项,不重复解释项目通用内容。  
docs/DEVELOPER_GUIDE.md
@@ -407,6 +407,7 @@ services: @@ -407,6 +407,7 @@ services:
407 | 运维、日志、多环境、故障 | [Usage-Guide.md](./Usage-Guide.md) | 407 | 运维、日志、多环境、故障 | [Usage-Guide.md](./Usage-Guide.md) |
408 | 索引模块职责与 Java 对接 | [indexer/README.md](../indexer/README.md) | 408 | 索引模块职责与 Java 对接 | [indexer/README.md](../indexer/README.md) |
409 | 向量模块与 clip-as-service | [embeddings/README.md](../embeddings/README.md) | 409 | 向量模块与 clip-as-service | [embeddings/README.md](../embeddings/README.md) |
  410 +| TEI 服务专项(安装/部署/GPU-CPU 模式) | [TEI_SERVICE说明文档.md](./TEI_SERVICE说明文档.md) |
410 | CN-CLIP 服务专项(环境/运维/GPU) | [CNCLIP_SERVICE说明文档.md](./CNCLIP_SERVICE说明文档.md) | 411 | CN-CLIP 服务专项(环境/运维/GPU) | [CNCLIP_SERVICE说明文档.md](./CNCLIP_SERVICE说明文档.md) |
411 412
412 ### 10.2 仓库内入口 413 ### 10.2 仓库内入口
docs/QUICKSTART.md
@@ -532,6 +532,7 @@ lsof -i :6004 @@ -532,6 +532,7 @@ lsof -i :6004
532 | `docs/搜索API对接指南.md` | 搜索 API 完整说明 | 532 | `docs/搜索API对接指南.md` | 搜索 API 完整说明 |
533 | `indexer/README.md` | 索引模块职责与接口 | 533 | `indexer/README.md` | 索引模块职责与接口 |
534 | `embeddings/README.md` | 向量化服务说明 | 534 | `embeddings/README.md` | 向量化服务说明 |
  535 +| `docs/TEI_SERVICE说明文档.md` | TEI 专项(安装、部署、GPU/CPU 模式、排障) |
535 | `docs/CNCLIP_SERVICE说明文档.md` | CN-CLIP/clip-as-service 专项(环境、GPU、运维) | 536 | `docs/CNCLIP_SERVICE说明文档.md` | CN-CLIP/clip-as-service 专项(环境、GPU、运维) |
536 | `reranker/README.md` | 重排服务说明 | 537 | `reranker/README.md` | 重排服务说明 |
537 538
docs/TEI_SERVICE说明文档.md 0 → 100644
@@ -0,0 +1,193 @@ @@ -0,0 +1,193 @@
  1 +# TEI 服务(Qwen3-Embedding-0.6B)说明
  2 +
  3 +> 本文是本仓库 TEI(Text Embeddings Inference)专项运行手册。目标是让新同事在一台新机器上可以独立完成环境准备、部署、验收与排障。
  4 +
  5 +## 1. 作用与边界
  6 +
  7 +- TEI 提供文本向量 HTTP 服务(默认 `http://127.0.0.1:8080`)。
  8 +- 本项目中 `embedding` 服务(6005)默认把文本向量请求转发到 TEI。
  9 +- TEI 仅负责文本向量,不负责图片向量(图片向量由 `cnclip` 提供)。
  10 +
  11 +## 2. 代码与脚本入口
  12 +
  13 +- 启动脚本:`scripts/start_tei_service.sh`
  14 +- 停止脚本:`scripts/stop_tei_service.sh`
  15 +- 统一编排:`scripts/service_ctl.sh`
  16 +- embedding 服务启动脚本(会校验 TEI 健康):`scripts/start_embedding_service.sh`
  17 +
  18 +## 3. 前置条件
  19 +
  20 +### 3.1 必需软件
  21 +
  22 +- Docker(必须)
  23 +- curl(用于健康检查与接口验收)
  24 +
  25 +### 3.2 GPU 模式额外要求(推荐生产)
  26 +
  27 +- NVIDIA 驱动可用(`nvidia-smi` 可执行)
  28 +- Docker 已配置 `nvidia` runtime(`docker info` 能看到 `nvidia`)
  29 +
  30 +检查命令:
  31 +
  32 +```bash
  33 +docker --version
  34 +docker info --format '{{json .Runtimes}}'
  35 +nvidia-smi
  36 +```
  37 +
  38 +## 4. 安装与准备(可复现)
  39 +
  40 +### 4.1 拉取代码并进入项目
  41 +
  42 +```bash
  43 +cd /data/saas-search
  44 +```
  45 +
  46 +### 4.2 (仅 GPU 模式)配置 Docker GPU runtime
  47 +
  48 +如果 `docker info` 中没有 `nvidia` runtime,先按机器环境安装 `nvidia-container-toolkit` 并执行:
  49 +
  50 +```bash
  51 +sudo nvidia-ctk runtime configure --runtime=docker
  52 +sudo systemctl restart docker
  53 +```
  54 +
  55 +再次确认:
  56 +
  57 +```bash
  58 +docker info --format '{{json .Runtimes}}'
  59 +```
  60 +
  61 +## 5. 启动方式
  62 +
  63 +### 5.1 GPU 模式启动(默认)
  64 +
  65 +```bash
  66 +TEI_USE_GPU=1 ./scripts/start_tei_service.sh
  67 +```
  68 +
  69 +预期输出包含:
  70 +
  71 +- `Image: ghcr.io/huggingface/text-embeddings-inference:cuda-...`
  72 +- `Mode: gpu`
  73 +- `TEI is ready: http://127.0.0.1:8080`
  74 +
  75 +### 5.2 CPU 模式启动(显式)
  76 +
  77 +```bash
  78 +TEI_USE_GPU=0 ./scripts/start_tei_service.sh
  79 +```
  80 +
  81 +预期输出包含:
  82 +
  83 +- `Image: ghcr.io/huggingface/text-embeddings-inference:1.9`(非 `cuda-`)
  84 +- `Mode: cpu`
  85 +- `TEI is ready: http://127.0.0.1:8080`
  86 +
  87 +### 5.3 停止服务
  88 +
  89 +```bash
  90 +./scripts/stop_tei_service.sh
  91 +```
  92 +
  93 +## 6. 验收步骤(必须做)
  94 +
  95 +### 6.1 健康检查
  96 +
  97 +```bash
  98 +curl -sS http://127.0.0.1:8080/health
  99 +```
  100 +
  101 +### 6.2 向量接口检查
  102 +
  103 +```bash
  104 +curl -sS http://127.0.0.1:8080/embed \
  105 + -H "Content-Type: application/json" \
  106 + -d '{"inputs":["Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: What is the capital of China?"]}'
  107 +```
  108 +
  109 +返回应为二维数组(每条输入对应一个向量)。
  110 +
  111 +### 6.3 与 embedding 服务联调
  112 +
  113 +```bash
  114 +./scripts/start_embedding_service.sh
  115 +
  116 +curl -sS -X POST "http://127.0.0.1:6005/embed/text" \
  117 + -H "Content-Type: application/json" \
  118 + -d '["芭比娃娃 儿童玩具"]'
  119 +```
  120 +
  121 +返回应为 1024 维向量数组。
  122 +
  123 +## 7. 配置项(环境变量)
  124 +
  125 +`scripts/start_tei_service.sh` 支持下列变量:
  126 +
  127 +- `TEI_USE_GPU`:`1/0`(或 `true/false`),默认 `1`
  128 +- `TEI_CONTAINER_NAME`:容器名,默认 `saas-search-tei`
  129 +- `TEI_PORT`:宿主机端口,默认 `8080`
  130 +- `TEI_MODEL_ID`:默认 `Qwen/Qwen3-Embedding-0.6B`
  131 +- `TEI_VERSION`:镜像版本,默认 `1.9`
  132 +- `TEI_DTYPE`:默认 `float16`
  133 +- `TEI_MAX_BATCH_TOKENS`:默认 `2048`
  134 +- `TEI_MAX_CLIENT_BATCH_SIZE`:默认 `8`
  135 +- `HF_CACHE_DIR`:HF 缓存目录,默认 `$HOME/.cache/huggingface`
  136 +- `HF_TOKEN`:可选,避免匿名限速
  137 +
  138 +## 8. service_ctl 使用方式
  139 +
  140 +启动全套(含 TEI):
  141 +
  142 +```bash
  143 +START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 START_TEI=1 TEI_USE_GPU=1 ./scripts/service_ctl.sh start
  144 +```
  145 +
  146 +仅启动 TEI:
  147 +
  148 +```bash
  149 +./scripts/service_ctl.sh start tei
  150 +```
  151 +
  152 +查看状态:
  153 +
  154 +```bash
  155 +./scripts/service_ctl.sh status tei
  156 +```
  157 +
  158 +## 9. 常见问题与排障
  159 +
  160 +### 9.1 `ERROR: TEI_USE_GPU=1 but Docker nvidia runtime is not configured`
  161 +
  162 +- 原因:Docker 未配置 NVIDIA runtime。
  163 +- 处理:按本文 4.2 配置后重启 Docker。
  164 +
  165 +### 9.2 `mode mismatch (need GPU/CPU)`
  166 +
  167 +- 原因:已有同名容器在另一种模式运行(比如当前是 GPU 容器,但你这次要求 CPU)。
  168 +- 处理:
  169 +
  170 +```bash
  171 +./scripts/stop_tei_service.sh
  172 +TEI_USE_GPU=0 ./scripts/start_tei_service.sh # 或改为 1
  173 +```
  174 +
  175 +### 9.3 embedding 服务报 TEI 不可达
  176 +
  177 +- 先检查 TEI:
  178 +
  179 +```bash
  180 +curl -sS http://127.0.0.1:8080/health
  181 +```
  182 +
  183 +- 再检查 embedding 侧地址:
  184 + - `TEI_BASE_URL`
  185 + - `services.embedding.backends.tei.base_url`(`config/config.yaml`)
  186 +
  187 +## 10. 相关文档
  188 +
  189 +- 开发总览:`docs/QUICKSTART.md`
  190 +- 体系规范:`docs/DEVELOPER_GUIDE.md`
  191 +- embedding 模块:`embeddings/README.md`
  192 +- CN-CLIP 专项:`docs/CNCLIP_SERVICE说明文档.md`
  193 +
docs/Usage-Guide.md
@@ -56,6 +56,10 @@ TEI_USE_GPU=1 ./scripts/start_tei_service.sh @@ -56,6 +56,10 @@ TEI_USE_GPU=1 ./scripts/start_tei_service.sh
56 TEI_USE_GPU=0 ./scripts/start_tei_service.sh 56 TEI_USE_GPU=0 ./scripts/start_tei_service.sh
57 ``` 57 ```
58 58
  59 +专项说明:
  60 +- `docs/TEI_SERVICE说明文档.md`
  61 +- `docs/CNCLIP_SERVICE说明文档.md`
  62 +
59 #### 2. 启动Elasticsearch 63 #### 2. 启动Elasticsearch
60 64
61 **方式1: 使用Docker(推荐)** 65 **方式1: 使用Docker(推荐)**
embeddings/README.md
1 # Embeddings 模块 1 # Embeddings 模块
2 2
3 **请求示例**见 `docs/QUICKSTART.md` §3.3。 3 **请求示例**见 `docs/QUICKSTART.md` §3.3。
  4 +**专项文档**:
  5 +- `../docs/TEI_SERVICE说明文档.md`
  6 +- `../docs/CNCLIP_SERVICE说明文档.md`
4 7
5 --- 8 ---
6 9
@@ -45,7 +48,7 @@ @@ -45,7 +48,7 @@
45 INSTALL_LOCAL_ST=1 ./scripts/setup_embedding_venv.sh 48 INSTALL_LOCAL_ST=1 ./scripts/setup_embedding_venv.sh
46 ``` 49 ```
47 50
48 -2. **启动 CN-CLIP 服务**(独立 gRPC 服务,默认端口 51000,详见 `docs/CNCLIP_SERVICE说明文档.md`): 51 +2. **启动 CN-CLIP 服务**(独立 gRPC 服务,默认端口 51000,详见 `../docs/CNCLIP_SERVICE说明文档.md`):
49 ```bash 52 ```bash
50 ./scripts/start_cnclip_service.sh 53 ./scripts/start_cnclip_service.sh
51 ``` 54 ```
reranker/backends/qwen3_vllm.py
@@ -18,7 +18,7 @@ try: @@ -18,7 +18,7 @@ try:
18 import torch 18 import torch
19 from transformers import AutoTokenizer 19 from transformers import AutoTokenizer
20 from vllm import LLM, SamplingParams 20 from vllm import LLM, SamplingParams
21 - from vllm.inputs import TokensPrompt 21 + from vllm.inputs.data import TokensPrompt
22 except ImportError as e: 22 except ImportError as e:
23 raise ImportError( 23 raise ImportError(
24 "Qwen3-vLLM reranker backend requires vllm>=0.8.5 and transformers. " 24 "Qwen3-vLLM reranker backend requires vllm>=0.8.5 and transformers. "
@@ -110,21 +110,25 @@ class Qwen3VLLMRerankerBackend: @@ -110,21 +110,25 @@ class Qwen3VLLMRerankerBackend:
110 self, 110 self,
111 pairs: List[Tuple[str, str]], 111 pairs: List[Tuple[str, str]],
112 ) -> List[TokensPrompt]: 112 ) -> List[TokensPrompt]:
113 - """Build tokenized prompts for vLLM from (query, doc) pairs."""  
114 - prompts = []  
115 - for q, d in pairs:  
116 - messages = _format_instruction(self._instruction, q, d)  
117 - # One conversation per call (apply_chat_template expects single conversation)  
118 - token_ids = self._tokenizer.apply_chat_template(  
119 - messages,  
120 - tokenize=True,  
121 - add_generation_prompt=False,  
122 - enable_thinking=False, 113 + """Build tokenized prompts for vLLM from (query, doc) pairs. Batch apply_chat_template."""
  114 + messages_batch = [
  115 + _format_instruction(self._instruction, q, d) for q, d in pairs
  116 + ]
  117 + tokenized = self._tokenizer.apply_chat_template(
  118 + messages_batch,
  119 + tokenize=True,
  120 + add_generation_prompt=False,
  121 + enable_thinking=False,
  122 + )
  123 + # Single conv returns flat list; batch returns list of lists
  124 + if tokenized and not isinstance(tokenized[0], list):
  125 + tokenized = [tokenized]
  126 + prompts = [
  127 + TokensPrompt(
  128 + prompt_token_ids=ids[: self._max_prompt_len] + self._suffix_tokens
123 ) 129 )
124 - if isinstance(token_ids, list) and token_ids and isinstance(token_ids[0], list):  
125 - token_ids = token_ids[0]  
126 - ids = token_ids[: self._max_prompt_len] + self._suffix_tokens  
127 - prompts.append(TokensPrompt(prompt_token_ids=ids)) 130 + for ids in tokenized
  131 + ]
128 return prompts 132 return prompts
129 133
130 def _compute_scores( 134 def _compute_scores(
@@ -141,16 +145,23 @@ class Qwen3VLLMRerankerBackend: @@ -141,16 +145,23 @@ class Qwen3VLLMRerankerBackend:
141 if not out.outputs: 145 if not out.outputs:
142 scores.append(0.0) 146 scores.append(0.0)
143 continue 147 continue
144 - logprobs = out.outputs[0].logprobs  
145 - if not logprobs: 148 + final_logits = out.outputs[0].logprobs
  149 + if not final_logits:
146 scores.append(0.0) 150 scores.append(0.0)
147 continue 151 continue
148 - last = logprobs[-1]  
149 - true_logp = last.get(self._true_token)  
150 - false_logp = last.get(self._false_token)  
151 - true_p = math.exp(true_logp.logprob) if true_logp else 1e-10  
152 - false_p = math.exp(false_logp.logprob) if false_logp else 1e-10  
153 - score = true_p / (true_p + false_p) 152 + last = final_logits[-1]
  153 + # Match official: missing token -> logprob = -10
  154 + if self._true_token not in last:
  155 + true_logit = -10
  156 + else:
  157 + true_logit = last[self._true_token].logprob
  158 + if self._false_token not in last:
  159 + false_logit = -10
  160 + else:
  161 + false_logit = last[self._false_token].logprob
  162 + true_score = math.exp(true_logit)
  163 + false_score = math.exp(false_logit)
  164 + score = true_score / (true_score + false_score)
154 scores.append(float(score)) 165 scores.append(float(score))
155 return scores 166 return scores
156 167