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
README.md
... ... @@ -82,6 +82,8 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh
82 82 | 3. API 详细说明 | `docs/搜索API对接指南.md` |
83 83 | 4. 快速参数速查 | `docs/搜索API速查表.md` |
84 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 204 tensor_parallel_size: 1
205 205 gpu_memory_utilization: 0.36
206 206 dtype: "float16"
207   - enable_prefix_caching: false
208   - enforce_eager: true
  207 + enable_prefix_caching: true
  208 + enforce_eager: false
209 209 instruction: "Given a web search query, retrieve relevant passages that answer the query"
210 210  
211 211 # SPU配置(已启用,使用嵌套skus)
... ...
docs/CNCLIP_SERVICE说明文档.md
1 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 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 52 ```bash
44 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 65 ./scripts/stop_cnclip_service.sh
46 66 ```
47 67  
48   -### 4.2 统一编排(推荐日常用法)
  68 +## 6. service_ctl 的行为与约定
  69 +
  70 +### 6.1 通过统一编排启动
49 71  
50 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 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 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 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 121 from clip_client import Client
83   -c = Client('grpc://localhost:51000')
  122 +c = Client('grpc://127.0.0.1:51000')
84 123 r = c.encode(['测试'])
85 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 132 ```bash
92 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 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 407 | 运维、日志、多环境、故障 | [Usage-Guide.md](./Usage-Guide.md) |
408 408 | 索引模块职责与 Java 对接 | [indexer/README.md](../indexer/README.md) |
409 409 | 向量模块与 clip-as-service | [embeddings/README.md](../embeddings/README.md) |
  410 +| TEI 服务专项(安装/部署/GPU-CPU 模式) | [TEI_SERVICE说明文档.md](./TEI_SERVICE说明文档.md) |
410 411 | CN-CLIP 服务专项(环境/运维/GPU) | [CNCLIP_SERVICE说明文档.md](./CNCLIP_SERVICE说明文档.md) |
411 412  
412 413 ### 10.2 仓库内入口
... ...
docs/QUICKSTART.md
... ... @@ -532,6 +532,7 @@ lsof -i :6004
532 532 | `docs/搜索API对接指南.md` | 搜索 API 完整说明 |
533 533 | `indexer/README.md` | 索引模块职责与接口 |
534 534 | `embeddings/README.md` | 向量化服务说明 |
  535 +| `docs/TEI_SERVICE说明文档.md` | TEI 专项(安装、部署、GPU/CPU 模式、排障) |
535 536 | `docs/CNCLIP_SERVICE说明文档.md` | CN-CLIP/clip-as-service 专项(环境、GPU、运维) |
536 537 | `reranker/README.md` | 重排服务说明 |
537 538  
... ...
docs/TEI_SERVICE说明文档.md 0 → 100644
... ... @@ -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 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 63 #### 2. 启动Elasticsearch
60 64  
61 65 **方式1: 使用Docker(推荐)**
... ...
embeddings/README.md
1 1 # Embeddings 模块
2 2  
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 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 52 ```bash
50 53 ./scripts/start_cnclip_service.sh
51 54 ```
... ...
reranker/backends/qwen3_vllm.py
... ... @@ -18,7 +18,7 @@ try:
18 18 import torch
19 19 from transformers import AutoTokenizer
20 20 from vllm import LLM, SamplingParams
21   - from vllm.inputs import TokensPrompt
  21 + from vllm.inputs.data import TokensPrompt
22 22 except ImportError as e:
23 23 raise ImportError(
24 24 "Qwen3-vLLM reranker backend requires vllm>=0.8.5 and transformers. "
... ... @@ -110,21 +110,25 @@ class Qwen3VLLMRerankerBackend:
110 110 self,
111 111 pairs: List[Tuple[str, str]],
112 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 132 return prompts
129 133  
130 134 def _compute_scores(
... ... @@ -141,16 +145,23 @@ class Qwen3VLLMRerankerBackend:
141 145 if not out.outputs:
142 146 scores.append(0.0)
143 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 150 scores.append(0.0)
147 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 165 scores.append(float(score))
155 166 return scores
156 167  
... ...