P3D — Vector Search Hybrid Path/Title Boost Design
P3D — Vector Search Hybrid Path/Title Boost Design
Date: 2026-05-11 Author: Opus 4.7 Directive: gpt-directive-opus-p3d-vector-search-hybrid-path-title-boost-pack-2026-05-11.md Mode: DESIGN ONLY — no implementation, no deploy Root cause: SEARCH_RANKING_NO_PATH_TITLE_BOOST_NOT_VECTOR_FRESHNESS
§A. Problem Statement
search_knowledge returns Qdrant vector-similarity results only. When user queries contain exact document names, path slugs, or titles, the correct document can rank below older docs with similar semantic content. Example: query "p3d-pack1-readonly-inventory-prompt revision 2" returns the exact file at rank 3, not rank 1.
This is a ranking problem, not a vector freshness problem. All recent docs are vectorized and retrievable — just not reliably ranked first for exact lookups.
§B. Evidence Summary
| Finding | Detail |
|---|---|
| All 5 target docs vectorized | vector_status=ready, Qdrant points present |
| 2/5 targets not rank 1 | GPT review target rank=5, inventory prompt rank=3 |
| 3/5 targets rank 1 | Queries with distinctive enough semantics work |
| Search mode | Qdrant vector ONLY for normal path (server.py:1185-1217) |
| PG keyword fallback | Only when Qdrant empty/error — bypassed for normal hits |
| Path/title boost | NONE — document_id used only for dedup |
| Code modifiable | Yes — /opt/incomex/docker/agent-data-repo/agent_data/ |
§C. Current Search Behavior
User query → embed via OpenAI → Qdrant vector search (limit=top_k*2)
→ deduplicate by document_id → return top_k results sorted by vector score
→ NO path/title/tag boost
→ PG keyword fallback ONLY if Qdrant empty/error
Patch point: vector_store.py:294-308 (dedup + return) or server.py:1194-1217 (result handling).
§D. Proposed Hybrid Rerank Behavior
User query → embed via OpenAI → Qdrant vector search (limit=top_k*2)
→ deduplicate by document_id
→ NEW: compute text-match boosts per result
→ NEW: combined_score = vector_score + path_boost + title_boost + tag_boost
→ sort by combined_score DESC
→ return top_k results
No Qdrant mutation. No reindex. No new collection. Pure app-layer rerank.
§E. Scoring/Boost Rules
Query tokens = lowercase split of query string.
| Boost | Condition | Weight | Rationale |
|---|---|---|---|
| path_exact | query contains full document_id path | +0.30 | Exact file lookup = must be rank 1 |
| basename_match | query tokens overlap ≥50% with basename tokens (filename without extension, split on -_) |
+0.20 | "p3d-pack1-readonly-inventory-prompt" in query = strong signal |
| title_match | query tokens overlap ≥50% with metadata.title tokens | +0.15 | Title is human-readable intent |
| tag_match | any query token found in metadata.tags array | +0.05 per match (max +0.15) | Tags are classification signal |
| path_token_match | query tokens overlap with path directory tokens | +0.05 per match (max +0.10) | "dieu44" in query + "dieu44-trien-khai" in path = boost |
Token overlap formula: overlap = len(query_tokens ∩ target_tokens) / len(query_tokens). Threshold ≥ 0.50 for basename/title; any match for tags/path.
Max total boost: +0.90 (extremely exact match). Typical exact filename query: +0.50 (basename + path tokens). Typical semantic query with no path match: +0.00 (pure vector score preserved).
§F. Test Cases
| # | Query | Expected rank | Expected boost type |
|---|---|---|---|
| T1 | "GPT Review P3D Step 1 Re-authored Spec Pack 1 Directive" | ≤2 | title_match + path_token_match |
| T2 | "p3d-pack1-readonly-inventory-prompt revision 2" | =1 | basename_match + path_token_match |
| T3 | "gpt-directive-agent-run-step1-checkpoint-and-pack1-inventory-readonly" | =1 | basename_match (already rank 1, should stay) |
| T4 | "p3d-agent-vector-search-freshness-audit-readonly 2026-05-10" | =1 | basename_match (already rank 1) |
| T5 (regression) | "vector search freshness root cause" | relevant docs in top 5 | NO boost (no path/title match) — pure vector |
| T6 (regression) | "operating rules SSOT" | OR doc in top 3 | title_match possible |
§G. Rollback Plan
- Patch is single-function change — add
_rerank_results()function, call it before return - Rollback = remove function call — one-line revert in
server.pyorvector_store.py - Feature flag option:
SEARCH_RERANK_ENABLED=trueenv var → if false, skip rerank entirely - No Qdrant/PG mutation → rollback has zero data risk
- Docker restart sufficient to apply/revert
§H. Production Risk Controls
| Risk | Mitigation |
|---|---|
| Boost too aggressive → semantic results displaced | Max boost capped (+0.90); T5/T6 regression tests verify semantic still works |
| Token splitting edge cases | Use simple lowercase split on whitespace + -_./; no NLP dependency |
| Performance | Rerank is O(n) over top_k*2 results (~40-100 items); sub-millisecond |
| OpenAI/Qdrant failure | Rerank only runs AFTER Qdrant returns; failure path unchanged |
| Encoding issues (Vietnamese) | Lowercase + token split works for path/title which are ASCII-ish; body content not tokenized in boost |
§I. Non-Goals
- ❌ Reindex Qdrant
- ❌ Create new Qdrant collection
- ❌ Implement IU vector
- ❌ Fix ghost/orphan audit warnings (separate concern)
- ❌ Change chunking strategy
- ❌ Add new embedding model
- ❌ Modify TAC/IU schema
- ❌ Build full-text search index
- ❌ Add BM25 or ElasticSearch
§J. Acceptance Criteria
| # | Criterion | Verification |
|---|---|---|
| AC-1 | T1 target rank ≤ 2 | search_knowledge query + check rank |
| AC-2 | T2 target rank = 1 | search_knowledge query + check rank |
| AC-3 | T3, T4 target remain rank 1 | No regression |
| AC-4 | T5 semantic query returns relevant docs in top 5 | No displacement |
| AC-5 | T6 semantic query returns relevant docs | No displacement |
| AC-6 | Feature flag SEARCH_RERANK_ENABLED toggles rerank |
Env var test |
| AC-7 | Docker restart applies change | Standard deploy |
| AC-8 | No Qdrant mutation | Audit before/after point count identical |
| AC-9 | Performance ≤ 5ms added latency | Timing log |
P3D Vector Search Hybrid Boost Design | 2026-05-11 | Opus 4.7 | DESIGN ONLY