Commit cd6d887e288e6b350d3ea71a263cc0d66503c9a9
1 parent
ea118f2b
reranker doc
Showing
3 changed files
with
478 additions
and
2 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,460 @@ |
| 1 | + | |
| 2 | +## 共享前缀+批量后缀 | |
| 3 | + | |
| 4 | +**"共享前缀批量推理"**(Shared-Prefix Batch Inference): | |
| 5 | +- 输入结构:`[Prefix A] + [Suffix B1]`、`[Prefix A] + [Suffix B2]`、`[Prefix A] + [Suffix B3]`... | |
| 6 | +- 痛点:Prefix A被重复计算成千上万次,浪费算力和时间 | |
| 7 | +- 理想方案:Prefix A只推理一次,KV Cache复用给所有后缀 | |
| 8 | + | |
| 9 | +--- | |
| 10 | + | |
| 11 | +## 一、顶级推荐方案 | |
| 12 | + | |
| 13 | +### 1. **FlashInfer + Cascade Attention** ⭐ 最强技术 | |
| 14 | +- **核心创新**:Cascade Inference(级联推理) | |
| 15 | +- **性能提升**:相比vLLM的PageAttention,在32K token共享前缀、batch size 256场景下可达 **31倍加速** | |
| 16 | +- **原理**: | |
| 17 | + - 将Attention计算分解为两个阶段: | |
| 18 | + 1. **多查询阶段**:对共享前缀使用Multi-Query Kernel,只计算一次,结果存入Shared Memory | |
| 19 | + 2. **批量解码阶段**:对每个唯一后缀使用Batch Decode Kernel | |
| 20 | + - 使用结合律算子合并部分Attention状态,类似FlashAttention的分块策略 | |
| 21 | +- **适用场景**:文档QA、系统提示词复用、RAG批量检索 | |
| 22 | +- **集成**:已集成到SGLang和vLLM中作为后端 | |
| 23 | +- **GitHub**: https://github.com/flashinfer-ai/flashinfer | |
| 24 | + | |
| 25 | +### 2. **SGLang + RadixAttention** ⭐ 最实用框架 | |
| 26 | +- **核心创新**:RadixTree(基数树)管理KV Cache | |
| 27 | +- **自动前缀复用**:无需手动配置,自动识别共享前缀并复用KV Cache | |
| 28 | +- **性能**:相比vLLM、LMQL等基线系统,结构化工作负载上可达 **6.4倍吞吐量提升** 和 **3.7倍延迟降低** | |
| 29 | +- **关键特性**: | |
| 30 | + - **In-Batch Prefix Caching**:同一batch内自动共享前缀(如你的A+B1, A+B2场景) | |
| 31 | + - **Multi-Item Scoring (MIS)**:LinkedIn用于推荐排序的优化,将多个候选项合并为单次前向传播 | |
| 32 | + - **Zero-Overhead CPU Scheduler**:GPU计算时CPU并行准备下一batch,利用率接近100% | |
| 33 | +- **特别适合**:Agent系统、工具链、RAG应用 | |
| 34 | +- **GitHub**: https://github.com/sgl-project/sglang | |
| 35 | + | |
| 36 | +### 3. **vLLM + Automatic Prefix Caching** ⭐ 最成熟稳定 | |
| 37 | +- **核心机制**:基于哈希表的块级前缀缓存 | |
| 38 | +- **工作原理**: | |
| 39 | + - 将KV Cache按块(默认16 tokens)哈希 | |
| 40 | + - 新请求先查哈希表,命中则直接复用,只计算新tokens | |
| 41 | + - 使用LRU策略管理缓存 eviction | |
| 42 | +- **使用方式**: | |
| 43 | + ```python | |
| 44 | + from vllm import LLM, SamplingParams | |
| 45 | + # 启用prefix caching | |
| 46 | + llm = LLM(model="your-model", enable_prefix_caching=True) | |
| 47 | + | |
| 48 | + # 第一次调用缓存前缀 | |
| 49 | + outputs = llm.generate(long_prefix + prompt_1, sampling_params) | |
| 50 | + # 第二次调用自动命中缓存,prefix部分零计算 | |
| 51 | + outputs = llm.generate(long_prefix + prompt_2, sampling_params) | |
| 52 | + ``` | |
| 53 | +- **注意事项**:vLLM 0.6.3之前调度器未考虑缓存命中率,高并发长序列场景可能性能下降,建议升级到0.6.5+ | |
| 54 | + | |
| 55 | +--- | |
| 56 | + | |
| 57 | +## 二、其他重要方案 | |
| 58 | + | |
| 59 | +### 4. **TensorRT-LLM + In-Flight Batching** | |
| 60 | +- **优势**:NVIDIA官方优化,与FlashInfer深度集成 | |
| 61 | +- **特性**: | |
| 62 | + - 支持Prefix Caching(具体实现闭源,但概念类似vLLM) | |
| 63 | + - XQA Kernel(Flash Attention 3变体)优化内存访问 | |
| 64 | + - 层融合技术减少中间结果存储 | |
| 65 | +- **性能**:在共享前缀数据集上,吞吐量提升 **~34.7%**,TPOT降低 **~20.9%** | |
| 66 | +- **适用**:NVIDIA GPU生产环境,追求极致性能 | |
| 67 | + | |
| 68 | +### 5. **LMDeploy + TurboMind** | |
| 69 | +- **定位**:纯C++引擎,消除Python开销 | |
| 70 | +- **性能**:与SGLang相当,在H100上可达 **~16,200 tok/s**(vLLM为12,553 tok/s) | |
| 71 | +- **优化**:支持KV Cache量化(8-bit)、Continuous Batching | |
| 72 | +- **适用**:高吞吐生产部署,对延迟敏感的场景 | |
| 73 | + | |
| 74 | +### 6. **Daft + Dynamic Prefix Bucketing**(大数据场景) | |
| 75 | +- **创新点**:动态前缀分桶 + 流式Continuous Batching | |
| 76 | +- **解决痛点**: | |
| 77 | + - 全局排序分组会导致GPU空闲 | |
| 78 | + - 动态分桶在推理同时进行前缀分组,实现流水线 | |
| 79 | +- **性能**:128 GPU集群上,20万prompts(1.28亿tokens)处理速度提升 **50.7%** | |
| 80 | +- **适用**:大规模离线批处理(如数据标注、合成数据生成) | |
| 81 | + | |
| 82 | +--- | |
| 83 | + | |
| 84 | +## 三、针对你的具体场景建议 | |
| 85 | + | |
| 86 | +### 场景1:在线服务(RAG检索、实时重排序) | |
| 87 | +**推荐栈**:**SGLang** 或 **vLLM + FlashInfer后端** | |
| 88 | + | |
| 89 | +```python | |
| 90 | +# SGLang示例:自动前缀复用 | |
| 91 | +import sglang as sgl | |
| 92 | + | |
| 93 | +@sgl.function | |
| 94 | +def rerank(s, query, docs): | |
| 95 | + # query是共享前缀,docs是批量后缀 | |
| 96 | + s += "Query: " + query + "\n" | |
| 97 | + s += "Document: " + sgl.arg(docs) + "\nRelevance:" | |
| 98 | + s += sgl.gen("score", max_tokens=5) | |
| 99 | + | |
| 100 | +# 批量执行,自动共享query部分的KV Cache | |
| 101 | +docs = ["doc1 content", "doc2 content", "doc3 content", ...] # 成千上万个 | |
| 102 | +state = rerank.run_batch( | |
| 103 | + [{"query": "user query", "docs": d} for d in docs], | |
| 104 | + max_new_tokens=5 | |
| 105 | +) | |
| 106 | +``` | |
| 107 | + | |
| 108 | +### 场景2:离线批量处理(数据标注、索引构建) | |
| 109 | +**推荐栈**:**Daft** 或 **FlashInfer原生API** | |
| 110 | + | |
| 111 | +```python | |
| 112 | +# Daft示例:动态前缀分桶 | |
| 113 | +import daft | |
| 114 | +from daft.functions import prompt | |
| 115 | + | |
| 116 | +df = daft.from_pydict({ | |
| 117 | + "query": ["shared query"] * 10000, | |
| 118 | + "doc": ["doc1", "doc2", ...] # 不同后缀 | |
| 119 | +}) | |
| 120 | + | |
| 121 | +df = df.with_column("score", | |
| 122 | + prompt( | |
| 123 | + df["query"] + "\n" + df["doc"], | |
| 124 | + provider="vllm-prefix-caching", # 利用前缀缓存 | |
| 125 | + model="your-model" | |
| 126 | + ) | |
| 127 | +) | |
| 128 | +``` | |
| 129 | + | |
| 130 | +### 场景3:Embedding/Reranker模型(Bi-Encoder/Cross-Encoder) | |
| 131 | +**推荐栈**:**Sentence-Transformers优化** + **ONNX/TensorRT** | |
| 132 | + | |
| 133 | +```python | |
| 134 | +# Cross-Encoder批量重排序优化 | |
| 135 | +from sentence_transformers import CrossEncoder | |
| 136 | +import numpy as np | |
| 137 | + | |
| 138 | +class OptimizedReranker: | |
| 139 | + def __init__(self, model_name="cross-encoder/ms-marco-MiniLM-L-6-v2"): | |
| 140 | + self.model = CrossEncoder(model_name, max_length=512, device="cuda") | |
| 141 | + | |
| 142 | + def rerank_batch(self, query, documents, batch_size=32): | |
| 143 | + # 构建所有pairs:[query, doc1], [query, doc2], ... | |
| 144 | + pairs = [[query, doc] for doc in documents] | |
| 145 | + | |
| 146 | + # 单次批量推理,自动共享query的编码计算 | |
| 147 | + scores = self.model.predict( | |
| 148 | + pairs, | |
| 149 | + batch_size=batch_size, | |
| 150 | + convert_to_numpy=True | |
| 151 | + ) | |
| 152 | + return np.argsort(scores)[::-1] | |
| 153 | +``` | |
| 154 | + | |
| 155 | +--- | |
| 156 | + | |
| 157 | +## 四、性能优化关键技巧 | |
| 158 | + | |
| 159 | +### 1. **Prompt构造策略** | |
| 160 | +- **对齐块边界**:确保共享前缀长度是KV Cache块大小(通常是16或32)的整数倍,避免部分块重计算 | |
| 161 | +- **静态前缀前置**:将不变的指令、系统提示放在最前面 | |
| 162 | + | |
| 163 | +### 2. **Batch构造策略** | |
| 164 | +- **前缀分桶(Prefix Bucketing)**:将相同前缀的请求分到同一batch,最大化缓存命中率 | |
| 165 | +- **长度排序**:同batch内按后缀长度排序,减少padding浪费 | |
| 166 | + | |
| 167 | +### 3. **硬件/精度优化** | |
| 168 | +- **FP16/BF16**:混合精度推理,吞吐量提升1.5-2倍 | |
| 169 | +- **KV Cache量化**:8-bit量化减少内存带宽压力,提升20%吞吐 | |
| 170 | +- **CUDA Graphs**:捕获静态计算图,减少CPU开销(适合固定batch size场景) | |
| 171 | + | |
| 172 | +--- | |
| 173 | + | |
| 174 | +## 五、方案对比总结 | |
| 175 | + | |
| 176 | +| 方案 | 前缀复用方式 | 适用场景 | 性能提升 | 易用性 | 成熟度 | | |
| 177 | +|------|-------------|---------|---------|--------|--------| | |
| 178 | +| **FlashInfer** | Cascade Attention | 超长前缀(32K+) | 31x | 中(需集成) | 高 | | |
| 179 | +| **SGLang** | RadixTree自动缓存 | Agent/RAG/工具链 | 6.4x | 高 | 高 | | |
| 180 | +| **vLLM** | 哈希块缓存 | 通用在线服务 | 2-5x | 高 | 极高 | | |
| 181 | +| **TensorRT-LLM** | 内部优化 | NVIDIA生产环境 | 1.3-2x | 中 | 高 | | |
| 182 | +| **LMDeploy** | TurboMind C++引擎 | 低延迟高吞吐 | 1.3x | 中 | 高 | | |
| 183 | +| **Daft** | 动态分桶 | 大规模离线批处理 | 50%+ | 高 | 中 | | |
| 184 | + | |
| 185 | +--- | |
| 186 | + | |
| 187 | +## 六、推荐实施路径 | |
| 188 | + | |
| 189 | +1. **快速验证**:先用 **vLLM** 开启`enable_prefix_caching=True`验证效果,零代码改动 | |
| 190 | +2. **性能突破**:切换到 **SGLang**,利用RadixAttention和自动batch优化,获得显著性能提升 | |
| 191 | +3. **极致优化**:对超长前缀场景,使用 **FlashInfer** 的Cascade Attention内核,或集成到自定义推理引擎中 | |
| 192 | + | |
| 193 | +需要针对具体框架的详细配置代码或部署架构建议吗? | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | +## 天然支持Prefix Caching的Reranker模型清单 | |
| 198 | + | |
| 199 | +最新版本 (v2 系列):混合架构 | |
| 200 | +在 BGE-Reranker-v2 中,BAAI 为了提升模型上限,引入了不同的底座: | |
| 201 | + | |
| 202 | +BGE-Reranker-v2-m3: 依然保持了 Encoder-only 架构(基于 XLM-RoBERTa),支持多语言和长文本,具有极高的性价比。 | |
| 203 | + | |
| 204 | +BGE-Reranker-v2-Gemma/Llama: 采用了 Decoder-only 架构。 | |
| 205 | + | |
| 206 | +这些模型是基于 Gemma-2b 或 Llama-3-8B 等大语言模型(LLM)微调而来的。 | |
| 207 | + | |
| 208 | +虽然原生 LLM 是单向(Causal)注意力的,但在作为 Reranker 使用时,模型通常会通过特殊的 Prompt 引导,并取最后一个 Token 的输出经过一个线性层来计算相关性分数。 | |
| 209 | + | |
| 210 | + | |
| 211 | +### 1. **BGE-Reranker-V2/V2.5 系列** ⭐ 强烈推荐 | |
| 212 | +基于Gemma/MiniCPM等Decoder-only架构,FlagEmbedding官方实现已优化 | |
| 213 | + | |
| 214 | +| 模型 | 架构 | 参数量 | 特点 | | |
| 215 | +|------|------|--------|------| | |
| 216 | +| `BAAI/bge-reranker-v2-gemma` | Gemma-2B (Decoder-only) | 2B | 多语言强,基础版 | | |
| 217 | +| `BAAI/bge-reranker-v2-minicpm-layerwise` | MiniCPM-2B (Decoder-only) | 2B | **支持层选择**,可截断到第24层加速 | | |
| 218 | +| `BAAI/bge-reranker-v2.5-gemma2-lightweight` | Gemma2-9B (Decoder-only) | 9B | **Token压缩+层选择**,极致效率 | | |
| 219 | + | |
| 220 | +**Prefix Caching友好特性** : | |
| 221 | +- 输入格式:`[Query A] + [SEP] + [Document B] + [Prompt]` | |
| 222 | +- Query部分作为前缀,可被所有Document共享 | |
| 223 | +- 官方代码中已实现`compute_score_single_gpu`的batch处理,自动对齐长度排序减少padding | |
| 224 | + | |
| 225 | +**使用示例**: | |
| 226 | +```python | |
| 227 | +from FlagEmbedding import FlagAutoReranker | |
| 228 | + | |
| 229 | +# 启用vLLM后端 + Prefix Caching | |
| 230 | +reranker = FlagAutoReranker.from_finetuned( | |
| 231 | + 'BAAI/bge-reranker-v2-gemma', | |
| 232 | + model_class='decoder-only-base', | |
| 233 | + use_fp16=True, | |
| 234 | + devices=['cuda:0'] | |
| 235 | +) | |
| 236 | + | |
| 237 | +# 批量推理:Query自动复用KV Cache | |
| 238 | +pairs = [ | |
| 239 | + ('what is panda?', 'The giant panda is a bear species...'), # A+B1 | |
| 240 | + ('what is panda?', 'Pandas are popular zoo animals.'), # A+B2 (Query复用) | |
| 241 | + ('what is panda?', 'Pandas eat bamboo and live in China.'), # A+B3 (Query复用) | |
| 242 | +] | |
| 243 | +scores = reranker.compute_score(pairs, batch_size=32) | |
| 244 | +``` | |
| 245 | + | |
| 246 | +### 2. **Qwen3-Reranker 系列** ⭐ 国产最强 | |
| 247 | +基于Qwen3 Decoder-only架构,阿里云官方支持 | |
| 248 | + | |
| 249 | +| 模型 | 架构 | 参数量 | 特点 | | |
| 250 | +|------|------|--------|------| | |
| 251 | +| `Qwen/Qwen3-Reranker-0.6B` | Qwen3-0.6B (Decoder-only) | 0.6B | 超轻量,快速 | | |
| 252 | +| `Qwen/Qwen3-Reranker-4B` | Qwen3-4B (Decoder-only) | 4B | 性能均衡 | | |
| 253 | +| `Qwen/Qwen3-Reranker-8B` | Qwen3-8B (Decoder-only) | 8B | 精度最高 | | |
| 254 | + | |
| 255 | +**架构细节** : | |
| 256 | +- **纯Decoder-only架构**:使用因果注意力,天然支持Prefix Caching | |
| 257 | +- **输入模板**: | |
| 258 | + ``` | |
| 259 | + <|im_start|>system | |
| 260 | + You are a helpful assistant. | |
| 261 | + <|im_end|> | |
| 262 | + <|im_start|>user | |
| 263 | + Query: {query} | |
| 264 | + Document: {document} | |
| 265 | + Does the document answer the query? Please answer Yes or No. | |
| 266 | + <|im_end|> | |
| 267 | + <|im_start|>assistant | |
| 268 | + Yes | |
| 269 | + ``` | |
| 270 | +- **输出**:只生成"Yes"或"No"的logits,作为相关性分数 | |
| 271 | + | |
| 272 | +**vLLM部署优化** : | |
| 273 | +```bash | |
| 274 | +# 启动vLLM服务,启用Prefix Caching | |
| 275 | +python -m vllm.entrypoints.openai.api_server \ | |
| 276 | + --model Qwen/Qwen3-Reranker-8B \ | |
| 277 | + --tensor-parallel-size 1 \ | |
| 278 | + --dtype half \ | |
| 279 | + --max-model-len 32768 \ | |
| 280 | + --enable-prefix-caching # 关键参数!提速40% | |
| 281 | +``` | |
| 282 | + | |
| 283 | +### 3. **Jina-Reranker-V3** ⭐ 创新架构 | |
| 284 | +基于Qwen3-0.6B的Listwise重排序器,支持跨Document注意力 | |
| 285 | + | |
| 286 | +| 模型 | 架构 | 参数量 | 特点 | | |
| 287 | +|------|------|--------|------| | |
| 288 | +| `jinaai/jina-reranker-v3` | Qwen3-0.6B (Decoder-only) | 0.6B | **Listwise**,单次处理64个docs | | |
| 289 | + | |
| 290 | +**独特优势** : | |
| 291 | +- **"Last but Not Late"交互**:在单个context window中同时处理Query+所有Documents | |
| 292 | +- **跨Document注意力**:通过因果注意力实现Document间交互,捕捉相对相关性 | |
| 293 | +- **Prefix Caching优化**:Query放在序列开头,可被所有Document共享 | |
| 294 | + | |
| 295 | +**输入格式** : | |
| 296 | +```xml | |
| 297 | +<|im_start|>system | |
| 298 | +You are a search relevance expert... | |
| 299 | +<|im_end|> | |
| 300 | +<|im_start|>user | |
| 301 | +Rank the passages based on their relevance to query: [QUERY] | |
| 302 | + | |
| 303 | +<passage id="1">[DOC_1]<|doc_emb|></passage> | |
| 304 | +<passage id="2">[DOC_2]<|doc_emb|></passage> | |
| 305 | +... | |
| 306 | +<passage id="k">[DOC_k]<|doc_emb|></passage> | |
| 307 | + | |
| 308 | +<query>[QUERY]<|query_emb|></query> | |
| 309 | +<|im_end|> | |
| 310 | +``` | |
| 311 | + | |
| 312 | +**性能**:BEIR nDCG@10达61.94,超过Qwen3-Reranker-4B,体积小6倍 | |
| 313 | + | |
| 314 | +### 4. **E5-Mistral / NV-Embed-v2 / SFR-Embedding-Mistral** | |
| 315 | +基于Mistral-7B Decoder-only架构的Embedding/Reranker | |
| 316 | + | |
| 317 | +| 模型 | 架构 | 用途 | 特点 | | |
| 318 | +|------|------|------|------| | |
| 319 | +| `intfloat/e5-mistral-7b-instruct` | Mistral-7B (Decoder-only) | Embedding | 指令微调,支持多任务 | | |
| 320 | +| `nvidia/NV-Embed-v2` | Mistral-7B (Decoder-only) | Embedding | 潜在注意力层优化 | | |
| 321 | +| `Salesforce/SFR-Embedding-Mistral` | Mistral-7B (Decoder-only) | Embedding | 长上下文优化 | | |
| 322 | + | |
| 323 | +**注意**:这些主要是**Embedding模型**(Bi-encoder),但可作为Reranker使用(如通过余弦相似度)。若需Cross-encoder式重排序,需配合其他技术。 | |
| 324 | + | |
| 325 | +### 5. **RankGPT / RankZephyr / RankLLaMA** | |
| 326 | +基于LLM的生成式重排序器 | |
| 327 | + | |
| 328 | +| 模型 | 架构 | 特点 | | |
| 329 | +|------|------|------| | |
| 330 | +| `RankGPT` (GPT-3.5/4) | Decoder-only API | 通过Prompt让LLM判断相关性 | | |
| 331 | +| `RankZephyr` | Zephyr-7B (Decoder-only) | 蒸馏自RankGPT,开源可部署 | | |
| 332 | +| `RankLLaMA` | LLaMA-2/3 (Decoder-only) | 本地部署,隐私友好 | | |
| 333 | + | |
| 334 | +**实现方式**: | |
| 335 | +- 使用LLM的logits或生成"Yes/No"判断相关性 | |
| 336 | +- 完全基于Decoder-only架构,天然支持Prefix Caching | |
| 337 | + | |
| 338 | +--- | |
| 339 | + | |
| 340 | +## 不支持Prefix Caching的Reranker(双向架构) | |
| 341 | + | |
| 342 | +### ❌ **BGE-Reranker-V1 / BGE-Reranker-Base/Large** | |
| 343 | +- **架构**:基于XLM-RoBERTa(Encoder-only,双向注意力) | |
| 344 | +- **问题**:Query和Document拼接后`[CLS]` token的表示依赖于整个序列,无法分离缓存 | |
| 345 | +- **适用场景**:轻量级、短文本,对延迟不敏感 | |
| 346 | + | |
| 347 | +### ❌ **Cross-Encoder (BERT-based)** | |
| 348 | +- **架构**:BERT/RoBERTa等Encoder-only模型 | |
| 349 | +- **问题**: | |
| 350 | + - 每个Query-Document对必须**联合编码** | |
| 351 | + - 前缀的KV Cache与后续token强耦合,无法复用 | |
| 352 | + - 计算复杂度O((|Q|+|D|)²),无法分解为O(|Q|²) + O(|D|²) | |
| 353 | + | |
| 354 | +### ❌ **ColBERT / ColPali / Late Interaction模型** | |
| 355 | +- **架构**:基于BERT的双向编码 + 后期MaxSim交互 | |
| 356 | +- **问题**: | |
| 357 | + - **独立编码,但双向注意力**:Query和Document分别编码,但各自内部仍是双向 | |
| 358 | + - **无法Prefix Cache**:虽然Query可独立编码,但Document的编码不依赖于Query,所以不存在"共享前缀"场景 | |
| 359 | + - **优化点**:Document可**预计算并离线存储**,Query实时编码,然后做MaxSim | |
| 360 | + - **本质区别**:这是"离线预计算"而非"Prefix Caching",适用于Document固定、Query变化的场景 | |
| 361 | + | |
| 362 | +**ColBERT的优化策略** : | |
| 363 | +```python | |
| 364 | +# ColBERT流程:Document预计算(离线) + Query实时编码(在线) | |
| 365 | +document_embeddings = encode_documents(docs) # 离线,一次性 | |
| 366 | +query_embedding = encode_query(query) # 在线,每次查询 | |
| 367 | +scores = maxsim(query_embedding, document_embeddings) # 轻量级交互 | |
| 368 | +``` | |
| 369 | + | |
| 370 | +--- | |
| 371 | + | |
| 372 | +## 实际部署建议 | |
| 373 | + | |
| 374 | +### 场景1:高并发在线服务(Query多变,Document固定) | |
| 375 | +**推荐**:**Jina-Reranker-V3** 或 **Qwen3-Reranker + vLLM Prefix Caching** | |
| 376 | + | |
| 377 | +```python | |
| 378 | +# vLLM配置优化 | |
| 379 | +from vllm import LLM, SamplingParams | |
| 380 | + | |
| 381 | +llm = LLM( | |
| 382 | + model="Qwen/Qwen3-Reranker-8B", | |
| 383 | + enable_prefix_caching=True, # 关键! | |
| 384 | + max_num_seqs=256, # 高并发 | |
| 385 | + max_model_len=32768 | |
| 386 | +) | |
| 387 | + | |
| 388 | +# 批量构造Prompts:共享Query前缀 | |
| 389 | +query = "What is the capital of France?" | |
| 390 | +docs = ["Paris is the capital...", "France is a country...", "Berlin is the capital of Germany..."] | |
| 391 | + | |
| 392 | +prompts = [ | |
| 393 | + f"Query: {query}\nDocument: {doc}\nRelevant:" | |
| 394 | + for doc in docs | |
| 395 | +] | |
| 396 | + | |
| 397 | +# vLLM自动识别共享前缀,只计算一次Query的KV Cache | |
| 398 | +sampling_params = SamplingParams(max_tokens=1, temperature=0) | |
| 399 | +outputs = llm.generate(prompts, sampling_params) | |
| 400 | +``` | |
| 401 | + | |
| 402 | +### 场景2:离线批量重排序(Query固定,Document多变) | |
| 403 | +**推荐**:**ColBERT / ColPali**(Document预计算策略) | |
| 404 | + | |
| 405 | +```python | |
| 406 | +from rankify.models.reranking import Reranking | |
| 407 | +from rankify.dataset.dataset import Document, Question, Context | |
| 408 | + | |
| 409 | +# ColBERT:Document预计算,Query实时编码 | |
| 410 | +model = Reranking(method='colbert_ranker', model_name='Colbert') | |
| 411 | + | |
| 412 | +# Documents已预计算并存储 | |
| 413 | +document = Document( | |
| 414 | + question=Question("What is RAG?"), | |
| 415 | + contexts=[Context(text=doc, id=i) for i, doc in enumerate(docs)] | |
| 416 | +) | |
| 417 | + | |
| 418 | +# 只需编码Query,然后MaxSim计算 | |
| 419 | +model.rank([document]) | |
| 420 | +``` | |
| 421 | + | |
| 422 | +### 场景3:极致性能 + 高精度 | |
| 423 | +**推荐**:**BGE-Reranker-V2.5-Gemma2-Lightweight**(Token压缩 + 层选择) | |
| 424 | + | |
| 425 | +```python | |
| 426 | +from FlagEmbedding import LightWeightFlagLLMReranker | |
| 427 | + | |
| 428 | +reranker = LightWeightFlagLLMReranker( | |
| 429 | + 'BAAI/bge-reranker-v2.5-gemma2-lightweight', | |
| 430 | + devices=["cuda:0"], | |
| 431 | + use_fp16=True | |
| 432 | +) | |
| 433 | + | |
| 434 | +# 综合优化:层截断 + Token压缩 | |
| 435 | +scores = reranker.compute_score( | |
| 436 | + pairs, | |
| 437 | + cutoff_layers=[28], # 只用前28层 | |
| 438 | + compress_ratio=4, # Token压缩4倍 | |
| 439 | + compress_layers=[24, 40] # 特定层压缩 | |
| 440 | +) | |
| 441 | +``` | |
| 442 | + | |
| 443 | +--- | |
| 444 | + | |
| 445 | +## 总结对比表 | |
| 446 | + | |
| 447 | +| 模型 | 架构 | Prefix Caching | 适用场景 | 性能/效率 | | |
| 448 | +|------|------|---------------|---------|----------| | |
| 449 | +| **BGE-Reranker-V2/V2.5** | Decoder-only ✅ | ✅ 原生支持 | 多语言、生产环境 | 高 | | |
| 450 | +| **Qwen3-Reranker** | Decoder-only ✅ | ✅ vLLM支持 | 中文优先、高精度 | 极高 | | |
| 451 | +| **Jina-Reranker-V3** | Decoder-only ✅ | ✅ Listwise优化 | 跨Doc交互、Top-K排序 | 极高 | | |
| 452 | +| **E5-Mistral/NV-Embed** | Decoder-only ✅ | ✅ 需配合框架 | Embedding+轻量重排 | 高 | | |
| 453 | +| **RankGPT/Zephyr** | Decoder-only ✅ | ✅ API/本地 | 生成式判断 | 中 | | |
| 454 | +| **BGE-Reranker-V1** | Encoder-only ❌ | ❌ 不支持 | 轻量、短文本 | 低 | | |
| 455 | +| **ColBERT/ColPali** | 双向+后期交互 | ❌ 不适用(预计算替代) | Document固定场景 | 中 | | |
| 456 | + | |
| 457 | +**最终建议**: | |
| 458 | +- 如果追求**Prefix Caching加速** + **高精度**:选择 **Qwen3-Reranker-8B** 或 **BGE-Reranker-V2.5**,配合 **vLLM + FlashInfer** 部署 | |
| 459 | +- 如果需要**跨Document比较**(Listwise):选择 **Jina-Reranker-V3** | |
| 460 | +- 如果**Document固定且量大**:选择 **ColBERT** 预计算策略(虽非Prefix Caching,但效率类似) | |
| 0 | 461 | \ No newline at end of file | ... | ... |
docs/搜索API对接指南.md
| ... | ... | @@ -148,6 +148,17 @@ curl -X POST "http://120.76.41.98:6002/search/" \ |
| 148 | 148 | |
| 149 | 149 | - **端点**: `POST /search/` |
| 150 | 150 | - **描述**: 执行文本搜索查询,支持多语言、布尔表达式、过滤器和分面搜索 |
| 151 | +- **租户标识**:`tenant_id` 通过 HTTP 请求头 **`X-Tenant-ID`** 传递(推荐);也可通过 URL query 参数 **`tenant_id`** 传递。**不要放在请求体中。** | |
| 152 | + | |
| 153 | +**请求示例(推荐)**: | |
| 154 | +```python | |
| 155 | +url = f"{base_url.rstrip('/')}/search/" | |
| 156 | +headers = { | |
| 157 | + "Content-Type": "application/json", | |
| 158 | + "X-Tenant-ID": "162", # 租户ID,必填 | |
| 159 | +} | |
| 160 | +response = requests.post(url, headers=headers, json={"query": "芭比娃娃"}) | |
| 161 | +``` | |
| 151 | 162 | |
| 152 | 163 | ### 3.2 请求参数 |
| 153 | 164 | |
| ... | ... | @@ -605,6 +616,7 @@ curl "http://localhost:6002/search/instant?q=玩具&size=5" |
| 605 | 616 | |
| 606 | 617 | - **端点**: `GET /search/{doc_id}` |
| 607 | 618 | - **描述**: 根据文档 ID 获取单个商品详情,用于点击结果后的详情页或排查问题。 |
| 619 | +- **租户标识**:同 [3.1](#31-接口信息),通过请求头 `X-Tenant-ID` 或 query 参数 `tenant_id` 传递。 | |
| 608 | 620 | |
| 609 | 621 | #### 路径参数 |
| 610 | 622 | |
| ... | ... | @@ -630,7 +642,8 @@ curl "http://localhost:6002/search/instant?q=玩具&size=5" |
| 630 | 642 | #### 请求示例 |
| 631 | 643 | |
| 632 | 644 | ```bash |
| 633 | -curl "http://localhost:6002/search/12345" | |
| 645 | +curl "http://localhost:6002/search/12345" -H "X-Tenant-ID: 162" | |
| 646 | +# 或使用 query 参数:curl "http://localhost:6002/search/12345?tenant_id=162" | |
| 634 | 647 | ``` |
| 635 | 648 | |
| 636 | 649 | --- |
| ... | ... | @@ -1287,6 +1300,8 @@ curl -X GET "http://localhost:6004/indexer/health" |
| 1287 | 1300 | |
| 1288 | 1301 | ## 常见场景示例 |
| 1289 | 1302 | |
| 1303 | +以下示例仅展示**请求体**(body);实际调用时请加上请求头 `X-Tenant-ID: <租户ID>`(或 URL 参数 `tenant_id`),参见 [3.1 接口信息](#31-接口信息)。 | |
| 1304 | + | |
| 1290 | 1305 | ### 7.1 基础搜索与排序 |
| 1291 | 1306 | |
| 1292 | 1307 | **按价格从低到高排序**: | ... | ... |
indexer/document_transformer.py
| ... | ... | @@ -348,7 +348,8 @@ class SPUDocumentTransformer: |
| 348 | 348 | if pd.notna(spu_row.get('image_src')): |
| 349 | 349 | image_src = str(spu_row['image_src']) |
| 350 | 350 | if not image_src.startswith('http'): |
| 351 | - image_src = f"//{image_src}" if image_src.startswith('//') else image_src | |
| 351 | + # 仅当尚未是协议相对 URL 时才补 "//",避免 "//host" 变成 "////host" | |
| 352 | + image_src = f"//{image_src}" if not image_src.startswith('//') else image_src | |
| 352 | 353 | doc['image_url'] = image_src |
| 353 | 354 | |
| 354 | 355 | def _process_skus( | ... | ... |