Spaces:
Sleeping
Download ABLATION_SYSTEM_VERIFICATION.md from babaralishah/rag-llm-app: direct link, hf CLI and curl.
- Browser
- Download file 14 kB
-
https://huggingface.co/spaces/babaralishah/rag-llm-app/resolve/main/ABLATION_SYSTEM_VERIFICATION.md
- Command line
-
hf download hf://spaces/babaralishah/rag-llm-app/ABLATION_SYSTEM_VERIFICATION.md
-
curl -L -o ABLATION_SYSTEM_VERIFICATION.md https://huggingface.co/spaces/babaralishah/rag-llm-app/resolve/main/ABLATION_SYSTEM_VERIFICATION.md
Ablation Study System - Complete Wiring Verification β
Executive Summary
The ablation study system is fully wired and operational. All feature flags flow correctly from the Streamlit UI through the FastAPI backend to the RAG pipeline with proper cache isolation per phase.
1. UI Layer (ui.py)
Phase Selector Creation
β Line 278-285: Phase selectbox created in sidebar
phase = st.selectbox(
"Select experimental phase",
options=["V1", "V2", "V3", "V4", "V5", "V6"],
index=5,
help="V1: Basic RAG | V2: +Query Rewriter | V3: +History | V4: +Hybrid | V5: +Reranker | V6: +Guardrails",
)
Query Payload
β Line 345: Phase included in every query request
payload = {
"question": question,
"top_k": top_k,
"use_reranker": use_reranker,
"use_hybrid": use_hybrid,
"phase": phase, # β SENT WITH EVERY QUERY
"history": [...]
}
2. Backend Request Model (api.py)
QueryRequest Definition
β Line 175-180: Phase field properly defined
class QueryRequest(BaseModel):
question: str
top_k: int = TOP_K
use_reranker: bool = True
use_hybrid: bool = True
phase: Optional[str] = None # β ACCEPTS PHASE FROM UI
history: Optional[List[ChatMessage]] = None
3. Phase Resolution Logic (api.py)
resolve_phase_flags()
β Line 184-194: Converts phase string to feature flags
def resolve_phase_flags(phase: Optional[str]) -> Dict[str, bool]:
if not phase:
return {}
phase_key = phase.strip().upper() # "v6" β "V6"
if phase_key not in PHASE_FLAGS:
logger.warning("Unknown phase '%s', falling back to runtime toggles.", phase_key)
return {}
return PHASE_FLAGS[phase_key].copy() # β RETURNS DICT OF FLAGS
get_query_settings()
β Line 196-209: Routes to correct phase or fallback
def get_query_settings(req: QueryRequest) -> Dict[str, bool]:
phase_settings = resolve_phase_flags(req.phase)
if phase_settings:
return phase_settings # β USE PHASE FLAGS IF PROVIDED
# Fallback to individual toggles if no phase specified
return {
"use_query_rewriter": True,
"use_hybrid": req.use_hybrid,
"use_reranker": req.use_reranker,
"use_chat_history": True,
"use_guardrails": False,
}
4. Phase Definitions (config.py)
PHASE_FLAGS Dictionary
β config.py lines 27-57: All 6 phases defined
PHASE_FLAGS = {
"V1": { # Baseline: No features
"use_query_rewriter": False,
"use_hybrid": False,
"use_chat_history": False,
"use_reranker": False,
"use_guardrails": False,
},
"V2": { # +Query Rewriter
"use_query_rewriter": True,
"use_hybrid": False,
"use_chat_history": False,
"use_reranker": False,
"use_guardrails": False,
},
"V3": { # +Chat History
"use_query_rewriter": True,
"use_hybrid": False,
"use_chat_history": True,
"use_reranker": False,
"use_guardrails": False,
},
"V4": { # +Hybrid Search
"use_query_rewriter": True,
"use_hybrid": True,
"use_chat_history": True,
"use_reranker": False,
"use_guardrails": False,
},
"V5": { # +Reranker
"use_query_rewriter": True,
"use_hybrid": True,
"use_chat_history": True,
"use_reranker": True,
"use_guardrails": False,
},
"V6": { # +Guardrails
"use_query_rewriter": True,
"use_hybrid": True,
"use_chat_history": True,
"use_reranker": True,
"use_guardrails": True,
},
}
5. Query Execution Pipeline (/query endpoint, api.py)
Step 1: Extract Phase Flags
β Line 607: Get flags from selected phase
flags = get_query_settings(req)
# flags = {"use_query_rewriter": True, "use_hybrid": True, ...}
Step 2: Chat History Based on Flags
β Line 608: Include history only if phase enables it
history = normalize_chat_history(req.history) if flags["use_chat_history"] else []
Step 3: Query Rewriting Based on Flags
β Line 609-611: Use query rewriter only if phase enables it
query_text, query_vector = rewrite_and_embed_query(
question, use_query_rewriter=flags["use_query_rewriter"] # β FLAG PASSED
)
- If
use_query_rewriter=Falseβ question used as-is - If
use_query_rewriter=Trueβ question rewritten via Gemini
Step 4: Document Search with Flags
β Line 612-618: Pass hybrid & reranker flags to search
retrieved = search_documents(
store,
query_text,
query_vector,
top_k=req.top_k,
use_hybrid=flags["use_hybrid"], # β Semantic + BM25
use_reranker=flags["use_reranker"], # β Sets retrieve_k
)
- If
use_reranker=Trueβ retrieve 12 chunks, keep top 6 after reranking - If
use_reranker=Falseβ retrieve exactlytop_kchunks
Step 5: Answer Generation with Flags
β Line 629-636: Pass reranker & guardrail flags to LLM pipeline
result = generate_answer_payload(question, retrieved, req, flags, history=history)
Where in generate_answer_payload:
def generate_answer_payload(question: str, retrieved: list, req: QueryRequest, flags: Dict[str, bool], ...):
out = rag_answer(
question=question,
retrieved=retrieved,
min_score=MIN_SCORE,
use_reranker=flags["use_reranker"], # β RERANKING FLAG
final_top_k=req.top_k,
use_guardrails=flags["use_guardrails"], # β GUARDRAIL FLAG
history=history,
)
# ...
6. RAG Pipeline Logic (src/rag_pipeline.py)
Reranking Based on Flag
β Lines 75-98: Reranking logic respects flag
if use_reranker and retrieved:
# Cross-encoder reranks the candidates
reranker = get_reranker()
retrieved = reranker.rerank(...)
else:
logger.info("β Re-ranking DISABLED by user")
for r in retrieved:
r["final_score"] = r.get("score", 0.0)
Guardrails Based on Flag
β Lines 107-121: Guardrail abstention respects flag
if use_guardrails and retrieved:
max_score = max((r.get("score", 0.0) for r in retrieved), default=0.0)
if max_score < min_score: # min_score = 0.5
logger.warning("Guardrail triggered: max retrieved score %.3f...", max_score, min_score)
return {
"answer": "I don't have enough high-confidence information from the documents...",
"sources": [],
}
7. Cache Isolation by Phase (src/cache.py)
Cache Key Includes Phase
β Line 47: Function signature accepts phase
def get_cache_key(question: str, use_hybrid=True, use_reranker=True, top_k=6, phase: str | None = None):
Phase Baked into Hash
β Lines 60-65: Phase included in cache key computation
data = {
"q": question.strip().lower(),
"hybrid": bool(use_hybrid),
"rerank": bool(use_reranker),
"k": int(top_k),
"phase": phase.strip().upper() if phase else None, # β PHASE IN KEY
}
return hashlib.md5(json.dumps(data, sort_keys=True).encode()).hexdigest()
Cache Key Called with Phase
β api.py line 590-596: Cache key generation includes phase
cache_key = get_cache_key(
question=question,
use_hybrid=req.use_hybrid,
use_reranker=req.use_reranker,
top_k=req.top_k,
phase=req.phase, # β PHASE PASSED
)
Result: Same question returns different cache keys per phase
cache_key_v1("What is ML?")βcache_key_v6("What is ML?")- Prevents cross-phase cache contamination β
8. Feature Disable Verification
| Feature | V1 | V2 | V3 | V4 | V5 | V6 | Disabled When |
|---|---|---|---|---|---|---|---|
| Query Rewriter | β | β | β | β | β | β | Line 609: rewrite_and_embed_query(..., use_query_rewriter=False) |
| Hybrid Search | β | β | β | β | β | β | Line 617: search_documents(..., use_hybrid=False) β only semantic |
| Chat History | β | β | β | β | β | β | Line 608: history = [] if flag is False |
| Reranker | β | β | β | β | β | β | Line 612: use_hybrid=False sets retrieve_k = top_k; Line 85 skips reranking |
| Guardrails | β | β | β | β | β | β | Line 632: use_guardrails=False β no score threshold check |
9. Tested Scenarios
All verified in tests/test_phase_flags.py:
β Test 1: Phase keys V1-V6 present in PHASE_FLAGS β Test 2: resolve_phase_flags() returns correct dictionaries for each phase β Test 3: get_query_settings() properly resolves phases β Test 4: Cache keys differ per phase (prevents cross-phase hits) β Test 5: Guardrail abstention triggers at score 0.2 (< 0.5 threshold) when enabled β Test 6: Normal LLM flow occurs at score 0.95 even when guardrails enabled β Test 7: Guardrails don't trigger in V5 (guardrails disabled)
10. Complete Request Flow Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STREAMLIT UI (ui.py) β
β User selects phase (V1-V6) and asks a question β
β Sends: {"question": "...", "phase": "V6", ...} β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FASTAPI /query ENDPOINT (api.py:583) β
β 1. Extract phase from request β "V6" β
β 2. Call get_query_settings(req) β
β β resolve_phase_flags("V6") β
β β Returns PHASE_FLAGS["V6"] β
β β {use_query_rewriter: True, use_hybrid: True, ...} β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββΌββββββββββββββ
βΌ βΌ βΌ
βββββββββββ βββββββββββ ββββββββββββββββ
β History β β Query β β Search & β
β Filter β β Rewrite β β Rerank β
β β β β β β
β use_... β β use_... β β use_hybrid β
β history β β rewriterβ β use_reranker β
ββββββ¬βββββ ββββββ¬βββββ ββββββββ¬ββββββββ
β β β
βΌ βΌ βΌ
[filtered or ] [rewritten or] [retrieve+rerank
full history] original query or just semantic]
β β β
βββββββββββββββΌβββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββ
β generate_answer_payload() β
β Calls rag_answer() with: β
β - use_reranker β
β - use_guardrails β
ββββββββββ¬ββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββ
β rag_pipeline.rag_answer() β
β β
β If use_reranker: β
β Cross-encode score candidates β
β If use_guardrails: β
β Check max_score >= 0.5 β
β If not β abstain β
β β
β Generate answer + evaluate β
ββββββββββ¬ββββββββββββββββββββββββββ
βΌ
βββββββββββββββββββββββββββββββ
β Cache with phase in key β
β (prevent cross-phase hits) β
ββββββββββ¬βββββββββββββββββββββ
βΌ
βββββββββββββββββββββββββββββββ
β Return answer to UI β
βββββββββββββββββββββββββββββββ
11. Conclusion
β System Status: FULLY OPERATIONAL
All wiring verified:
- UI phase selector β sends phase string
- QueryRequest accepts phase
- Phase resolution β correctly maps to PHASE_FLAGS
- Feature flags β properly extracted and passed through pipeline
- Each flag β correctly disables/enables the corresponding feature
- Cache isolation β prevents cross-phase contamination
- Tests β all passing and verified
You can now:
- Start the backend:
python -m uvicorn src.api:app --reload - Start the UI:
streamlit run ui.py - Select phase V1-V6 in the dropdown
- Submit queries and observe feature behavior change per phase
- Run full ablation study with high confidence in feature isolation