# Session 31 — RAG v1.3 Baseline PASS + retrieval.py critical fix **Date:** 2026-05-26 **Duration:** ~1.5h **Commits:** 1 pending (S31 docs/infra) **Sub-agents:** 0 ## Summary Session 31 followed directly from S30 (RAG v1.3 setup). S30 had bootstrapped 2949 chunks but the v1.1 baseline was tentative (PENDING_RELOAD). S31 re-ran the eval after CLI restart and discovered the real root cause. ## Root Cause Discovered: qdrant-client 1.18 removed search() **Symptom:** Most golden set queries returned 0 results even with 2949 Qdrant points green. **Root cause:** `qdrant-client 1.18.0` removed `QdrantClient.search()` method entirely. `retrieval.py:vector_search()` called `_qdrant.search(...)` which raised `AttributeError`. This exception was silently swallowed by `except Exception: continue` → `vec_results = []` always → pipeline fell back to BM25-only. **Diagnosis chain:** 1. q01/q02/q03/q08 worked (BM25 exact match found all tokens in same chunk) 2. q04/q05/q06/q07/q09/q10/q11 returned 0 (BM25 strict AND failed, vector broken) 3. Qdrant HTTP `/collections/proj_solution_erp` → `status=green`, `points_count=2949` ✓ 4. BM25 SQLite FTS: `MediatR` → 3 hits, `fallback` → 173 hits, `ApprovalWorkflow` → 40 hits (data IS there) 5. `python -c "from qdrant_client import QdrantClient; c=QdrantClient(...); c.search"` → AttributeError confirmed 6. Tested `query_points(query=...).points` → worked, 5 results returned ## Fix Applied **File:** `D:\Dropbox\CONG_VIEC\AI_INFRA\claude-rag\lib\retrieval.py` — `vector_search()` function ```python # Before (broken in qdrant-client 1.18): hits = _qdrant.search( collection_name=c, query_vector=query_vector, limit=top_k, with_payload=True, ) for h in hits: results.append({"chunk_id": h.id, ...}) # After (qdrant-client 1.12+ query_points API): resp = _qdrant.query_points( collection_name=c, query=query_vector, # param renamed limit=top_k, with_payload=True, ) for h in resp.points: # access .points on response results.append({"chunk_id": h.id, ...}) ``` ## Eval Results | Version | recall@5 | hits/11 | Pass | |---|---|---|---| | v1.0 (S30) | 0.455 | 5/11 | ❌ FAIL | | v1.1 tentative (S30 stale MCP) | 0.364 | 4/11 | ❌ FAIL | | **v1.1 FINAL (S31 after fix)** | **1.000** | **11/11** | **✅ PASS** | **avg_top1_rerank:** 0.847 (gate threshold 0.65) ✅ **Negative query behavior (q12/q13/q14):** - q12 GraphQL: rerank 0.43 (< 0.7 threshold — not a false positive) - q13 Redis: rerank 0.38 — correct exclusion - q14 Kubernetes: rerank 0.43 — correct exclusion ## Files Updated **AI_INFRA:** - `claude-rag/lib/retrieval.py` — vector_search() fix - `claude-rag/eval_v11.py` — temp eval script (not in SOLUTION_ERP) **SOLUTION_ERP:** - `eval/runs/2026-05-26-baseline-v1.1-final.json` — official baseline record - `eval/trial-state-lock.json` — quality_gate.pass=true, baseline_recall=1.000 - `docs/gotchas.md` — gotcha #52 added - `docs/STATUS.md` — S31 entry added - `docs/HANDOFF.md` — S31 entry added ## New Gotcha #52 `qdrant-client 1.18` removed `search()` API. When library upgrades break internal API, silent `except Exception: continue` in retrieval pipeline produces no error but returns empty results. Fix: use `query_points().points`. Lesson: pin qdrant-client version OR add health check `hasattr(_qdrant, 'query_points')`. ## Next Session **CRITICAL:** CLI restart to pick up retrieval.py fix (MCP server still loaded old code in memory). After CLI restart: - Live `search_memory` will use fixed vector + BM25 pipeline - RAG recall@5 = 1.000 confirmed **Other pending:** - Curate 4 sub-agent MEMORY files (Implementer over threshold) - Plan B-Wrap (Contract V2 test bundle BW1-BW7) - Phase 9 UAT hard blockers (SMTP, rotate creds, SQL backup, win-acme cert)