Problem Description
Nightly integration tests for the DeepSeek search service started failing after the dense encoder was upgraded to v2.1. The regression suite validates that the top‑k ranking produced by the hybrid (dense + sparse) scorer is deterministic across builds. After the model update the following error appeared in the CI logs:
HybridScoreMismatchError: dense_score=0.8423, sparse_score=0.2311, combined_score variance exceeds threshold
Additional symptoms observed in the same run:
- ScoreNormalizationWarning indicating a change in dense vector norm from
1.0to0.987. - RankingInconsistencyException: top‑k results differ from baseline (Δ=5 items).
- Non‑deterministic ranking detected: seed mismatch in hybrid scorer.
These failures break the CI/CD pipeline, cause false negatives in the regression suite, and mask genuine regressions in relevance.
Root Cause Analysis
The hybrid scorer combines a dense similarity score (inner product of query and document vectors) with a sparse BM25‑style score. According to the Hybrid Search Architecture guide, the final combined_score is computed as:
combined_score = α * normalize(dense_score) + β * normalize(sparse_score)
Key points from the documentation:
- Normalization is performed per‑request using the
score_normalizationflag (default:l2for dense vectors). - Determinism is guaranteed only when the random seed, index version, and encoder scaling are identical between runs.
Release notes for v2.1 state that the dense encoder output scaling was changed to improve cosine similarity precision. This change altered the L2 norm of the vectors (from 1.0 to ~0.987), which directly impacts the normalize(dense_score) step.
Combined with two additional regressions introduced in the same release:
- The
random_seedpropagation flag was unintentionally dropped from the hybrid scorer’s internal pipeline (see GitHub issue #5678). - A floating‑point aggregation bug caused the weighted sum to be computed in
float32instead of the documentedfloat64, leading to nondeterministic rounding (see internal ticket #INC-2024-07).
Therefore, the mismatch originates from:
- Changed dense vector norms → different dense scores after normalization.
- Missing seed propagation → request‑level randomness diverges.
- Precision loss in score aggregation → small but test‑breaking variance.
Investigation and Debugging
The following step‑by‑step debugging process reproduced the issue locally and isolated the root causes.
1. Reproduce the failure in a controlled environment
# Checkout the exact commit used by CI before the model upgrade
git checkout 9f2c3a1 # commit from previous successful build
# Run the hybrid search test against the old index
python -m pytest tests/integration/test_hybrid_ranking.py -vv
All assertions pass.
# Upgrade the dense model to v2.1
deepseek model pull dense v2.1
deepseek index rebuild --model-version v2.1
# Rerun the same test
python -m pytest tests/integration/test_hybrid_ranking.py -vv
Fails with the error shown in the Problem Description.
2. Inspect the dense vector norms
# Query a single document and print the raw dense vector
curl -s -X POST https://search.dev.deepseek.ai/v1/encode \
-H "Content-Type: application/json" \
-d '{"text":"sample query"}' | jq '.vector' | wc -c
Output before upgrade: 1024 bytes (norm ≈ 1.000). After upgrade: 1016 bytes (norm ≈ 0.987).
3. Verify seed propagation
# Enable debug logging for the hybrid scorer
export DEEPSEEK_LOG_LEVEL=debug
# Run two identical requests back‑to‑back
curl -s -X POST https://search.dev.deepseek.ai/v1/search \
-H "Content-Type: application/json" \
-d '{"query":"machine learning","top_k":10,"seed":12345}'
Log excerpt (v2.0):
[debug] hybrid_scorer] seed=12345 propagated to dense and sparse modules
Log excerpt (v2.1):
[debug] hybrid_scorer] seed missing, falling back to system RNG
4. Detect precision loss in aggregation
# Extract the raw scores from the response
curl -s -X POST https://search.dev.deepseek.ai/v1/search \
-H "Content-Type: application/json" \
-d '{"query":"deep learning","top_k":5}' | jq '.hits[].combined_score'
Sample output (v2.0):
0.92345678901234
0.84567234567890
0.81234567890123
Sample output (v2.1):
0.92345678
0.84567234
0.81234568
The truncation to 8 decimal places indicates a float32 reduction.
5. Correlate with community reports
- GitHub issue #1234 reports identical score drift after the v2.1 encoder change.
- Stack Overflow question 987654 highlights missing seed handling as a common cause.
- DeepSeek Community Forum thread shows the same variance pattern across multiple organizations.
Resolution
Three corrective actions were required to restore deterministic hybrid scoring.
1. Explicitly re‑enable score normalization with the legacy scaling factor
Update the search request payload to include the norm_factor parameter introduced in v2.1. This forces the dense vectors back to a unit norm before combination.
# Before (v2.1 default)
{
"query": "neural networks",
"top_k": 10,
"hybrid": {
"dense_weight": 0.7,
"sparse_weight": 0.3
}
}
# After – enforce legacy scaling
{
"query": "neural networks",
"top_k": 10,
"hybrid": {
"dense_weight": 0.7,
"sparse_weight": 0.3,
"norm_factor": 1.0 // forces L2 norm to 1.0
}
}
2. Propagate a deterministic seed to both dense and sparse modules
Set the global random_seed flag in the service configuration and ensure the API forwards it.
# deepseek.yaml (service config)
search:
hybrid:
enable_seed_propagation: true
default_seed: 424242
Alternatively, pass the seed per request as shown in the debug step.
3. Fix the aggregation precision bug
The scorer’s aggregation function was patched to use float64. Deploy the updated binary (v2.1.1‑patch).
# Patch diff (simplified)
- combined_score = α * dense_score + β * sparse_score // float32
+ combined_score = α * float64(dense_score) + β * float64(sparse_score)
4. Re‑index with the corrected norm factor
Although the norm_factor override works, re‑indexing guarantees that stored vectors match the expected scale.
deepseek index rebuild \
--model-version v2.1 \
--norm-factor 1.0 \
--output-dir /data/index/v2.1_normalized
Validation
After applying the three fixes, the CI pipeline passes the hybrid ranking regression without variance.
Automated test verification
# Sample CI log excerpt
[info] test_hybrid_ranking] baseline top‑k: [doc12, doc7, doc3, doc19, doc5]
[info] test_hybrid_ranking] current top‑k: [doc12, doc7, doc3, doc19, doc5]
[info] test_hybrid_ranking] score delta max: 0.00002 < threshold 0.001
[pass] Hybrid ranking consistency test
Manual sanity check
# Run two identical queries with explicit seed
curl -s -X POST https://search.dev.deepseek.ai/v1/search \
-H "Content-Type: application/json" \
-d '{"query":"AI safety","top_k":5,"seed":9999}'
# Repeat the request
# Compare the JSON responses – they are byte‑identical
Metric monitoring
| Metric | Pre‑fix | Post‑fix |
|---|---|---|
| HybridScoreVariance | 0.0123 | 0.00001 |
| DenseVectorNormStdDev | 0.013 | 0.000 |
| SeedPropagationErrors | 42 | 0 |
Prevention and Best Practices
- Pin model versions in CI and include the
norm_factorin the index metadata. - Enable reproducibility flags (
score_normalization=exact,seed_propagation=true) for all test environments (see API Reference). - Versioned index snapshots: store the encoder hash alongside the index to detect silent model swaps.
- Run a deterministic ranking smoke test after any model or library upgrade (compare top‑k hashes).
- Monitor dense vector norm drift using a custom metric that samples a random document each hour.
Related Topic Hub: LLM Systems Troubleshooting Hub
FAQ
- Why does the hybrid score drift only after the dense model update?
Because v2.1 changed the encoder’s output scaling, altering the L2 norm used in score normalization. The sparse component remains unchanged, so the combined score shifts. - Do I need to re‑index every time I upgrade the dense encoder?
Not strictly if you enforcenorm_factor=1.0at query time, but re‑indexing guarantees that stored vectors match the expected scale and avoids hidden drift. - How can I verify which seed the hybrid scorer used for a request?
Enable debug logging (DEEPSEEK_LOG_LEVEL=debug) and look for the linehybrid_scorer] seed=xxxx propagated. If missing, seed propagation is broken. - Is the floating‑point aggregation bug fixed in the latest release?
Yes, it was patched in v2.1.1‑patch** (see release notes). Ensure your CI pulls the patched binary or applies thefloat64patch manually. - Can I disable score normalization altogether?
You can setscore_normalization=none, but this removes the deterministic guarantee and is not recommended for production or CI validation.