Commit bc089b431d6d5e7755f50855bf2888e54fee8b34
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
Showing
9 changed files
with
360 additions
and
107 deletions
Show diff stats
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 | ... | ... |
| ... | ... | @@ -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
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 | ... | ... |