Commit 9d0214bbab2cde5b64ed5011ffe10fc825aeae5c

Authored by tangwang
1 parent 45b39796

qp性能优化

context/request_context.py
... ... @@ -41,6 +41,7 @@ class QueryAnalysisResult:
41 41 rewritten_query: Optional[str] = None
42 42 detected_language: Optional[str] = None
43 43 translations: Dict[str, str] = field(default_factory=dict)
  44 + keywords_queries: Dict[str, str] = field(default_factory=dict)
44 45 query_vector: Optional[List[float]] = None
45 46 boolean_ast: Optional[str] = None
46 47  
... ...
frontend/static/js/app.js
... ... @@ -1062,6 +1062,7 @@ function buildGlobalFunnelHtml(data, debugInfo) {
1062 1062 const fineInfo = rankingFunnel.fine_rank || debugInfo.fine_rank || {};
1063 1063 const rerankInfo = rankingFunnel.rerank || debugInfo.rerank || {};
1064 1064 const translations = queryAnalysis.translations || {};
  1065 + const keywordsQueries = queryAnalysis.keywords_queries || {};
1065 1066  
1066 1067 const summaryHtml = `
1067 1068 <div class="debug-section-block">
... ... @@ -1072,11 +1073,13 @@ function buildGlobalFunnelHtml(data, debugInfo) {
1072 1073 { label: 'detected_language', value: queryAnalysis.detected_language || 'N/A' },
1073 1074 { label: 'index_languages', value: (queryAnalysis.index_languages || []).join(', ') || 'N/A' },
1074 1075 { label: 'query_tokens', value: (queryAnalysis.query_tokens || []).join(', ') || 'N/A' },
  1076 + { label: 'base_keywords', value: keywordsQueries.base || 'N/A' },
1075 1077 { label: 'translation_enabled', value: featureFlags.translation_enabled ? 'enabled' : 'disabled' },
1076 1078 { label: 'embedding_enabled', value: featureFlags.embedding_enabled ? 'enabled' : 'disabled' },
1077 1079 { label: 'style_intent_active', value: featureFlags.style_intent_active ? 'yes' : 'no' },
1078 1080 ])}
1079 1081 ${Object.keys(translations).length ? renderJsonDetails('Translations', translations, true) : ''}
  1082 + ${Object.keys(keywordsQueries).length ? renderJsonDetails('Keywords Queries', keywordsQueries, true) : ''}
1080 1083 ${formatIntentDetectionHtml(queryAnalysis.intent_detection ?? queryAnalysis.style_intent_profile)}
1081 1084 </div>
1082 1085 `;
... ...
query/query_parser.py
... ... @@ -359,16 +359,15 @@ class QueryParser:
359 359 else:
360 360 active_logger.debug(msg)
361 361  
  362 + before_wait_t0 = time.perf_counter()
  363 +
362 364 # Stage 1: Normalize
363   - normalize_t0 = time.perf_counter()
364 365 normalized = self.normalizer.normalize(query)
365   - normalize_ms = (time.perf_counter() - normalize_t0) * 1000.0
366 366 log_debug(f"Normalization completed | '{query}' -> '{normalized}'")
367 367 if context:
368 368 context.store_intermediate_result('query_normalized', normalized)
369 369  
370 370 # Stage 2: Query rewriting
371   - rewrite_t0 = time.perf_counter()
372 371 query_text = normalized
373 372 rewritten = normalized
374 373 if self.config.query_config.rewrite_dictionary: # Enable rewrite if dictionary exists
... ... @@ -379,12 +378,10 @@ class QueryParser:
379 378 if context:
380 379 context.store_intermediate_result('rewritten_query', rewritten)
381 380 context.add_warning(f"Query was rewritten: {query_text}")
382   - rewrite_ms = (time.perf_counter() - rewrite_t0) * 1000.0
383 381  
384 382 normalized_targets = self._normalize_language_codes(target_languages)
385 383  
386 384 # Stage 3: Language detection
387   - language_detect_t0 = time.perf_counter()
388 385 detected_lang = self._detect_query_language(
389 386 query_text,
390 387 target_languages=normalized_targets,
... ... @@ -392,7 +389,6 @@ class QueryParser:
392 389 # Use default language if detection failed (None or "unknown")
393 390 if not detected_lang or detected_lang == "unknown":
394 391 detected_lang = self.config.query_config.default_language
395   - language_detect_ms = (time.perf_counter() - language_detect_t0) * 1000.0
396 392 log_info(f"Language detection | Detected language: {detected_lang}")
397 393 if context:
398 394 context.store_intermediate_result('detected_language', detected_lang)
... ... @@ -433,9 +429,7 @@ class QueryParser:
433 429 thread_name_prefix="query-enrichment",
434 430 )
435 431  
436   - async_submit_ms = 0.0
437 432 try:
438   - async_submit_t0 = time.perf_counter()
439 433 if async_executor is not None:
440 434 for lang in translation_targets:
441 435 model_name = self._pick_query_translation_model(
... ... @@ -503,7 +497,6 @@ class QueryParser:
503 497 future = async_executor.submit(_encode_image_query_vector)
504 498 future_to_task[future] = ("image_embedding", None)
505 499 future_submit_at[future] = time.perf_counter()
506   - async_submit_ms = (time.perf_counter() - async_submit_t0) * 1000.0
507 500 except Exception as e:
508 501 error_msg = f"Async query enrichment submission failed | Error: {str(e)}"
509 502 log_info(error_msg)
... ... @@ -516,14 +509,8 @@ class QueryParser:
516 509 future_submit_at.clear()
517 510  
518 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 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 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 515 log_debug(f"Query analysis | Query tokens: {query_tokens}")
529 516 if context:
... ... @@ -541,6 +528,7 @@ class QueryParser:
541 528 keywords_base_ms = (time.perf_counter() - keywords_base_t0) * 1000.0
542 529 except Exception as e:
543 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 533 # Wait for translation + embedding concurrently; shared budget depends on whether
546 534 # the detected language belongs to caller-provided target_languages.
... ... @@ -569,7 +557,6 @@ class QueryParser:
569 557 async_wait_t0 = time.perf_counter()
570 558 done, not_done = wait(list(future_to_task.keys()), timeout=budget_sec)
571 559 async_wait_ms = (time.perf_counter() - async_wait_t0) * 1000.0
572   - async_collect_t0 = time.perf_counter()
573 560 for future in done:
574 561 task_type, lang = future_to_task[future]
575 562 t0 = future_submit_at.pop(future, None)
... ... @@ -630,7 +617,6 @@ class QueryParser:
630 617 log_info(timeout_msg)
631 618 if context:
632 619 context.add_warning(timeout_msg)
633   - async_collect_ms = (time.perf_counter() - async_collect_t0) * 1000.0
634 620  
635 621 if async_executor:
636 622 async_executor.shutdown(wait=False)
... ... @@ -639,7 +625,6 @@ class QueryParser:
639 625 context.store_intermediate_result("translations", translations)
640 626 else:
641 627 async_wait_ms = 0.0
642   - async_collect_ms = 0.0
643 628  
644 629 tail_sync_t0 = time.perf_counter()
645 630 keywords_queries: Dict[str, str] = {}
... ... @@ -655,6 +640,9 @@ class QueryParser:
655 640 base_keywords_query=keywords_base_query,
656 641 )
657 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 646 except Exception as e:
659 647 log_info(f"Keyword extraction failed | Error: {e}")
660 648  
... ... @@ -671,39 +659,15 @@ class QueryParser:
671 659 keywords_queries=keywords_queries,
672 660 _text_analysis_cache=text_analysis_cache,
673 661 )
674   - style_intent_t0 = time.perf_counter()
675 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 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 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 665 log_info(
692 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 667 f"before_wait_ms={before_wait_ms:.1f} | "
701 668 f"async_wait_ms={async_wait_ms:.1f} | "
702   - f"async_collect_ms={async_collect_ms:.1f} | "
703 669 f"base_keywords_ms={keywords_base_ms:.1f} | "
704 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 671 f"tail_sync_ms={tail_sync_ms:.1f}"
708 672 )
709 673 if context:
... ...
query/tokenization.py
... ... @@ -89,11 +89,18 @@ def _build_phrase_candidates(tokens: Sequence[str], max_ngram: int) -&gt; List[str]
89 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 106 @dataclass(frozen=True)
... ... @@ -159,7 +166,11 @@ class QueryTextAnalysisCache:
159 166 normalized_text = normalize_query_text(normalized_input)
160 167 fine_raw = extract_token_strings(self.get_tokenizer_result(normalized_input))
161 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 175 bundle = TokenizedText(
165 176 text=normalized_input,
... ...
search/searcher.py
... ... @@ -446,6 +446,7 @@ class Searcher:
446 446 rewritten_query=parsed_query.rewritten_query,
447 447 detected_language=parsed_query.detected_language,
448 448 translations=parsed_query.translations,
  449 + keywords_queries=parsed_query.keywords_queries,
449 450 query_vector=parsed_query.query_vector.tolist() if parsed_query.query_vector is not None else None,
450 451 )
451 452 context.metadata["feature_flags"]["style_intent_active"] = self._has_style_intent(parsed_query)
... ... @@ -454,6 +455,7 @@ class Searcher:
454 455 f"查询解析完成 | 原查询: '{parsed_query.original_query}' | "
455 456 f"重写后: '{parsed_query.rewritten_query}' | "
456 457 f"语言: {parsed_query.detected_language} | "
  458 + f"关键词: {parsed_query.keywords_queries} | "
457 459 f"文本向量: {'是' if parsed_query.query_vector is not None else '否'} | "
458 460 f"图片向量: {'是' if getattr(parsed_query, 'image_query_vector', None) is not None else '否'}",
459 461 extra={'reqid': context.reqid, 'uid': context.uid}
... ... @@ -1172,6 +1174,7 @@ class Searcher:
1172 1174 "detected_language": context.query_analysis.detected_language,
1173 1175 "index_languages": index_langs,
1174 1176 "translations": context.query_analysis.translations,
  1177 + "keywords_queries": context.query_analysis.keywords_queries,
1175 1178 "has_vector": context.query_analysis.query_vector is not None,
1176 1179 "has_image_vector": getattr(parsed_query, "image_query_vector", None) is not None,
1177 1180 "query_tokens": getattr(parsed_query, "query_tokens", []),
... ...