diff --git a/frontend/static/css/style.css b/frontend/static/css/style.css index ac81869..4e26c5e 100644 --- a/frontend/static/css/style.css +++ b/frontend/static/css/style.css @@ -357,16 +357,22 @@ body { color: #555; border-left: 1px dashed #eee; padding-left: 12px; - max-height: 260px; + max-height: 540px; overflow: auto; } .product-debug-title { font-weight: 600; - margin-bottom: 6px; + margin-bottom: 8px; color: #333; } +.product-debug-subtitle { + margin: 10px 0 6px; + font-weight: 600; + color: #666; +} + .product-debug-line { margin-bottom: 2px; } @@ -418,6 +424,191 @@ body { word-break: break-word; } +.debug-panel { + display: flex; + flex-direction: column; + gap: 14px; + padding: 12px; + font-family: Menlo, Consolas, "Courier New", monospace; + font-size: 12px; +} + +.debug-section-block { + background: #fff; + border: 1px solid #e8e8e8; + border-radius: 10px; + padding: 14px; +} + +.debug-section-title { + font-size: 13px; + font-weight: 700; + color: #222; + margin-bottom: 10px; +} + +.debug-stage-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + gap: 12px; +} + +.debug-stage-card { + border: 1px solid #ececec; + border-radius: 8px; + padding: 12px; + background: linear-gradient(180deg, #fff 0%, #fafafa 100%); +} + +.debug-stage-title { + font-size: 13px; + font-weight: 700; + color: #333; +} + +.debug-stage-subtitle { + margin: 4px 0 8px; + color: #888; + font-size: 11px; +} + +.debug-metrics { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 8px; +} + +.debug-metric { + padding: 8px 9px; + background: #f7f7f7; + border-radius: 6px; + border: 1px solid #efefef; +} + +.debug-metric-label { + font-size: 11px; + color: #777; + margin-bottom: 2px; +} + +.debug-metric-value { + color: #222; + font-weight: 600; + word-break: break-word; +} + +.debug-score-pills { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 6px; +} + +.debug-score-pill { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border-radius: 999px; + border: 1px solid #e3e3e3; + background: #f7f7f7; +} + +.debug-score-pill-label { + color: #666; +} + +.debug-score-pill-value { + color: #111; + font-weight: 700; +} + +.tone-es { + background: #f8f1ff; + border-color: #e6d5ff; +} + +.tone-coarse { + background: #eef8ff; + border-color: #cae8ff; +} + +.tone-fine { + background: #f3fbef; + border-color: #d8f1c8; +} + +.tone-rerank { + background: #fff4e8; + border-color: #ffd9b0; +} + +.tone-final { + background: #fff1f0; + border-color: #ffc9c4; +} + +.tone-neutral { + background: #f5f5f5; +} + +.debug-details { + margin-top: 10px; +} + +.debug-details summary { + cursor: pointer; + color: #555; + font-weight: 600; +} + +.debug-json-pre { + margin-top: 8px; + padding: 10px; + background: #f5f5f5; + border-radius: 6px; + overflow: auto; + max-height: 240px; + white-space: pre-wrap; + word-break: break-word; +} + +.debug-timing-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.debug-timing-row { + display: grid; + grid-template-columns: 220px 1fr 90px; + gap: 10px; + align-items: center; +} + +.debug-timing-label { + color: #444; +} + +.debug-timing-bar-wrap { + height: 10px; + background: #f0f0f0; + border-radius: 999px; + overflow: hidden; +} + +.debug-timing-bar { + height: 100%; + background: linear-gradient(90deg, #f39c12 0%, #e74c3c 100%); + border-radius: 999px; +} + +.debug-timing-value { + text-align: right; + color: #666; + font-weight: 600; +} + .product-debug-link { display: inline-block; margin-top: 0; @@ -687,10 +878,41 @@ footer span { } .product-grid { - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - gap: 15px; padding: 15px; } + + .product-card { + flex-direction: column; + } + + .product-main { + width: 100%; + } + + .product-image-wrapper { + width: 100%; + max-width: 320px; + } + + .product-debug { + width: 100%; + border-left: none; + border-top: 1px dashed #eee; + padding-left: 0; + padding-top: 12px; + } + + .debug-stage-grid { + grid-template-columns: 1fr; + } + + .debug-timing-row { + grid-template-columns: 1fr; + } + + .debug-timing-value { + text-align: left; + } .pagination { padding: 20px 15px; @@ -699,10 +921,6 @@ footer span { } @media (max-width: 480px) { - .product-grid { - grid-template-columns: repeat(2, 1fr); - } - .header-left { gap: 15px; } diff --git a/frontend/static/js/app.js b/frontend/static/js/app.js index c597d96..a18528f 100644 --- a/frontend/static/js/app.js +++ b/frontend/static/js/app.js @@ -407,89 +407,12 @@ function displayResults(data) { let debugHtml = ''; if (debug) { - const esScore = typeof debug.es_score === 'number' ? debug.es_score.toFixed(4) : String(debug.es_score ?? ''); - const es_score_normalized = typeof debug.es_score_normalized === 'number' - ? debug.es_score_normalized.toFixed(4) - : (debug.es_score_normalized == null ? '' : String(debug.es_score_normalized)); - const rerankScore = typeof debug.rerank_score === 'number' - ? debug.rerank_score.toFixed(4) - : (debug.rerank_score == null ? '' : String(debug.rerank_score)); - - const fusedScore = typeof debug.fused_score === 'number' - ? debug.fused_score.toFixed(4) - : (debug.fused_score == null ? '' : String(debug.fused_score)); - - // Build multilingual title info - let titleLines = ''; - if (debug.title_multilingual && typeof debug.title_multilingual === 'object') { - Object.entries(debug.title_multilingual).forEach(([lang, val]) => { - if (val) { - titleLines += `
title.${escapeHtml(String(lang))}: ${escapeHtml(String(val))}
`; - } - }); - } - - const resultJson = customStringify(result); - const rawUrl = `${API_BASE_URL}/search/es-doc/${encodeURIComponent(spuId)}?tenant_id=${encodeURIComponent(tenantId)}`; - const rerankInputHtml = debug.rerank_input - ? ` -
- Rerank input -
${escapeHtml(customStringify(debug.rerank_input))}
-
- ` - : ''; - const styleIntentHtml = debug.style_intent_sku - ? ` -
- Selected SKU -
${escapeHtml(customStringify(debug.style_intent_sku))}
-
- ` - : ''; - const matchedQueriesHtml = debug.matched_queries - ? ` -
- matched_queries -
${escapeHtml(customStringify(debug.matched_queries))}
-
- ` - : ''; - - debugHtml = ` -
-
Ranking Debug
-
spu_id: ${escapeHtml(String(spuId || ''))}
-
Position before rerank: ${escapeHtml(String(debug.initial_rank ?? ''))}
-
Position after rerank: ${escapeHtml(String(debug.final_rank ?? ''))}
-
ES score: ${esScore}
-
ES normalized: ${es_score_normalized}
-
Rerank score: ${rerankScore}
-
rerank_factor: ${escapeHtml(String(debug.rerank_factor ?? ''))}
-
text_score: ${escapeHtml(String(debug.text_score ?? ''))}
-
text_factor: ${escapeHtml(String(debug.text_factor ?? ''))}
-
knn_score: ${escapeHtml(String(debug.knn_score ?? ''))}
-
knn_factor: ${escapeHtml(String(debug.knn_factor ?? ''))}
-
Fused score: ${fusedScore}
- ${titleLines} - ${rerankInputHtml} - ${styleIntentHtml} - ${matchedQueriesHtml} -
- - - 查看 ES 原始文档 - -
- -
- `; + debugHtml = buildProductDebugHtml({ + debug, + result, + spuId, + tenantId, + }); } html += ` @@ -527,6 +450,126 @@ function displayResults(data) { grid.innerHTML = html; } +function formatDebugNumber(value, digits = 4) { + if (typeof value === 'number' && Number.isFinite(value)) { + return value.toFixed(digits); + } + return value == null || value === '' ? 'N/A' : String(value); +} + +function renderMetricList(items) { + const rows = items + .filter((item) => item && item.value !== undefined && item.value !== null && item.value !== '') + .map((item) => ` +
+
${escapeHtml(item.label)}
+
${escapeHtml(String(item.value))}
+
+ `) + .join(''); + return rows ? `
${rows}
` : ''; +} + +function renderScorePills(items) { + const pills = items + .filter((item) => item && item.value !== undefined && item.value !== null && item.value !== '') + .map((item) => ` +
+ ${escapeHtml(item.label)} + ${escapeHtml(String(item.value))} +
+ `) + .join(''); + return pills ? `
${pills}
` : ''; +} + +function renderJsonDetails(title, payload, open = false) { + if (!payload || (typeof payload === 'object' && Object.keys(payload).length === 0)) { + return ''; + } + return ` +
+ ${escapeHtml(title)} +
${escapeHtml(customStringify(payload))}
+
+ `; +} + +function buildProductDebugHtml({ debug, result, spuId, tenantId }) { + const resultJson = customStringify(result); + const rawUrl = `${API_BASE_URL}/search/es-doc/${encodeURIComponent(spuId)}?tenant_id=${encodeURIComponent(tenantId)}`; + + const rankSummary = renderMetricList([ + { label: 'Initial Rank', value: debug.initial_rank ?? 'N/A' }, + { label: 'Final Rank', value: debug.final_rank ?? 'N/A' }, + { label: 'Rank Delta', value: (debug.initial_rank && debug.final_rank) ? String(debug.initial_rank - debug.final_rank) : 'N/A' }, + { label: 'SPU', value: spuId || 'N/A' }, + ]); + + const stageScores = renderScorePills([ + { label: 'ES', value: formatDebugNumber(debug.es_score), tone: 'tone-es' }, + { label: 'ES Norm', value: formatDebugNumber(debug.es_score_normalized), tone: 'tone-neutral' }, + { label: 'Coarse', value: formatDebugNumber(debug.coarse_score), tone: 'tone-coarse' }, + { label: 'Fine', value: formatDebugNumber(debug.fine_score), tone: 'tone-fine' }, + { label: 'Rerank', value: formatDebugNumber(debug.rerank_score), tone: 'tone-rerank' }, + { label: 'Fused', value: formatDebugNumber(debug.fused_score), tone: 'tone-final' }, + ]); + + const factorMetrics = renderMetricList([ + { label: 'coarse_text_factor', value: formatDebugNumber(debug.coarse_text_factor) }, + { label: 'coarse_knn_factor', value: formatDebugNumber(debug.coarse_knn_factor) }, + { label: 'text_factor', value: formatDebugNumber(debug.text_factor) }, + { label: 'knn_factor', value: formatDebugNumber(debug.knn_factor) }, + { label: 'fine_factor', value: formatDebugNumber(debug.fine_factor) }, + { label: 'rerank_factor', value: formatDebugNumber(debug.rerank_factor) }, + ]); + + const signalMetrics = renderMetricList([ + { label: 'text_score', value: formatDebugNumber(debug.text_score) }, + { label: 'text_source', value: formatDebugNumber(debug.text_source_score) }, + { label: 'text_translation', value: formatDebugNumber(debug.text_translation_score) }, + { label: 'text_primary', value: formatDebugNumber(debug.text_primary_score) }, + { label: 'text_support', value: formatDebugNumber(debug.text_support_score) }, + { label: 'knn_score', value: formatDebugNumber(debug.knn_score) }, + { label: 'text_knn', value: formatDebugNumber(debug.text_knn_score) }, + { label: 'image_knn', value: formatDebugNumber(debug.image_knn_score) }, + ]); + + const titlePayload = {}; + if (debug.title_multilingual) titlePayload.title = debug.title_multilingual; + if (debug.brief_multilingual) titlePayload.brief = debug.brief_multilingual; + if (debug.vendor_multilingual) titlePayload.vendor = debug.vendor_multilingual; + + return ` +
+
Ranking Funnel
+ ${rankSummary} + ${stageScores} +
Fusion Factors
+ ${factorMetrics} +
Signal Breakdown
+ ${signalMetrics} + ${renderJsonDetails('Rerank Input', debug.rerank_input, true)} + ${renderJsonDetails('Selected SKU', debug.style_intent_sku, true)} + ${renderJsonDetails('Matched Queries', debug.matched_queries, false)} + ${renderJsonDetails('Multilingual Fields', titlePayload, false)} +
+ + + 查看 ES 原始文档 + +
+ +
+ `; +} + // Display facets as filter tags (一级分类 + 三个属性分面) function displayFacets(facets) { if (!facets || !Array.isArray(facets)) { @@ -919,127 +962,174 @@ function formatIntentDetectionHtml(intent) { return block; } +function buildStageCard(title, subtitle, metrics, extraHtml = '') { + return ` +
+
${escapeHtml(title)}
+ ${subtitle ? `
${escapeHtml(subtitle)}
` : ''} + ${renderMetricList(metrics)} + ${extraHtml} +
+ `; +} + +function renderTimingBars(stageTimings) { + if (!stageTimings || typeof stageTimings !== 'object') { + return ''; + } + const orderedStages = [ + 'query_parsing', + 'query_building', + 'elasticsearch_search_primary', + 'coarse_ranking', + 'style_sku_prepare_hits', + 'fine_ranking', + 'reranking', + 'elasticsearch_page_fill', + 'result_processing', + 'total_search', + ]; + const entries = Object.entries(stageTimings) + .sort((a, b) => { + const ai = orderedStages.indexOf(a[0]); + const bi = orderedStages.indexOf(b[0]); + return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi); + }); + const total = Number(stageTimings.total_search || 0); + return ` +
+ ${entries.map(([stage, duration]) => { + const numeric = Number(duration) || 0; + const width = total > 0 ? Math.max(2, Math.round((numeric / total) * 100)) : 2; + return ` +
+
${escapeHtml(stage)}
+
+
${numeric.toFixed(2)}ms
+
+ `; + }).join('')} +
+ `; +} + +function buildGlobalFunnelHtml(data, debugInfo) { + const queryAnalysis = debugInfo.query_analysis || {}; + const searchParams = debugInfo.search_params || {}; + const featureFlags = debugInfo.feature_flags || {}; + const esResponse = debugInfo.es_response || {}; + const esQueryContext = debugInfo.es_query_context || {}; + const coarseInfo = debugInfo.coarse_rank || {}; + const fineInfo = debugInfo.fine_rank || {}; + const rerankInfo = debugInfo.rerank || {}; + const translations = queryAnalysis.translations || {}; + + const summaryHtml = ` +
+
Query Context
+ ${renderMetricList([ + { label: 'original_query', value: queryAnalysis.original_query || 'N/A' }, + { label: 'rewritten_query', value: queryAnalysis.rewritten_query || 'N/A' }, + { label: 'detected_language', value: queryAnalysis.detected_language || 'N/A' }, + { label: 'index_languages', value: (queryAnalysis.index_languages || []).join(', ') || 'N/A' }, + { label: 'query_tokens', value: (queryAnalysis.query_tokens || []).join(', ') || 'N/A' }, + { label: 'translation_enabled', value: featureFlags.translation_enabled ? 'enabled' : 'disabled' }, + { label: 'embedding_enabled', value: featureFlags.embedding_enabled ? 'enabled' : 'disabled' }, + { label: 'style_intent_active', value: featureFlags.style_intent_active ? 'yes' : 'no' }, + ])} + ${Object.keys(translations).length ? renderJsonDetails('Translations', translations, true) : ''} + ${formatIntentDetectionHtml(queryAnalysis.intent_detection ?? queryAnalysis.style_intent_profile)} +
+ `; + + const funnelHtml = ` +
+
Ranking Funnel
+
+ ${buildStageCard('ES Recall', 'First-pass retrieval', [ + { label: 'fetch_from', value: searchParams.es_fetch_from ?? 0 }, + { label: 'fetch_size', value: searchParams.es_fetch_size ?? 'N/A' }, + { label: 'total_hits', value: esResponse.total_hits ?? 'N/A' }, + { label: 'es_took_ms', value: esResponse.took_ms ?? 'N/A' }, + { label: 'include_named_queries_score', value: esQueryContext.include_named_queries_score ? 'yes' : 'no' }, + ])} + ${buildStageCard('Coarse Rank', 'Lexical + vector fusion only', [ + { label: 'docs_in', value: coarseInfo.docs_in ?? searchParams.es_fetch_size ?? 'N/A' }, + { label: 'docs_out', value: coarseInfo.docs_out ?? 'N/A' }, + { label: 'formula', value: 'text x knn' }, + ], coarseInfo.fusion ? renderJsonDetails('Coarse Fusion', coarseInfo.fusion, false) : '')} + ${buildStageCard('Fine Rank', 'Lightweight reranker', [ + { label: 'service_url', value: fineInfo.service_url || 'N/A' }, + { label: 'docs', value: fineInfo.docs ?? fineInfo.top_n ?? 'N/A' }, + { label: 'top_n', value: fineInfo.top_n ?? 'N/A' }, + { label: 'query_template', value: fineInfo.query_template || 'N/A' }, + ], fineInfo.meta ? renderJsonDetails('Fine Meta', fineInfo.meta, false) : '')} + ${buildStageCard('Final Rerank', 'Heavy reranker + final fusion', [ + { label: 'service_url', value: rerankInfo.service_url || 'N/A' }, + { label: 'docs', value: rerankInfo.docs ?? 'N/A' }, + { label: 'top_n', value: rerankInfo.top_n ?? 'N/A' }, + { label: 'query_template', value: rerankInfo.query_template || 'N/A' }, + ], `${rerankInfo.fusion ? renderJsonDetails('Final Fusion', rerankInfo.fusion, false) : ''}${rerankInfo.meta ? renderJsonDetails('Rerank Meta', rerankInfo.meta, false) : ''}`)} + ${buildStageCard('Page Return', 'Final slice returned to UI', [ + { label: 'from', value: searchParams.from_ ?? 0 }, + { label: 'size', value: searchParams.size ?? 'N/A' }, + { label: 'returned', value: (data.results || []).length }, + { label: 'max_score', value: formatDebugNumber(esResponse.max_score, 3) }, + ])} +
+
+ `; + + const timingHtml = ` +
+
Timing Breakdown
+ ${renderTimingBars(debugInfo.stage_timings)} +
+ `; + + const rawPayloadHtml = ` +
+
Raw Payloads
+ ${renderJsonDetails('ES Query DSL', debugInfo.es_query, false)} + ${renderJsonDetails('ES Query Context', debugInfo.es_query_context, false)} + ${renderJsonDetails('Search Params', debugInfo.search_params, false)} +
+ `; + + return ` +
+ ${summaryHtml} + ${funnelHtml} + ${timingHtml} + ${rawPayloadHtml} +
+ `; +} + // Display debug info function displayDebugInfo(data) { const debugInfoDiv = document.getElementById('debugInfo'); - + if (!state.debug || !data.debug_info) { - // If debug mode is off or no debug info, show basic query info if (data.query_info) { - let html = '
'; - html += `
original_query: ${escapeHtml(data.query_info.original_query || 'N/A')}
`; - html += `
detected_language: ${escapeHtml(data.query_info.detected_language || 'N/A')}
`; - html += '
'; - debugInfoDiv.innerHTML = html; + debugInfoDiv.innerHTML = ` +
+
+
Query Context
+ ${renderMetricList([ + { label: 'original_query', value: data.query_info.original_query || 'N/A' }, + { label: 'detected_language', value: data.query_info.detected_language || 'N/A' }, + ])} +
+
+ `; } else { debugInfoDiv.innerHTML = ''; } return; } - - // Display comprehensive debug info when debug mode is on - const debugInfo = data.debug_info; - let html = '
'; - - // Query Analysis - if (debugInfo.query_analysis) { - html += '
Query Analysis:'; - html += `
original_query: ${escapeHtml(debugInfo.query_analysis.original_query || 'N/A')}
`; - html += `
query_normalized: ${escapeHtml(debugInfo.query_analysis.query_normalized || 'N/A')}
`; - html += `
rewritten_query: ${escapeHtml(debugInfo.query_analysis.rewritten_query || 'N/A')}
`; - html += `
detected_language: ${escapeHtml(debugInfo.query_analysis.detected_language || 'N/A')}
`; - html += `
index_languages: ${escapeHtml((debugInfo.query_analysis.index_languages || []).join(', ') || 'N/A')}
`; - html += `
query_tokens: ${escapeHtml((debugInfo.query_analysis.query_tokens || []).join(', ') || 'N/A')}
`; - - if (debugInfo.query_analysis.translations && Object.keys(debugInfo.query_analysis.translations).length > 0) { - html += '
translations: '; - for (const [lang, translation] of Object.entries(debugInfo.query_analysis.translations)) { - if (translation) { - html += `${lang}: ${escapeHtml(translation)}; `; - } - } - html += '
'; - } - - if (debugInfo.query_analysis.boolean_ast) { - html += `
boolean_ast: ${escapeHtml(debugInfo.query_analysis.boolean_ast)}
`; - } - const intentPayload = debugInfo.query_analysis.intent_detection ?? debugInfo.query_analysis.style_intent_profile; - html += formatIntentDetectionHtml(intentPayload); - - html += '
'; - } - - // Feature Flags - if (debugInfo.feature_flags) { - html += '
Feature Flags:'; - html += `
translation_enabled: ${debugInfo.feature_flags.translation_enabled ? 'enabled' : 'disabled'}
`; - html += `
embedding_enabled: ${debugInfo.feature_flags.embedding_enabled ? 'enabled' : 'disabled'}
`; - html += `
rerank_enabled: ${debugInfo.feature_flags.rerank_enabled ? 'enabled' : 'disabled'}
`; - if (debugInfo.feature_flags.style_intent_enabled !== undefined) { - html += `
style_intent_enabled: ${debugInfo.feature_flags.style_intent_enabled ? 'enabled' : 'disabled'}
`; - } - if (debugInfo.feature_flags.style_intent_active !== undefined) { - html += `
style_intent_active: ${debugInfo.feature_flags.style_intent_active ? 'yes' : 'no'}
`; - } - html += '
'; - } - - // ES Response - if (debugInfo.es_response) { - html += '
ES Response:'; - html += `
took_ms: ${debugInfo.es_response.took_ms}ms
`; - html += `
total_hits: ${debugInfo.es_response.total_hits}
`; - html += `
max_score: ${debugInfo.es_response.max_score?.toFixed(3) || 0}
`; - html += `
es_score_normalization_factor: ${escapeHtml(String(debugInfo.es_response.es_score_normalization_factor ?? ''))}
`; - html += '
'; - } - - if (debugInfo.rerank) { - html += '
Rerank:'; - html += `
query_template: ${escapeHtml(debugInfo.rerank.query_template || 'N/A')}
`; - html += `
doc_template: ${escapeHtml(debugInfo.rerank.doc_template || 'N/A')}
`; - html += `
query_text: ${escapeHtml(debugInfo.rerank.query_text || 'N/A')}
`; - html += `
docs: ${escapeHtml(String(debugInfo.rerank.docs ?? ''))}
`; - html += `
top_n: ${escapeHtml(String(debugInfo.rerank.top_n ?? ''))}
`; - if (debugInfo.rerank.fusion) { - html += '
fusion:
'; - html += `
${escapeHtml(customStringify(debugInfo.rerank.fusion))}
`; - } - html += '
'; - } - - // Stage Timings - if (debugInfo.stage_timings) { - html += '
Stage Timings:'; - const bounds = debugInfo.stage_time_bounds_ms || {}; - for (const [stage, duration] of Object.entries(debugInfo.stage_timings)) { - const b = bounds[stage]; - if (b && b.start_unix_ms != null && b.end_unix_ms != null) { - html += `
${stage}: ${Number(duration).toFixed(2)}ms (start ${b.start_unix_ms} → end ${b.end_unix_ms} unix ms)
`; - } else { - html += `
${stage}: ${Number(duration).toFixed(2)}ms
`; - } - } - html += '
'; - } - - // ES Query - if (debugInfo.es_query) { - html += '
ES Query DSL:'; - html += `
${escapeHtml(customStringify(debugInfo.es_query))}
`; - html += '
'; - } - - if (debugInfo.es_query_context) { - html += '
ES Query Context:'; - html += `
${escapeHtml(customStringify(debugInfo.es_query_context))}
`; - html += '
'; - } - - html += '
'; - debugInfoDiv.innerHTML = html; + debugInfoDiv.innerHTML = buildGlobalFunnelHtml(data, data.debug_info); } // Custom JSON stringify that compresses numeric arrays (like embeddings) to single line @@ -1070,4 +1160,3 @@ function formatDate(dateStr) { return dateStr; } } - diff --git a/search/searcher.py b/search/searcher.py index e032504..20f08ec 100644 --- a/search/searcher.py +++ b/search/searcher.py @@ -897,6 +897,16 @@ class Searcher: if doc_id is None: continue rerank_debug_by_doc[str(doc_id)] = item + coarse_debug_raw = context.get_intermediate_result('coarse_rank_scores', None) + coarse_debug_by_doc: Dict[str, Dict[str, Any]] = {} + if isinstance(coarse_debug_raw, list): + for item in coarse_debug_raw: + if not isinstance(item, dict): + continue + doc_id = item.get("doc_id") + if doc_id is None: + continue + coarse_debug_by_doc[str(doc_id)] = item fine_debug_raw = context.get_intermediate_result('fine_rank_scores', None) fine_debug_by_doc: Dict[str, Dict[str, Any]] = {} if isinstance(fine_debug_raw, list): @@ -937,6 +947,9 @@ class Searcher: rerank_debug = None if doc_id is not None: rerank_debug = rerank_debug_by_doc.get(str(doc_id)) + coarse_debug = None + if doc_id is not None: + coarse_debug = coarse_debug_by_doc.get(str(doc_id)) fine_debug = None if doc_id is not None: fine_debug = fine_debug_by_doc.get(str(doc_id)) @@ -974,6 +987,11 @@ class Searcher: "vendor_multilingual": vendor_multilingual, } + if coarse_debug: + debug_entry["coarse_score"] = coarse_debug.get("coarse_score") + debug_entry["coarse_text_factor"] = coarse_debug.get("coarse_text_factor") + debug_entry["coarse_knn_factor"] = coarse_debug.get("coarse_knn_factor") + # 若存在重排调试信息,则补充 doc 级别的融合分数信息 if rerank_debug: debug_entry["doc_id"] = rerank_debug.get("doc_id") -- libgit2 0.21.2