QAnything 阅读优化策略03——查询转换

📅 2026/7/22 7:56:43 👁️ 阅读次数 📝 编程学习
QAnything 阅读优化策略03——查询转换

查询转换(Query Transformation)

查询转换是将用户查询从文本形式转化为检索引擎能处理的格式(如 embedding 向量),同时做必要的清洗和保护。这是让"人类语言"适配"机器引擎"的关键桥梁。


一、是什么

查询转换是将用户查询从文本形式转化为检索引擎能处理的格式(如 embedding 向量),同时做必要的清洗和保护。

核心输入:改写后的检索查询
核心输出:归一化的 embedding 向量 + 保护后的查询文本


二、为什么重要

检索引擎不认识"人类语言",它只认识向量。

转换质量直接影响检索质量:

  • 图片标记![figure]会干扰 embedding 模型
  • 过长的 query 会导致 Rerank 模型截断
  • 不同组件需要不同的 Token 计数精度

与传统方案的对比

优化点传统方案传统方案的问题QAnything 方案优势
向量化平均池化(mean pooling)所有 token 平均,重要信息被稀释CLS token 提取[:,0]全局语义更聚焦
归一化不归一化余弦相似度计算需要除法,长向量主导排序L2 归一化余弦相似度 = 点积,加速计算
图片处理原样发送![figure](xxx.jpg)干扰语义发送前过滤图片标记检索向量更纯净
Token 计数统一用一个 tokenizertiktoken 计 100 token,实际可能 120三级 tokenizer + 安全余量各组件精度匹配
推理框架PyTorch 直接推理推理慢,部署体积大,无算子融合ONNX Runtime + 全图优化推理速度提升 2-3x
分数输出原始 logits(无界)阈值难设,不可解释Sigmoid + 线性拉伸0~1 可解释,区分度高

三、怎么做

3.1 Embedding 向量化 + L2 归一化

问题:文本需要转为向量才能在 Milvus 中检索。

方案:提取 CLS token 作为全局语义表示,然后 L2 归一化。

# Embedding ONNX 模型推理embedding=outputs_onnx[0][:,0]# 提取 CLS token(全局语义)# L2 归一化:使所有向量落到单位球面norm_arr=np.linalg.norm(embedding,axis=1,keepdims=True)embeddings_normalized=embedding/norm_arr

归一化的价值

  • 归一化后余弦相似度 = 点积,加速后续相似度计算
  • 所有向量长度一致,避免"长向量主导排序"的问题

为什么用 CLS token

  • BERT 类模型的[:, 0]token 经过自注意力聚合,代表整句话的全局语义
  • 比平均池化更能捕捉句子的核心含义

3.2 图片引用过滤

问题:查询中的![figure](xxx.jpg)会干扰 Embedding 模型的语义理解。

方案:发送前过滤掉图片和公式标记。

# Embedding 客户端:发送前过滤def_process_query(query):return'\n'.join([lineforlineinquery.split('\n')ifnotline.strip().startswith('![figure]')andnotline.strip().startswith('![equation]')])

为什么不在索引阶段过滤

  • 索引阶段的图片引用是文档内容的一部分
  • 但查询阶段用户不会主动写图片标记,这些通常是系统拼接进来的

3.3 三级 Tokenizer 精度体系

问题:不同组件对 token 计数的精度要求不同,用错 tokenizer 会导致切分不准或预算超支。

Tokenizer使用场景为什么用它
tiktoken(GPT)LLM Token 预算计算直接影响 API 成本,需要精确
embedding_tokenizer文件切分判断必须与 Embedding 模型的 max_length 对齐
rerank_tokenizerRerank 资格判断必须与 Rerank 模型的 max_length 对齐
# LLM Token 估算:额外加安全余量total_tokens*=1.1# tiktoken 精度较高,加 10%total_tokens*=1.2# 如果用 cl100k_base 兜底(中文模型偏差大),加 20%

安全余量的原因

  • tiktoken 是 GPT 专用 tokenizer,与国产 LLM(如 Qwen)有偏差
  • cl100k_base 是通用 tokenizer,中文场景偏差更大
  • 加余量是为了防止实际 token 超过预算

3.4 Rerank 查询长度保护

问题:Rerank 模型 max_length = 512,如果 query 太长就没有空间放 doc。

方案:query > 300 token 时跳过 Rerank,给 doc 留出 ~200 token 空间。

# 三个条件全部满足才执行 Rerankifrerankandlen(source_documents)>1andnum_tokens_rerank(query)<=300:source_documents=awaitself.rerank.arerank_documents(...)

为什么是 300 而不是 512

  • Rerank 输入 = query + doc,512 是总长度
  • 给 doc 留 200 token 空间,防止截断

3.5 ONNX Runtime 推理加速

问题:PyTorch 推理速度慢,部署体积大。

方案:使用 ONNX Runtime 进行模型推理。

sess_options=SessionOptions()sess_options.graph_optimization_level=GraphOptimizationLevel.ORT_ENABLE_ALL# 全图优化# 自动线程调度 + GPU/CPU 自适应providers=['CUDAExecutionProvider','CPUExecutionProvider']

优化项

  • 算子融合:将多个小算子合并为一个大算子
  • 常量折叠:编译期计算常量表达式
  • 自动线程调度:根据 CPU 核心数分配线程
  • 设备自适应:自动选择 GPU 或 CPU

3.6 Rerank Sigmoid 分数校准

问题:交叉编码器输出 logits(无界值),需要映射为 0~1 之间的可解释分数。

defsigmoid(x):scores=1/(1+np.exp(-x))# 标准 sigmoid → (0, 1)scores=np.clip(1.5*(scores-0.5)+0.5,0,1)# 线性拉伸 → 增强区分度returnscores

线性拉伸的作用

标准 sigmoid 输出: 0.48 0.50 0.52 0.55 → 区分度低,难以设定阈值 拉伸后: 0.47 0.50 0.53 0.575 → 高分更高,低分更低,区分度增强

四、解决的问题汇总

手段解决的问题
Embedding 归一化余弦相似度计算效率
图片引用过滤无效 token 干扰语义
三级 Tokenizer不同组件精度需求不同
Rerank 长度保护Rerank 模型输入截断
ONNX 加速推理速度慢
Sigmoid 校准分数无界不可解释