e756b18e
tangwang
重构了文本召回构建器,现在每个 b...
|
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
2. 核心搜索功能优化
2.1 意图识别模块
- 增加款式意图识别模块
- 意图类型: 颜色,尺码(目前只需要支持这两种)
- 意图召回层:
每种意图,有一个召回词集合
对query(包括原始query、各种翻译query 都做匹配)
- 以颜色意图为例:
有一个词表,每一行 都逗号分割,互为同义词,行内第一个为标准化词
query匹配了其中任何一个词,都认为,具有颜色意图
匹配规则: 用细粒度、粗粒度分词,看是否有在词表中的。原始query分词、和每种翻译的分词,都要用。
- 意图判断:
暂时留空,直接返回true。目前没有模型,即只要召回了(词表匹配了),即认为有该维度款式需求。
(以后考虑建设fasttext/bert系列多分类模型)
- 意图使用:
我们第一阶段,使用 参与ES提权。
- 一、参与ES提权
- 二、参与reranker
- 如果有: 先做sku筛选,然后把最优的拼接到名称中,参与reranker。
- 现在在reranker、分页之后、做填充的时候,已经有做sku的筛选。
需要优化:
现在是,先做包含的判断,找到第一个 option_value被query包含的,则直接认为匹配。改为
1. 第一轮:遍历完,如果有且仅有一个被query包含,那么认为匹配。
2. 第二轮:如果有多个符合(被query包含),跳到3。如果没有,对每个词都走泛化词表进行匹配。
3. 第三轮:如果有多个,那么对这多个,走embedding相关性取最高的。如果一个也没有,则对所有的走embedding相关性取最高的
这个sku筛选也需要提取为一个独立的模块。
- 另外:现在是reranker、分页之后做sku筛选,要改为:
1. 有款式意图的时候,才做sku筛选
2. sku筛选的时机,改为在reranker之前,对所有内容做sku筛选,然后
3. 从仅 option1 扩展到多个维度,识别的意图,包含意图的维度名(color)和维度名的泛化词list(color、颜色、colour、olors、、、、),遍历option1_name,option2_name,option3_name,看哪个能匹配上意图的维度名list,哪个匹配上了,则在这个维度筛选。
4. Rerank doc (有款式意图的时候)要带上属性后缀,拼接到title后面。在调用 run_rerank 前,对每条 hit 生成「用于重排的 doc 文本」(标题 + 可选后缀)
5. TODO : 还有一个问题。 目前,sku只返回一个维度(店铺主维度。默认应该是option1,不是所有维度的sku信息都返回的。所以,如果有款式意图,但是主维度是颜色,那么拿不到全的款式sku)
- 筛选SKU: 先只筛选第一个维度,但考虑到用户搜索词可能带了尺码,所以第二、三个维度也要考虑
当前项目功能已经较多,但是有清晰的框架,请务必基于现有框架进行改造,不要进行补丁式的修改,避免代码逻辑分叉。
请一步一步来,先设计意图识别模块,仔细思考需求,意图识别模块需要提供哪些内容,用于返回数据接口的定义,深度思考,定义一个合理的接口后,再给出合理的模块设计。
2.3 向量检索与融合
- 把knn跟文本相关性的融合方式修改为 "rank": {"rrf": {} }需要licence,可以帮我修改源码支持吗?
knn_boost: 2.0
{
"query": { ...全文检索... },
"knn": { ...向量检索... },
"rank": {
"rrf": {}
}
}
- 融合打分(已完成,2026-03)
以下已经完成:
1. fuse_scores_and_resort 已改为乘法融合,并通过 matched_queries 提取:
- base_query
- base_query_trans_*
- fallback_original_query_*
- knn_query
2. 文本相关性大分不再依赖 phrase_query / keywords_query,这两类查询已清理。
3. 当前融合策略:
- text_score = primary(weighted_source, weighted_translation, weighted_fallback) + 0.25 * support
- fused_score = (rerank_score + 0.00001) * (text_score + 0.1) ** 0.35 * (knn_score + 0.6) ** 0.2
4. track_scores 与 include_named_queries_score 已接入,调试字段与评估方法已同步到:
- docs/相关性检索优化说明.md
|
e756b18e
tangwang
重构了文本召回构建器,现在每个 b...
|
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
|
- docs/Usage-Guide.md
未完成的:
(归一化、次序融合?还乘法公式?)
RRF:先把多路召回稳妥融合
linear + minmax:让你能精调 knn 和文本的权重
reranker:对前面召回出来的 top-k 再做“最后一刀”
2.4 文本相关性优化
- 调研:
Princeton WordNet — 英文同义词底库
Shopify Product Taxonomy — 电商品类标准
Querqy — 电商搜索规则框架
gensimpson/elasticsearch-synonyms — ES 同义词规则落地
- tags字段使用的优化:
现在是keyword,在搜索中,不太好使用(目前主要用于suggest)。
可以考虑也拆分多语言,配合analyzer使用(和qanchors一样)
- 是否需要:
当「源语言不在 index_languages」且「某些目标语言的翻译缺失」时,ES 里会额外加一层 用「原始 query 字符串」去撞缺失语种字段
- 检索相关性优化:
原始搜索词和翻译的词,都需要有对应的主干分析
这个主干可以根据词性简单提取名词即可
在搜索时,原始词和主干都成对地出现,原始词和trunk_keywords一起组成一个或查询。
有一种方案是把原始词和主干词拼接起来。但是bm25要调tf系数。
2.5 图片相关性与向量字段调整
- "image_embedding": {
"type": "nested",
"properties": {
"vector": {
"type": "dense_vector",
"dims": 1024,
"index": true,
"similarity": "dot_product",
"element_type": "bfloat16"
},
"url": {
"type": "text"
}
}
},
去掉 image_embedding_512
image_embedding改为,一个spu有多个sku向量,每个向量内部properties:
除了vector url还应该包括,该图片是对应哪些sku
"image_embedding": {
"type": "nested",
"properties": {
"vector": {
"type": "dense_vector",
"dims": 1024,
"index": true,
"similarity": "dot_product",
"element_type": "bfloat16"
},
"url": {
"type": "text"
}
}
},
- 引入图片的相关性:
图片的向量最好做SKU维度,用 SPU 维度还是 SKU 维度?
1. SKU维度(主款式,option1维度),如果用户搜索“蓝色 T恤”,这种图片相关性会比较有价值。
2. 我不考虑颜色的差异,其余的款式一般是大小之类的。这些图片,embedding细分到 SKU 维度,可能价值不大,性价比偏低
- 属性的筛选:
训练一个bert/transformer多分类模型,分类: 颜色、尺寸、材质 等等。但是要注意一些属性的值不规范、非常多,要考虑 是不是做规范化,如何规范化。
2.6 无结果重查与翻译缺失处理
- 无结果重查
稀有语言,翻译可能超时(因为zh-en互译之外的翻译耗时更长)
---
3. 模型与推理服务优化
3.1 大模型API与本地部署
- 外部需求:
1. 对推理能力要求很低、对耗时要求很高的大模型API(或者本地部署一个7b Q4量化的大模型),prompt大概30-50个token,首token响应要求500ms以内
2. ES支持reranker pipline?
- 本地部署一个7b Q4量化的大模型
3.2 Embedding服务优化
- 先阅读文本embedding相关的代码:
@embeddings/README.md @embeddings/server.py @docs/搜索API对接指南-07-微服务接口(Embedding-Reranker-Translation).md @embeddings/text_encoder.py
目前有TEXT_MAX_INFLIGHT / IMAGE_MAX_INFLIGHT 准入限制,超限返回过载状态码。
- 文本embedding服务,要支持 priority 查询参数,priority > 0:不计入上述 inflight、不会因准入被拒绝(图片embedding不需要支持,因为只有离线需要用到图片embedding)
priority == 0(默认,适合做索引之类的离线任务):仍走原有 TEXT_MAX_INFLIGHT / IMAGE_MAX_INFLIGHT 准入;超限返回过载状态码。
priority > 0(或者==1)(适合在线请求):不会因准入被拒绝,但是仍然需要占用inflight,这样保证在线请求不被限制,并且在线请求很多的时候可以拒绝掉离线的请求。
- 除了限制规则的修改,更进一步的,也需要保证这种请求是优先处理的(priority=1的相比=0的更优先被处理)。
关于技术方案,有Worker + 双队列、PriorityMutex等等,除此之外,也请你思考合适的方案。
成熟稳定、不带来复杂度、性能、稳定性方面的副作用,是最重要的。请先了解代码、需求,深度思考解决方案
- 向量的缓存
3.3 Reranker优化
- 多reranker:
改 reranker 服务,一次请求返回多路分
服务启动时 加载多个 backend(或按请求懒加载),/rerank 响应扩展为例如
scores: [...](兼容主后端)+ scores_by_backend: { "bge": [...], "qwen3_vllm": [...] }。
搜索侧解析多路分,再融合或只透传 debug。
优点:搜索侧仍只调一个 URL。缺点:单进程多大模型 显存压力很大;
- 融合层要注意的一点
fuse_scores_and_resort 目前只消费 一条 rerank_scores 序列,并写入 _rerank_score
多 backend 之后需要rerank_scores 都参与融合
- 必要性:
见 qwen3-reranker和bge-m3的严重badcase
不一定是要多reranker的方式,但是一定会需要解决方案。
- reranker 补充:nvidia/llama-nemotron-rerank-1b-v2
https://huggingface.co/nvidia/llama-nemotron-rerank-1b-v2
后端推理也建议使用vLLM
注意搜索相关资料,挖掘我的特斯拉 T4 GPU 的性能,充分挖掘性能
你有充足的自由度进行实验
encoder架构。
比较新。
性能更好。
亚马逊 电商搜索数据集比qwen-reranker-4b更好。
支持vLLM。
- Qwen3-Reranker-4B-GGUF
https://modelscope.cn/models/dengcao/Qwen3-Reranker-4B-GGUF/summary
1. 要确定选择哪种量化方式
2. 确定提示词
- qwen3-embedding、qwen3-reranker (done)
选一个推理引擎,相比于我自己直接调 sentence-transformers,主要是多进程和负载均衡、连续批处理,比较有用
当前结论:embedding 场景优先 TEI;vLLM 更偏向生成式与 rerank 场景。
- rerank 性能优化
3.4 翻译模型优化
- 翻译,增加facebook/nllb-200-distilled-600M
https://blog.csdn.net/qq_42746084/article/details/154947534
https://huggingface.co/facebook/nllb-200-distilled-600M
- 店铺的语言:英语能占到80%,所以专门增加一个en-zh的
https://huggingface.co/Helsinki-NLP/opus-mt-zh-en
https://huggingface.co/Helsinki-NLP/opus-mt-en-zh
- opus-mt-zh-en
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
model_name = "./models/opus-mt-en-zh"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
data = 'test'
encoded = tokenizer([data], return_tensors="pt")
translation = model.generate(**encoded)
result = tokenizer.batch_decode(translation, skip_special_tokens=True)[0]
print(result)
- nllb-200-distilled-600M性能优化
已完成(2026-03)
- CTranslate2 迁移 + float16 转换
- 扩展压测报告:perf_reports/20260318/translation_local_models_ct2/README.md
- T4 聚焦调优报告:perf_reports/20260318/translation_local_models_ct2_focus/README.md
- NLLB T4 商品标题专项报告:perf_reports/20260318/nllb_t4_product_names_ct2/README.md
- 当前结论:
- NLLB 在线默认推荐:ct2_inter_threads=4 + ct2_max_queued_batches=32 + ct2_batch_type=examples + ct2_decoding_length_mode=source(+8,min=32)
- opus-mt-zh-en 维持保守默认更稳
- opus-mt-en-zh 如追求离线吞吐可继续做单独 profile
- 请搜索nllb-200-distilled-600M这类seq2seq、transformer架构的模型,有哪些性能优化方案,提高线上翻译服务的吞吐量、降低耗时,搜索相关的在线推理服务方案,找到高性能的服务化方法
- 查看翻译的缓存情况
3.5 其他模型优化
- cnclip的性能优化
---
4. 性能优化与超时配置
4.1 超时配置
- Query 分析阶段等待翻译/embedding 的硬超时
配置文件位置:config/config.yaml
配置项:query_config.async_wait_timeout_ms: 80
代码生效点:query/query_parser.py 使用该值换算成秒传给 wait(...)
2. Embedding HTTP 调用超时(Text/Image)
不再使用任何环境变量覆盖(之前提到的 EMBEDDING_HTTP_TIMEOUT_SEC 已不采用)
配置文件位置:config/config.yaml
配置项:services.embedding.providers.http.timeout_sec(已在 YAML 里补了示例默认 60)
代码生效点:
embeddings/text_encoder.py:requests.post(..., timeout=self.timeout_sec)
embeddings/image_encoder.py:requests.post(..., timeout=self.timeout_sec)
4.2 生成式服务优化(Partial Mode)
- product_enrich : Partial Mode : done
https://help.aliyun.com/zh/model-studio/partial-mode?spm=a2c4g.11186623.help-menu-2400256.d_0_3_0_7.74a630119Ct6zR
需在messages 数组中将最后一条消息的 role 设置为 assistant,并在其 content 中提供前缀,在此消息中设置参数 "partial": true。messages格式如下:
[
{
"role": "user",
"content": "请补全这个斐波那契函数,勿添加其它内容"
},
{
"role": "assistant",
"content": "def calculate_fibonacci(n):\n if n <= 1:\n return n\n else:\n",
"partial": true
}
]
模型会以前缀内容为起点开始生成。
支持 非思考模式。
---
5. Elasticsearch相关
- es需要licence的两个功能,如果费用低,开通下licence,或者改es源码定制开发下,支持 rank.rrf,reranker
{
"query": { ...全文检索... },
"knn": { ...向量检索... },
"rank": {
"rrf": {}
}
}
---
1. 配置体系重构
Referring to @docs/config-system-review-and-redesign.md , most of the modifications have been completed. Could you conduct a review to check what else needs improvement in the configuration documentation system? Are there any outstanding issues?
一、仍然存在大量通过环境变量获取配置的地方
_SERVICE_KIND = (os.getenv("EMBEDDING_SERVICE_KIND", "all") or "all").strip().lower()
if _SERVICE_KIND not in {"all", "text", "image"}:
raise RuntimeError(
f"Invalid EMBEDDING_SERVICE_KIND={_SERVICE_KIND!r}; expected all, text, or image"
)
_TEXT_ENABLED_BY_ENV = os.getenv("EMBEDDING_ENABLE_TEXT_MODEL", "true").lower() in ("1", "true", "yes")
_IMAGE_ENABLED_BY_ENV = os.getenv("EMBEDDING_ENABLE_IMAGE_MODEL", "true").lower() in ("1", "true", "yes")
open_text_model = _TEXT_ENABLED_BY_ENV and _SERVICE_KIND in {"all", "text"}
open_image_model = _IMAGE_ENABLED_BY_ENV and _SERVICE_KIND in {"all", "image"}
_text_encode_lock = threading.Lock()
_image_encode_lock = threading.Lock()
_TEXT_MICROBATCH_WINDOW_SEC = max(
0.0, float(os.getenv("TEXT_MICROBATCH_WINDOW_MS", "4")) / 1000.0
)
_TEXT_REQUEST_TIMEOUT_SEC = max(
1.0, float(os.getenv("TEXT_REQUEST_TIMEOUT_SEC", "30"))
)
_TEXT_MAX_INFLIGHT = max(1, int(os.getenv("TEXT_MAX_INFLIGHT", "32")))
_IMAGE_MAX_INFLIGHT = max(1, int(os.getenv("IMAGE_MAX_INFLIGHT", "1")))
_OVERLOAD_STATUS_CODE = int(os.getenv("EMBEDDING_OVERLOAD_STATUS_CODE", "503"))
_LOG_PREVIEW_COUNT = max(1, int(os.getenv("EMBEDDING_LOG_PREVIEW_COUNT", "3")))
_LOG_TEXT_PREVIEW_CHARS = max(32, int(os.getenv("EMBEDDING_LOG_TEXT_PREVIEW_CHARS", "120")))
_LOG_IMAGE_PREVIEW_CHARS = max(32, int(os.getenv("EMBEDDING_LOG_IMAGE_PREVIEW_CHARS", "180")))
_VECTOR_PREVIEW_DIMS = max(1, int(os.getenv("EMBEDDING_VECTOR_PREVIEW_DIMS", "6")))
_CACHE_PREFIX = str(REDIS_CONFIG.get("embedding_cache_prefix", "embedding")).strip() or "embedding"
还有这些写死的地址 @embedding/config.py
self.TEI_BASE_URL = str(text_backend.get("base_url") or "http://127.0.0.1:8080")
self.TEI_TIMEOUT_SEC = int(text_backend.get("timeout_sec", 60))
self.USE_CLIP_AS_SERVICE = services.image_backend == "clip_as_service"
self.CLIP_AS_SERVICE_SERVER = str(image_backend.get("server") or "grpc://127.0.0.1:51000")
看起来似乎并没有完全遵循这些原则?
4. 重新设计的设计原则
重新设计应遵循以下规则。
4.1 单一逻辑配置系统
可以有多个文件,但不能有多个职责重叠的加载器。
必须有一个加载器管道,能够生成一个类型化的 AppConfig 对象。
4.2 配置文件负责声明,解析代码负责解释,环境变量负责运行时注入
职责应明确如下:
配置文件
声明非敏感的目标行为和可部署的非敏感设置
解析逻辑
加载、合并、验证、规范化并暴露类型化的配置
绝不发明隐藏的业务行为
环境变量
承载密钥和少量运行时/进程相关的值
不随意地重新定义业务行为
4.3 整个系统采用单一的优先级规则
除非明确豁免,否则每个配置类别都应遵循相同的合并模型。
4.4 业务行为不得有静默的隐式后备
在启动时,如果必需的配置缺失或无效,应快速失败。
不要静默地回退到诸如硬编码语言列表之类的遗留行为。
4.5 有效配置必须可观测
每个服务都应能够展示:
配置版本或哈希值
加载的源文件
环境名称
经过清理的有效配置
5. 推荐的目标设计
5.1 边界模型
使用三个清晰的层级。
层级 1:代码仓库管理的静态配置
目的:
搜索行为
租户行为
提供商/后端注册表
非敏感的服务拓扑默认值
功能开关
示例:
字段权重
查询策略
重排序融合参数
租户语言方案
翻译能力注册表
嵌入后端选择默认值
层级 2:特定环境的层叠配置
目的:
按环境区分的非敏感差异
按环境区分的服务端点
按环境区分的资源大小默认值
开发/测试/生产环境的运维差异
示例:
本地嵌入 URL 与生产环境嵌入 URL
开发环境重排序后端与生产环境重排序后端
本地开发环境中较低的并发度
层级 3:环境变量
目的:
密钥
绑定主机/端口
外部基础设施凭证
容器编排器的最后一步注入
示例:
ES_HOST, ES_USERNAME, ES_PASSWORD
DB_HOST, DB_USERNAME, DB_PASSWORD
REDIS_HOST, REDIS_PASSWORD
DASHSCOPE_API_KEY, DEEPL_AUTH_KEY
API_HOST, API_PORT, INDEXER_PORT, TRANSLATION_PORT
规则:
环境变量不应成为选择业务行为(如翻译模型、嵌入后端或租户语言策略)的常规途径
如果允许对非敏感字段进行环境变量覆盖,则必须将其明确列出并记录为运维覆盖手段,而非隐藏的约定
5.2 统一的优先级
推荐的优先级顺序:
代码中的模式(schema)默认值
config/base.yaml
config/environments/<env>.yaml
来自 config/tenants/ 的租户层叠配置
针对明确允许的运行时键的环境变量
仅适用于当前进程的命令行标志
重要规则:
只能有一个模块实现此合并逻辑
任何业务模块不得直接调用 os.getenv() 来获取配置
二、cursor(composer2)帮我审查的
## 配置文档系统审查
这是对配置文档系统的简要审查,对照 [`docs/config-system-review-and-redesign.md`](docs/config-system-review-and-redesign.md) 和当前代码库进行。
### 实现已匹配的部分
代码库已朝着重新设计的目标迈进了很大一步:
- 单一类型化根节点:`AppConfig` 位于 [`config/schema.py`](config/schema.py),通过 [`config/loader.py`](config/loader.py) 和 [`get_app_config()`](config/loader.py) 加载。
- 精简的 `services_config`:[`config/services_config.py`](config/services_config.py) 委托给 `get_app_config()`(解决了服务层的发现 A)。
- 租户目录:[`config/tenant_config_loader.py`](config/tenant_config_loader.py) 使用 `get_app_config().tenants`。
- 重写资产路径:`config/dictionaries/query_rewrite.dict` 已存在(解决了发现 E 中的文件名不匹配问题)。
- 可观测性:[`GET /admin/config`](api/routes/admin.py) 返回经过脱敏处理的有效配置树;[`GET /admin/config/meta`](api/routes/admin.py) 暴露环境信息、`config_hash`、`loaded_files`、`deprecated_keys`(涵盖了 §5.10 的意图;文档中提议的是 `/admin/config/effective`,但实际实现位于 `/admin/config`)。
因此,"单一加载器 + 有效配置可见性" 的故事在代码中已基本实现;文档尚未完全跟上。
---
## 文档问题(影响最大)
### 1. 管理 API 文档中关于 `/admin/config` 的描述错误
|