Commit 9d0214bbab2cde5b64ed5011ffe10fc825aeae5c

Authored by tangwang
1 parent 45b39796

qp性能优化

context/request_context.py
@@ -41,6 +41,7 @@ class QueryAnalysisResult: @@ -41,6 +41,7 @@ class QueryAnalysisResult:
41 rewritten_query: Optional[str] = None 41 rewritten_query: Optional[str] = None
42 detected_language: Optional[str] = None 42 detected_language: Optional[str] = None
43 translations: Dict[str, str] = field(default_factory=dict) 43 translations: Dict[str, str] = field(default_factory=dict)
  44 + keywords_queries: Dict[str, str] = field(default_factory=dict)
44 query_vector: Optional[List[float]] = None 45 query_vector: Optional[List[float]] = None
45 boolean_ast: Optional[str] = None 46 boolean_ast: Optional[str] = None
46 47
frontend/static/js/app.js
@@ -1062,6 +1062,7 @@ function buildGlobalFunnelHtml(data, debugInfo) { @@ -1062,6 +1062,7 @@ function buildGlobalFunnelHtml(data, debugInfo) {
1062 const fineInfo = rankingFunnel.fine_rank || debugInfo.fine_rank || {}; 1062 const fineInfo = rankingFunnel.fine_rank || debugInfo.fine_rank || {};
1063 const rerankInfo = rankingFunnel.rerank || debugInfo.rerank || {}; 1063 const rerankInfo = rankingFunnel.rerank || debugInfo.rerank || {};
1064 const translations = queryAnalysis.translations || {}; 1064 const translations = queryAnalysis.translations || {};
  1065 + const keywordsQueries = queryAnalysis.keywords_queries || {};
1065 1066
1066 const summaryHtml = ` 1067 const summaryHtml = `
1067 <div class="debug-section-block"> 1068 <div class="debug-section-block">
@@ -1072,11 +1073,13 @@ function buildGlobalFunnelHtml(data, debugInfo) { @@ -1072,11 +1073,13 @@ function buildGlobalFunnelHtml(data, debugInfo) {
1072 { label: 'detected_language', value: queryAnalysis.detected_language || 'N/A' }, 1073 { label: 'detected_language', value: queryAnalysis.detected_language || 'N/A' },
1073 { label: 'index_languages', value: (queryAnalysis.index_languages || []).join(', ') || 'N/A' }, 1074 { label: 'index_languages', value: (queryAnalysis.index_languages || []).join(', ') || 'N/A' },
1074 { label: 'query_tokens', value: (queryAnalysis.query_tokens || []).join(', ') || 'N/A' }, 1075 { label: 'query_tokens', value: (queryAnalysis.query_tokens || []).join(', ') || 'N/A' },
  1076 + { label: 'base_keywords', value: keywordsQueries.base || 'N/A' },
1075 { label: 'translation_enabled', value: featureFlags.translation_enabled ? 'enabled' : 'disabled' }, 1077 { label: 'translation_enabled', value: featureFlags.translation_enabled ? 'enabled' : 'disabled' },
1076 { label: 'embedding_enabled', value: featureFlags.embedding_enabled ? 'enabled' : 'disabled' }, 1078 { label: 'embedding_enabled', value: featureFlags.embedding_enabled ? 'enabled' : 'disabled' },
1077 { label: 'style_intent_active', value: featureFlags.style_intent_active ? 'yes' : 'no' }, 1079 { label: 'style_intent_active', value: featureFlags.style_intent_active ? 'yes' : 'no' },
1078 ])} 1080 ])}
1079 ${Object.keys(translations).length ? renderJsonDetails('Translations', translations, true) : ''} 1081 ${Object.keys(translations).length ? renderJsonDetails('Translations', translations, true) : ''}
  1082 + ${Object.keys(keywordsQueries).length ? renderJsonDetails('Keywords Queries', keywordsQueries, true) : ''}
1080 ${formatIntentDetectionHtml(queryAnalysis.intent_detection ?? queryAnalysis.style_intent_profile)} 1083 ${formatIntentDetectionHtml(queryAnalysis.intent_detection ?? queryAnalysis.style_intent_profile)}
1081 </div> 1084 </div>
1082 `; 1085 `;
query/query_parser.py
@@ -359,16 +359,15 @@ class QueryParser: @@ -359,16 +359,15 @@ class QueryParser:
359 else: 359 else:
360 active_logger.debug(msg) 360 active_logger.debug(msg)
361 361
  362 + before_wait_t0 = time.perf_counter()
  363 +
362 # Stage 1: Normalize 364 # Stage 1: Normalize
363 - normalize_t0 = time.perf_counter()  
364 normalized = self.normalizer.normalize(query) 365 normalized = self.normalizer.normalize(query)
365 - normalize_ms = (time.perf_counter() - normalize_t0) * 1000.0  
366 log_debug(f"Normalization completed | '{query}' -> '{normalized}'") 366 log_debug(f"Normalization completed | '{query}' -> '{normalized}'")
367 if context: 367 if context:
368 context.store_intermediate_result('query_normalized', normalized) 368 context.store_intermediate_result('query_normalized', normalized)
369 369
370 # Stage 2: Query rewriting 370 # Stage 2: Query rewriting
371 - rewrite_t0 = time.perf_counter()  
372 query_text = normalized 371 query_text = normalized
373 rewritten = normalized 372 rewritten = normalized
374 if self.config.query_config.rewrite_dictionary: # Enable rewrite if dictionary exists 373 if self.config.query_config.rewrite_dictionary: # Enable rewrite if dictionary exists
@@ -379,12 +378,10 @@ class QueryParser: @@ -379,12 +378,10 @@ class QueryParser:
379 if context: 378 if context:
380 context.store_intermediate_result('rewritten_query', rewritten) 379 context.store_intermediate_result('rewritten_query', rewritten)
381 context.add_warning(f"Query was rewritten: {query_text}") 380 context.add_warning(f"Query was rewritten: {query_text}")
382 - rewrite_ms = (time.perf_counter() - rewrite_t0) * 1000.0  
383 381
384 normalized_targets = self._normalize_language_codes(target_languages) 382 normalized_targets = self._normalize_language_codes(target_languages)
385 383
386 # Stage 3: Language detection 384 # Stage 3: Language detection
387 - language_detect_t0 = time.perf_counter()  
388 detected_lang = self._detect_query_language( 385 detected_lang = self._detect_query_language(
389 query_text, 386 query_text,
390 target_languages=normalized_targets, 387 target_languages=normalized_targets,
@@ -392,7 +389,6 @@ class QueryParser: @@ -392,7 +389,6 @@ class QueryParser:
392 # Use default language if detection failed (None or "unknown") 389 # Use default language if detection failed (None or "unknown")
393 if not detected_lang or detected_lang == "unknown": 390 if not detected_lang or detected_lang == "unknown":
394 detected_lang = self.config.query_config.default_language 391 detected_lang = self.config.query_config.default_language
395 - language_detect_ms = (time.perf_counter() - language_detect_t0) * 1000.0  
396 log_info(f"Language detection | Detected language: {detected_lang}") 392 log_info(f"Language detection | Detected language: {detected_lang}")
397 if context: 393 if context:
398 context.store_intermediate_result('detected_language', detected_lang) 394 context.store_intermediate_result('detected_language', detected_lang)
@@ -433,9 +429,7 @@ class QueryParser: @@ -433,9 +429,7 @@ class QueryParser:
433 thread_name_prefix="query-enrichment", 429 thread_name_prefix="query-enrichment",
434 ) 430 )
435 431
436 - async_submit_ms = 0.0  
437 try: 432 try:
438 - async_submit_t0 = time.perf_counter()  
439 if async_executor is not None: 433 if async_executor is not None:
440 for lang in translation_targets: 434 for lang in translation_targets:
441 model_name = self._pick_query_translation_model( 435 model_name = self._pick_query_translation_model(
@@ -503,7 +497,6 @@ class QueryParser: @@ -503,7 +497,6 @@ class QueryParser:
503 future = async_executor.submit(_encode_image_query_vector) 497 future = async_executor.submit(_encode_image_query_vector)
504 future_to_task[future] = ("image_embedding", None) 498 future_to_task[future] = ("image_embedding", None)
505 future_submit_at[future] = time.perf_counter() 499 future_submit_at[future] = time.perf_counter()
506 - async_submit_ms = (time.perf_counter() - async_submit_t0) * 1000.0  
507 except Exception as e: 500 except Exception as e:
508 error_msg = f"Async query enrichment submission failed | Error: {str(e)}" 501 error_msg = f"Async query enrichment submission failed | Error: {str(e)}"
509 log_info(error_msg) 502 log_info(error_msg)
@@ -516,14 +509,8 @@ class QueryParser: @@ -516,14 +509,8 @@ class QueryParser:
516 future_submit_at.clear() 509 future_submit_at.clear()
517 510
518 # Stage 4: Query analysis (tokenization) now overlaps with async enrichment work. 511 # Stage 4: Query analysis (tokenization) now overlaps with async enrichment work.
519 - query_analysis_t0 = time.perf_counter()  
520 - query_tokenizer_t0 = time.perf_counter()  
521 query_tokenizer_result = text_analysis_cache.get_tokenizer_result(query_text) 512 query_tokenizer_result = text_analysis_cache.get_tokenizer_result(query_text)
522 - query_tokenizer_ms = (time.perf_counter() - query_tokenizer_t0) * 1000.0  
523 - query_token_extract_t0 = time.perf_counter()  
524 query_tokens = self._extract_tokens(query_tokenizer_result) 513 query_tokens = self._extract_tokens(query_tokenizer_result)
525 - query_token_extract_ms = (time.perf_counter() - query_token_extract_t0) * 1000.0  
526 - query_analysis_ms = (time.perf_counter() - query_analysis_t0) * 1000.0  
527 514
528 log_debug(f"Query analysis | Query tokens: {query_tokens}") 515 log_debug(f"Query analysis | Query tokens: {query_tokens}")
529 if context: 516 if context:
@@ -541,6 +528,7 @@ class QueryParser: @@ -541,6 +528,7 @@ class QueryParser:
541 keywords_base_ms = (time.perf_counter() - keywords_base_t0) * 1000.0 528 keywords_base_ms = (time.perf_counter() - keywords_base_t0) * 1000.0
542 except Exception as e: 529 except Exception as e:
543 log_info(f"Base keyword extraction failed | Error: {e}") 530 log_info(f"Base keyword extraction failed | Error: {e}")
  531 + before_wait_ms = (time.perf_counter() - before_wait_t0) * 1000.0
544 532
545 # Wait for translation + embedding concurrently; shared budget depends on whether 533 # Wait for translation + embedding concurrently; shared budget depends on whether
546 # the detected language belongs to caller-provided target_languages. 534 # the detected language belongs to caller-provided target_languages.
@@ -569,7 +557,6 @@ class QueryParser: @@ -569,7 +557,6 @@ class QueryParser:
569 async_wait_t0 = time.perf_counter() 557 async_wait_t0 = time.perf_counter()
570 done, not_done = wait(list(future_to_task.keys()), timeout=budget_sec) 558 done, not_done = wait(list(future_to_task.keys()), timeout=budget_sec)
571 async_wait_ms = (time.perf_counter() - async_wait_t0) * 1000.0 559 async_wait_ms = (time.perf_counter() - async_wait_t0) * 1000.0
572 - async_collect_t0 = time.perf_counter()  
573 for future in done: 560 for future in done:
574 task_type, lang = future_to_task[future] 561 task_type, lang = future_to_task[future]
575 t0 = future_submit_at.pop(future, None) 562 t0 = future_submit_at.pop(future, None)
@@ -630,7 +617,6 @@ class QueryParser: @@ -630,7 +617,6 @@ class QueryParser:
630 log_info(timeout_msg) 617 log_info(timeout_msg)
631 if context: 618 if context:
632 context.add_warning(timeout_msg) 619 context.add_warning(timeout_msg)
633 - async_collect_ms = (time.perf_counter() - async_collect_t0) * 1000.0  
634 620
635 if async_executor: 621 if async_executor:
636 async_executor.shutdown(wait=False) 622 async_executor.shutdown(wait=False)
@@ -639,7 +625,6 @@ class QueryParser: @@ -639,7 +625,6 @@ class QueryParser:
639 context.store_intermediate_result("translations", translations) 625 context.store_intermediate_result("translations", translations)
640 else: 626 else:
641 async_wait_ms = 0.0 627 async_wait_ms = 0.0
642 - async_collect_ms = 0.0  
643 628
644 tail_sync_t0 = time.perf_counter() 629 tail_sync_t0 = time.perf_counter()
645 keywords_queries: Dict[str, str] = {} 630 keywords_queries: Dict[str, str] = {}
@@ -655,6 +640,9 @@ class QueryParser: @@ -655,6 +640,9 @@ class QueryParser:
655 base_keywords_query=keywords_base_query, 640 base_keywords_query=keywords_base_query,
656 ) 641 )
657 keyword_tail_ms = (time.perf_counter() - keywords_t0) * 1000.0 642 keyword_tail_ms = (time.perf_counter() - keywords_t0) * 1000.0
  643 + if context:
  644 + context.store_intermediate_result("keywords_queries", keywords_queries)
  645 + log_info(f"Keyword extraction completed | keywords_queries={keywords_queries}")
658 except Exception as e: 646 except Exception as e:
659 log_info(f"Keyword extraction failed | Error: {e}") 647 log_info(f"Keyword extraction failed | Error: {e}")
660 648
@@ -671,39 +659,15 @@ class QueryParser: @@ -671,39 +659,15 @@ class QueryParser:
671 keywords_queries=keywords_queries, 659 keywords_queries=keywords_queries,
672 _text_analysis_cache=text_analysis_cache, 660 _text_analysis_cache=text_analysis_cache,
673 ) 661 )
674 - style_intent_t0 = time.perf_counter()  
675 style_intent_profile = self.style_intent_detector.detect(base_result) 662 style_intent_profile = self.style_intent_detector.detect(base_result)
676 - style_intent_ms = (time.perf_counter() - style_intent_t0) * 1000.0  
677 - product_title_exclusion_t0 = time.perf_counter()  
678 product_title_exclusion_profile = self.product_title_exclusion_detector.detect(base_result) 663 product_title_exclusion_profile = self.product_title_exclusion_detector.detect(base_result)
679 - product_title_exclusion_ms = (  
680 - (time.perf_counter() - product_title_exclusion_t0) * 1000.0  
681 - )  
682 tail_sync_ms = (time.perf_counter() - tail_sync_t0) * 1000.0 664 tail_sync_ms = (time.perf_counter() - tail_sync_t0) * 1000.0
683 - before_wait_ms = (  
684 - normalize_ms  
685 - + rewrite_ms  
686 - + language_detect_ms  
687 - + async_submit_ms  
688 - + query_analysis_ms  
689 - + keywords_base_ms  
690 - )  
691 log_info( 665 log_info(
692 "Query parse stage timings | " 666 "Query parse stage timings | "
693 - f"normalize_ms={normalize_ms:.1f} | "  
694 - f"rewrite_ms={rewrite_ms:.1f} | "  
695 - f"language_detect_ms={language_detect_ms:.1f} | "  
696 - f"query_tokenizer_ms={query_tokenizer_ms:.1f} | "  
697 - f"query_token_extract_ms={query_token_extract_ms:.1f} | "  
698 - f"query_analysis_ms={query_analysis_ms:.1f} | "  
699 - f"async_submit_ms={async_submit_ms:.1f} | "  
700 f"before_wait_ms={before_wait_ms:.1f} | " 667 f"before_wait_ms={before_wait_ms:.1f} | "
701 f"async_wait_ms={async_wait_ms:.1f} | " 668 f"async_wait_ms={async_wait_ms:.1f} | "
702 - f"async_collect_ms={async_collect_ms:.1f} | "  
703 f"base_keywords_ms={keywords_base_ms:.1f} | " 669 f"base_keywords_ms={keywords_base_ms:.1f} | "
704 f"keyword_tail_ms={keyword_tail_ms:.1f} | " 670 f"keyword_tail_ms={keyword_tail_ms:.1f} | "
705 - f"style_intent_ms={style_intent_ms:.1f} | "  
706 - f"product_title_exclusion_ms={product_title_exclusion_ms:.1f} | "  
707 f"tail_sync_ms={tail_sync_ms:.1f}" 671 f"tail_sync_ms={tail_sync_ms:.1f}"
708 ) 672 )
709 if context: 673 if context:
query/tokenization.py
@@ -89,11 +89,18 @@ def _build_phrase_candidates(tokens: Sequence[str], max_ngram: int) -&gt; List[str] @@ -89,11 +89,18 @@ def _build_phrase_candidates(tokens: Sequence[str], max_ngram: int) -&gt; List[str]
89 return phrases 89 return phrases
90 90
91 91
92 -def _build_coarse_tokens(text: str, fine_tokens: Sequence[str]) -> List[str]:  
93 - coarse_tokens = _dedupe_preserve_order(simple_tokenize_query(text))  
94 - if contains_han_text(text) and fine_tokens:  
95 - return list(_dedupe_preserve_order(fine_tokens))  
96 - return coarse_tokens 92 +def _build_coarse_tokens(
  93 + text: str,
  94 + *,
  95 + language_hint: Optional[str],
  96 + tokenizer_tokens: Sequence[str],
  97 +) -> List[str]:
  98 + normalized_language = normalize_query_text(language_hint)
  99 + if normalized_language == "zh" or (contains_han_text(text) and tokenizer_tokens):
  100 + # Chinese coarse tokenization should follow the model tokenizer rather than a
  101 + # regex that collapses the whole sentence into one CJK span.
  102 + return list(_dedupe_preserve_order(tokenizer_tokens))
  103 + return _dedupe_preserve_order(simple_tokenize_query(text))
97 104
98 105
99 @dataclass(frozen=True) 106 @dataclass(frozen=True)
@@ -159,7 +166,11 @@ class QueryTextAnalysisCache: @@ -159,7 +166,11 @@ class QueryTextAnalysisCache:
159 normalized_text = normalize_query_text(normalized_input) 166 normalized_text = normalize_query_text(normalized_input)
160 fine_raw = extract_token_strings(self.get_tokenizer_result(normalized_input)) 167 fine_raw = extract_token_strings(self.get_tokenizer_result(normalized_input))
161 fine_tokens = _dedupe_preserve_order(fine_raw) 168 fine_tokens = _dedupe_preserve_order(fine_raw)
162 - coarse_tokens = _build_coarse_tokens(normalized_input, fine_tokens) 169 + coarse_tokens = _build_coarse_tokens(
  170 + normalized_input,
  171 + language_hint=self.get_language_hint(normalized_input),
  172 + tokenizer_tokens=fine_tokens,
  173 + )
163 174
164 bundle = TokenizedText( 175 bundle = TokenizedText(
165 text=normalized_input, 176 text=normalized_input,
search/searcher.py
@@ -446,6 +446,7 @@ class Searcher: @@ -446,6 +446,7 @@ class Searcher:
446 rewritten_query=parsed_query.rewritten_query, 446 rewritten_query=parsed_query.rewritten_query,
447 detected_language=parsed_query.detected_language, 447 detected_language=parsed_query.detected_language,
448 translations=parsed_query.translations, 448 translations=parsed_query.translations,
  449 + keywords_queries=parsed_query.keywords_queries,
449 query_vector=parsed_query.query_vector.tolist() if parsed_query.query_vector is not None else None, 450 query_vector=parsed_query.query_vector.tolist() if parsed_query.query_vector is not None else None,
450 ) 451 )
451 context.metadata["feature_flags"]["style_intent_active"] = self._has_style_intent(parsed_query) 452 context.metadata["feature_flags"]["style_intent_active"] = self._has_style_intent(parsed_query)
@@ -454,6 +455,7 @@ class Searcher: @@ -454,6 +455,7 @@ class Searcher:
454 f"查询解析完成 | 原查询: '{parsed_query.original_query}' | " 455 f"查询解析完成 | 原查询: '{parsed_query.original_query}' | "
455 f"重写后: '{parsed_query.rewritten_query}' | " 456 f"重写后: '{parsed_query.rewritten_query}' | "
456 f"语言: {parsed_query.detected_language} | " 457 f"语言: {parsed_query.detected_language} | "
  458 + f"关键词: {parsed_query.keywords_queries} | "
457 f"文本向量: {'是' if parsed_query.query_vector is not None else '否'} | " 459 f"文本向量: {'是' if parsed_query.query_vector is not None else '否'} | "
458 f"图片向量: {'是' if getattr(parsed_query, 'image_query_vector', None) is not None else '否'}", 460 f"图片向量: {'是' if getattr(parsed_query, 'image_query_vector', None) is not None else '否'}",
459 extra={'reqid': context.reqid, 'uid': context.uid} 461 extra={'reqid': context.reqid, 'uid': context.uid}
@@ -1172,6 +1174,7 @@ class Searcher: @@ -1172,6 +1174,7 @@ class Searcher:
1172 "detected_language": context.query_analysis.detected_language, 1174 "detected_language": context.query_analysis.detected_language,
1173 "index_languages": index_langs, 1175 "index_languages": index_langs,
1174 "translations": context.query_analysis.translations, 1176 "translations": context.query_analysis.translations,
  1177 + "keywords_queries": context.query_analysis.keywords_queries,
1175 "has_vector": context.query_analysis.query_vector is not None, 1178 "has_vector": context.query_analysis.query_vector is not None,
1176 "has_image_vector": getattr(parsed_query, "image_query_vector", None) is not None, 1179 "has_image_vector": getattr(parsed_query, "image_query_vector", None) is not None,
1177 "query_tokens": getattr(parsed_query, "query_tokens", []), 1180 "query_tokens": getattr(parsed_query, "query_tokens", []),