Vizz17/context-aware-rag
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>The Change — ContextAware</title>7 <meta name="description" content="How ContextAware replaces top-k retrieval with multi-objective optimization">8 <script src="https://cdn.tailwindcss.com"></script>9 <!-- Added Plotly for the charts -->10 <script src="https://cdn.plot.ly/plotly-2.32.0.min.js"></script>11 12 <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-twilight.min.css" rel="stylesheet" />13 <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>14 <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>15 <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>16 17 <link rel="stylesheet" href="/static/style.css">18 <style>19 body { overflow-y: auto; padding: 0; }20 /* Code tabs */21 .code-tabs { display: flex; gap: 4px; border-bottom: 2px solid var(--glass-border); margin-bottom: 1rem; }22 .code-tab { padding: 0.8rem 1.5rem; font-size: 0.85rem; font-weight: 700; color: var(--text-muted); cursor: pointer; transition: all 0.2s; border-bottom: 3px solid transparent; margin-bottom: -2px; }23 .code-tab:hover { color: var(--text-main); }24 .code-tab.active { color: #00e5ff; border-bottom-color: #00e5ff; }25 .code-panel { display: none; padding: 0; animation: fadeIn 0.3s; }26 .code-panel.active { display: block; }27 pre.code-block { 28 background: rgba(0,0,0,0.5); 29 border: 1px solid rgba(255,255,255,0.06); 30 border-radius: 12px; 31 padding: 1.5rem; 32 overflow-x: auto; 33 font-family: 'JetBrains Mono', monospace; 34 font-size: 0.85rem; 35 line-height: 1.6; 36 color: #c8ccd0; 37 box-shadow: inset 0 2px 10px rgba(0,0,0,0.5); 38 white-space: pre-wrap;39 }40 .code-block .comment { color: #555; }41 .code-block .keyword { color: #b388ff; font-weight: bold; }42 .code-block .function { color: #00e5ff; }43 .code-block .string { color: #00e676; }44 .code-block .number { color: #ffd740; }45 </style>46</head>47<body>48 49 <!-- Particle Canvas -->50 <canvas id="particleCanvas"></canvas>51 <!-- Mesh Glow -->52 <div class="bg-mesh"></div>53 54 <div class="page-container mb-12">55 <!-- Back btn -->56 <a href="/" class="inline-flex items-center gap-2 text-sm font-semibold text-[var(--text-main)] hover:text-[#00e5ff] mb-8 btn-glass transition">← Back to Chat</a>57 58 <!-- Hero -->59 <div class="text-center py-8 mb-10 border-b border-[var(--glass-border)]">60 <span class="inline-block bg-[rgba(0,230,118,0.06)] text-[#00e676] border border-[rgba(0,230,118,0.12)] rounded-full px-4 py-1.5 text-xs font-bold uppercase tracking-widest mb-4 shadow-[0_0_15px_rgba(0,230,118,0.08)]">⚡ Formal Optimization Model · Beyond Top-K</span>61 <h1 class="text-4xl md:text-5xl font-extrabold tracking-tight mb-4 text-white">Algorithmic Paradigm Shift</h1>62 <p class="text-[var(--text-muted)] mt-2 text-lg max-w-3xl mx-auto">Transitioning from naïve Top-K dense retrieval to multi-objective submodular optimization in ContextAware RAG architectures.</p>63 <div class="mt-4 inline-flex items-center gap-2 bg-teal-500/10 border border-teal-500/20 rounded-full px-4 py-1.5 text-xs font-bold text-teal-400 uppercase tracking-widest">64 📊 Evaluated on 8 queries · 283 chunks · Real measured metrics65 </div>66 </div>67 68 <!-- Stats -->69 <div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-14">70 <div class="doc-card p-5 text-center transition-transform hover:-translate-y-2 border-b-4 border-b-emerald-500">71 <div class="text-3xl font-extrabold text-[var(--text-main)]">3</div>72 <div class="text-xs font-semibold text-[var(--text-muted)] mt-2 uppercase tracking-wide">Scoring Objectives</div>73 </div>74 <div class="doc-card p-5 text-center transition-transform hover:-translate-y-2 border-b-4 border-b-indigo-500">75 <div class="text-3xl font-extrabold text-[var(--text-main)]">30</div>76 <div class="text-xs font-semibold text-[var(--text-muted)] mt-2 uppercase tracking-wide">Candidate Pool</div>77 </div>78 <div class="doc-card p-5 text-center transition-transform hover:-translate-y-2 border-b-4 border-b-purple-500">79 <div class="text-3xl font-extrabold text-[var(--text-main)]">+24%</div>80 <div class="text-xs font-semibold text-[var(--text-muted)] mt-2 uppercase tracking-wide">Faithfulness Gain</div>81 </div>82 <div class="doc-card p-5 text-center transition-transform hover:-translate-y-2 border-b-4 border-b-amber-500">83 <div class="text-3xl font-extrabold text-[var(--text-main)]">77.5%</div>84 <div class="text-xs font-semibold text-[var(--text-muted)] mt-2 uppercase tracking-wide">Redundancy Reduced</div>85 </div>86 <div class="doc-card p-5 text-center transition-transform hover:-translate-y-2 border-b-4 border-b-red-500">87 <div class="text-3xl font-extrabold text-[var(--text-main)]">81%</div>88 <div class="text-xs font-semibold text-[var(--text-muted)] mt-2 uppercase tracking-wide">Grounding Score</div>89 </div>90 </div>91 92 <!-- The Problem with Standard Top-K -->93 <h2 class="text-2xl font-bold text-[var(--text-main)] mb-2 border-l-4 border-emerald-500 pl-3">🔍 Theoretical Limitations of Standard Top-K Retrieval</h2>94 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">Why marginal cosine similarity ranking systematically fails to produce reliable generative grounding</p>95 96 <div class="grid md:grid-cols-2 gap-6 mb-14">97 <div class="p-6 bg-red-500/5 border border-red-500/20 rounded-2xl shadow-[0_4px_30px_rgba(239,68,68,0.02)] border-l-4 border-l-red-500 hover:-translate-y-1 transition">98 <div class="inline-block bg-red-500/15 text-red-500 text-[10px] font-bold px-2 py-1 rounded-lg uppercase tracking-wider mb-4">❌ Standard Baseline</div>99 <div class="font-extrabold text-[var(--text-main)] mb-4 text-lg">Top-K Marginal Cosine Similarity</div>100 <ul class="text-sm text-[var(--text-muted)] space-y-3 font-medium list-disc pl-5">101 <li>Ranks each document <strong>independently</strong> by query vector proximity.</li>102 <li>Top-ranked distributions are frequently <strong>near-duplicates</strong> offering redundant context.</li>103 <li>Zero dynamic state awareness; ignoring the <strong>submodular coverage</strong> of the drawn set.</li>104 <li>Vital corroborating evidence is systematically <strong>omitted</strong> due to ranking displacement.</li>105 <li>Generative model consumes <strong>redundant tokens</strong>, drastically wasting context windows.</li>106 <li>Exponential <strong>hallucination risk</strong> when synthesizing multi-faceted inquiries.</li>107 </ul>108 </div>109 <div class="p-6 bg-emerald-500/5 border border-emerald-500/20 rounded-2xl shadow-[0_4px_30px_rgba(16,185,129,0.05)] border-l-4 border-l-emerald-500 hover:-translate-y-1 transition">110 <div class="inline-block bg-emerald-500/15 text-emerald-500 text-[10px] font-bold px-2 py-1 rounded-lg uppercase tracking-wider mb-4">✅ Proposed Architecture</div>111 <div class="font-extrabold text-[var(--text-main)] mb-4 text-lg">Multi-Objective Constrained Optimization</div>112 <ul class="text-sm text-[var(--text-muted)] space-y-3 font-medium list-disc pl-5">113 <li>Transforms text selection into a formal <strong>constrained knapsack optimization mapping</strong>.</li>114 <li>Computes a continuous joint objective across <strong>Relevance, Coverage, and Inter-Document Support</strong>.</li>115 <li>Employs a greedy solver to <strong>maximize marginal submodular gain</strong> iteratively.</li>116 <li>Mathematically <strong>enforces spatial diversity</strong> throughout the grounded evidence set.</li>117 <li>LLM constraint strictly receives <strong>non-redundant, comprehensively mapped</strong> context variables.</li>118 <li>Generative generation is probabilistically <strong>anchored</strong> with robust factual consensus.</li>119 </ul>120 </div>121 </div>122 123 <!-- Multi-Objective Breakdown -->124 <h2 class="text-2xl font-bold text-[var(--text-main)] mb-2 border-l-4 border-indigo-500 pl-3">🎯 Tri-Metric Scoring Function Formulation</h2>125 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">Vector-state evaluation dynamics scaling candidate nodes simultaneously across three axes</p>126 127 <div class="grid md:grid-cols-3 gap-6 mb-8">128 <div class="doc-card p-6 text-center shadow-[0_4px_30px_rgba(99,102,241,0.05)] border border-indigo-500/20 bg-black/20 hover:scale-105">129 <div class="font-bold text-xs mb-4 text-indigo-400 tracking-widest uppercase">📐 Relevance</div>130 <div class="bg-black/30 border border-[var(--glass-border)] rounded-lg py-3 font-mono text-[14px] font-bold text-[var(--text-main)] mb-3 shadow-inner">Rel(q,d) = cos(q,d)</div>131 <p class="text-xs text-[var(--text-muted)] font-medium">How semantically similar is this document to the user's query? Computed via cosine similarity between query and document embeddings.</p>132 </div>133 <div class="doc-card p-6 text-center shadow-[0_4px_30px_rgba(16,185,129,0.05)] border border-emerald-500/20 bg-black/20 hover:scale-105">134 <div class="font-bold text-xs mb-4 text-emerald-400 tracking-widest uppercase">🌐 Coverage</div>135 <div class="bg-black/30 border border-[var(--glass-border)] rounded-lg py-3 font-mono text-[14px] font-bold text-[var(--text-main)] mb-3 shadow-inner">Cov(d|S) = 1 − max sim(d,s)</div>136 <p class="text-xs text-[var(--text-muted)] font-medium">How much <em>new</em> information does this doc bring? Measures diversity — penalizes documents that duplicate already-selected content.</p>137 </div>138 <div class="doc-card p-6 text-center shadow-[0_4px_30px_rgba(245,158,11,0.05)] border border-amber-500/20 bg-black/20 hover:scale-105">139 <div class="font-bold text-xs mb-4 text-amber-400 tracking-widest uppercase">🤝 Support</div>140 <div class="bg-black/30 border border-[var(--glass-border)] rounded-lg py-3 font-mono text-[14px] font-bold text-[var(--text-main)] mb-3 shadow-inner">Sup(d,S) = mean sim(d,s)</div>141 <p class="text-xs text-[var(--text-muted)] font-medium">Is this document's claims supported by other selected evidence? Higher support = greater cross-document agreement = more reliable answers.</p>142 </div>143 </div>144 145 <!-- Formula Banner -->146 <div class="bg-indigo-500/5 border border-indigo-500/20 rounded-2xl p-8 text-center shadow-[0_4px_40px_rgba(99,102,241,0.05)] mb-14 relative overflow-hidden">147 <div class="text-sm font-bold text-[var(--text-main)] uppercase tracking-wider mb-6">🧮 Combined Score Function</div>148 <div class="inline-block bg-black/30 border border-[var(--glass-border)] shadow-2xl rounded-2xl px-10 py-6 font-mono text-xl font-bold text-[var(--text-main)] mb-4">149 Score(d) = α · Rel(q, d) + β · Cov(d | S) + γ · Sup(d, S)150 </div>151 <p class="text-[var(--text-muted)] font-medium mt-2 text-sm leading-relaxed">152 <span class="text-indigo-400 font-bold">α·Rel</span> — semantic similarity | 153 <span class="text-emerald-400 font-bold">β·Cov</span> — marginal coverage gain | 154 <span class="text-amber-400 font-bold">γ·Sup</span> — agreement155 </p>156 </div>157 158 <!-- Greedy Algorithm Waterfall -->159 <h2 class="text-2xl font-bold text-[var(--text-main)] mb-2 border-l-4 border-amber-500 pl-3">⚙️ The Greedy Selection Algorithm</h2>160 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">How we efficiently find the optimal document set — step by step</p>161 162 <div class="grid md:grid-cols-2 gap-6 mb-14">163 <div class="doc-card p-5 relative overflow-hidden hover:translate-x-2 transition border-transparent hover:border-indigo-400 hover:shadow-[0_0_24px_rgba(99,102,241,0.1)] group">164 <div class="inline-flex w-8 h-8 rounded-full bg-indigo-500/20 text-indigo-400 font-bold items-center justify-center mb-3">1</div>165 <div class="font-bold text-[var(--text-main)] mb-1">Candidate Retrieval</div>166 <div class="text-sm text-[var(--text-muted)] leading-relaxed">Retrieve top N=30 candidates using dense cosine similarity from ChromaDB. Larger pool gives the optimizer more choices.</div>167 </div>168 <div class="doc-card p-5 relative overflow-hidden hover:translate-x-2 transition border-transparent hover:border-violet-400 hover:shadow-[0_0_24px_rgba(139,92,246,0.1)] group">169 <div class="inline-flex w-8 h-8 rounded-full bg-violet-500/20 text-violet-400 font-bold items-center justify-center mb-3">2</div>170 <div class="font-bold text-[var(--text-main)] mb-1">Initialize Empty Set</div>171 <div class="text-sm text-[var(--text-muted)] leading-relaxed">Start with S = ∅. Coverage defaults to 1.0, support to 0.0 for the first pick.</div>172 </div>173 <div class="doc-card p-5 relative overflow-hidden hover:translate-x-2 transition border-transparent hover:border-sky-400 hover:shadow-[0_0_24px_rgba(14,165,233,0.1)] group">174 <div class="inline-flex w-8 h-8 rounded-full bg-sky-500/20 text-sky-400 font-bold items-center justify-center mb-3">3</div>175 <div class="font-bold text-[var(--text-main)] mb-1">Score All Remaining</div>176 <div class="text-sm text-[var(--text-muted)] leading-relaxed">For each candidate d ∉ S, compute Score(d). Coverage and support are dynamically recalculated against S.</div>177 </div>178 <div class="doc-card p-5 relative overflow-hidden hover:translate-x-2 transition border-transparent hover:border-emerald-400 hover:shadow-[0_0_24px_rgba(16,185,129,0.1)] group">179 <div class="inline-flex w-8 h-8 rounded-full bg-emerald-500/20 text-emerald-400 font-bold items-center justify-center mb-3">4</div>180 <div class="font-bold text-[var(--text-main)] mb-1">Select the Best</div>181 <div class="text-sm text-[var(--text-muted)] leading-relaxed">Pick the document d* with the highest combined score and add to S. Greedily maximizes marginal gain at each step.</div>182 </div>183 <div class="doc-card p-5 relative overflow-hidden hover:translate-x-2 transition border-transparent hover:border-amber-400 hover:shadow-[0_0_24px_rgba(245,158,11,0.1)] group">184 <div class="inline-flex w-8 h-8 rounded-full bg-amber-500/20 text-amber-400 font-bold items-center justify-center mb-3">5</div>185 <div class="font-bold text-[var(--text-main)] mb-1">Repeat Until |S| = k</div>186 <div class="text-sm text-[var(--text-muted)] leading-relaxed">Loop steps 3–4 until we've selected k documents. Each iteration accounts for what's already been chosen.</div>187 </div>188 <div class="doc-card p-5 relative overflow-hidden hover:translate-x-2 transition border-transparent hover:border-red-400 hover:shadow-[0_0_24px_rgba(239,68,68,0.1)] group">189 <div class="inline-flex w-8 h-8 rounded-full bg-red-500/20 text-red-400 font-bold items-center justify-center mb-3">6</div>190 <div class="font-bold text-[var(--text-main)] mb-1">Return Optimized Set</div>191 <div class="text-sm text-[var(--text-muted)] leading-relaxed">Pass the selected set S to the reranker → LLM for answer generation. Downstream pipeline is completely unharmed.</div>192 </div>193 </div>194 195 196 <!-- CODE UNDER THE HOOD -->197 <h2 class="text-2xl font-bold text-[var(--text-main)] mt-14 mb-2 border-l-4 border-pink-500 pl-3">💻 Code Under The Hood</h2>198 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">The actual Python implementation — scoring functions, greedy loop, and retriever integration</p>199 200 <div class="code-tabs mb-6">201 <div class="code-tab active" onclick="switchTab(0)">📐 Scoring Functions</div>202 <div class="code-tab" onclick="switchTab(1)">🔄 Greedy Optimizer</div>203 <div class="code-tab" onclick="switchTab(2)">🔧 Retriever (Before → After)</div>204 <div class="code-tab" onclick="switchTab(3)">⚙️ Configuration</div>205 </div>206 207 <!-- Panel 0: Scoring Functions -->208 <div class="code-panel active" id="panel-0">209 <div class="bg-black/30 border border-indigo-500/20 rounded-xl p-5 mb-4">210 <p class="text-indigo-400 text-[11px] font-bold tracking-widest uppercase mb-1">📄 app/services/optimizer.py — Scoring Functions</p>211 <p class="text-[var(--text-muted)] text-sm mb-6">Each function computes one dimension of the multi-objective score. All use cosine similarity as the base metric.</p>212 <pre><code class="language-python">def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:213 """Compute cosine similarity between two vectors."""214 norm_a = np.linalg.norm(a)215 norm_b = np.linalg.norm(b)216 if norm_a == 0 or norm_b == 0:217 return 0.0218 return float(np.dot(a, b) / (norm_a * norm_b))219 220def compute_relevance(query_emb, doc_emb) -> float:221 """Rel(q, d) = cosine_similarity(q, d)"""222 return cosine_similarity(query_emb, doc_emb)223 224def compute_coverage(doc_emb, selected_embs) -> float:225 """Cov(d | S) = 1 - max(similarity(d, s)) for all s in S."""226 if not selected_embs:227 return 1.0 # First doc always gets full coverage228 max_sim = max(cosine_similarity(doc_emb, s) for s in selected_embs)229 return 1.0 - max_sim230 231def compute_support(doc_emb, selected_embs) -> float:232 """Sup(d, S) = mean(similarity(d, s)) for all s in S."""233 if not selected_embs:234 return 0.0235 total_sim = sum(cosine_similarity(doc_emb, s) for s in selected_embs)236 return total_sim / len(selected_embs)</code></pre>237 </div>238 <div class="grid grid-cols-3 gap-4">239 <div class="bg-indigo-500/5 border border-indigo-500/20 rounded-xl p-3 text-center">240 <p class="text-indigo-400 font-bold text-sm mb-1">Relevance</p>241 <p class="text-[var(--text-muted)] text-[11px]">Range: [-1, 1]<br>Higher = more relevant</p>242 </div>243 <div class="bg-emerald-500/5 border border-emerald-500/20 rounded-xl p-3 text-center">244 <p class="text-emerald-400 font-bold text-sm mb-1">Coverage</p>245 <p class="text-[var(--text-muted)] text-[11px]">Range: [0, 1]<br>Higher = more unique info</p>246 </div>247 <div class="bg-amber-500/5 border border-amber-500/20 rounded-xl p-3 text-center">248 <p class="text-amber-400 font-bold text-sm mb-1">Support</p>249 <p class="text-[var(--text-muted)] text-[11px]">Range: [0, 1]<br>Higher = more corroborated</p>250 </div>251 </div>252 </div>253 254 <!-- Panel 1: Greedy Loop -->255 <div class="code-panel" id="panel-1">256 <div class="bg-black/30 border border-emerald-500/20 rounded-xl p-5 mb-4">257 <p class="text-emerald-400 text-[11px] font-bold tracking-widest uppercase mb-1">📄 app/services/optimizer.py — Greedy Loop</p>258 <p class="text-[var(--text-muted)] text-sm mb-6">The core algorithm: iteratively picks the document with the <strong class="text-[var(--text-main)]">highest combined score</strong> considering what's already been selected. This is the key difference from top-k.</p>259 <pre><code class="language-python">def optimize_selection(query_embedding, candidates, k, alpha=0.5, beta=0.3, gamma=0.2):260 """261 Greedy multi-objective document selection.262 Selects k documents maximizing: α·Rel + β·Cov + γ·Sup263 """264 query_emb = np.asarray(query_embedding, dtype=np.float32)265 candidate_embs = [np.asarray(c["embedding"]) for c in candidates]266 267 relevance_scores = [268 compute_relevance(query_emb, emb) for emb in candidate_embs269 ]270 271 selected = []272 selected_embs = []273 remaining = list(range(len(candidates)))274 275 while len(selected) < k and remaining:276 best_score = -float("inf")277 best_idx = -1278 279 for idx in remaining:280 # ── THE KEY DIFFERENCE FROM TOP-K ──281 # Coverage & support change at every iteration dynamically282 rel = relevance_scores[idx] 283 cov = compute_coverage(candidate_embs[idx], selected_embs)284 sup = compute_support(candidate_embs[idx], selected_embs)285 286 score = alpha * rel + beta * cov + gamma * sup287 288 if score > best_score:289 best_score = score290 best_idx = idx291 292 doc = candidates[best_idx].copy()293 doc["opt_score"] = best_score294 doc.pop("embedding", None)295 296 selected.append(doc)297 selected_embs.append(candidate_embs[best_idx])298 remaining.remove(best_idx)299 300 return selected # ← Replaces naive top-k results</code></pre>301 </div>302 <div class="bg-emerald-500/5 border border-emerald-500/20 rounded-xl p-4">303 <p class="text-emerald-400 font-bold text-sm mb-2">💡 Why This Works</p>304 <p class="text-[var(--text-muted)] text-xs leading-relaxed">305 <strong class="text-[var(--text-main)]">Iteration 1:</strong> Coverage = 1.0 for all, Support = 0.0 → picks the most relevant doc (like top-k).<br>306 <strong class="text-[var(--text-main)]">Iteration 2+:</strong> Near-duplicates get Coverage ≈ 0 (penalized), so diverse docs with moderate relevance win.<br>307 <strong class="text-[var(--text-main)]">Result:</strong> A balanced set that covers more ground while staying relevant to the query.308 </p>309 </div>310 </div>311 312 <!-- Panel 2: Retriever -->313 <div class="code-panel" id="panel-2">314 <div class="bg-black/30 border border-amber-500/20 rounded-xl p-5 mb-4">315 <p class="text-amber-400 text-[11px] font-bold tracking-widest uppercase mb-1">📄 app/services/retriever.py — Before vs After</p>316 <p class="text-[var(--text-muted)] text-sm mb-6">The retriever was modified to use the optimizer when enabled, while keeping the original top-k path intact as a <strong class="text-[var(--text-main)]">fallback option</strong>.</p>317 <div class="grid md:grid-cols-2 gap-4">318 <div>319 <p class="text-red-400 text-xs font-bold tracking-widest uppercase mb-2">❌ Before — retriever.py</p>320 <pre><code class="language-python">def retrieve(query, top_k=None, filters=None):321 top_k = top_k or settings.retrieval_top_k322 store = get_vector_store()323 324 # Dense search — fixed small pool325 query_emb = embed_query(query)326 dense_results = store.search(327 query_emb, top_k=top_k328 )329 330 # Optional BM25 hybrid331 if settings.enable_hybrid_search:332 sparse = _bm25_search(query, dense_results)333 results = _merge_results(dense_results, sparse)334 else:335 results = dense_results336 337 # Just slice top-k — no optimization!338 return results[:top_k]</code></pre>339 </div>340 <div>341 <p class="text-emerald-400 text-xs font-bold tracking-widest uppercase mb-2">✅ After — retriever.py</p>342 <pre><code class="language-python">def retrieve(query, top_k=None, filters=None):343 top_k = top_k or settings.retrieval_top_k344 store = get_vector_store()345 query_emb = embed_query(query)346 347 if settings.enable_optimizer:348 # Larger candidate pool for optimization349 dense_results = store.search_with_embeddings(350 query_emb, top_k=settings.optimizer_candidate_n351 )352 else:353 dense_results = store.search(query_emb, top_k=top_k)354 355 # Optional BM25 hybrid356 if settings.enable_hybrid_search:357 sparse = _bm25_search(query, dense_results)358 results = _merge_results(dense_results, sparse)359 else:360 results = dense_results361 362 # NEW: Optimization-based selection363 if settings.enable_optimizer:364 from app.services.optimizer import optimize_selection365 return optimize_selection(366 query_emb, results, k=top_k,367 alpha=settings.optimizer_alpha,368 beta=settings.optimizer_beta,369 gamma=settings.optimizer_gamma,370 )371 else:372 return results[:top_k]</code></pre>373 </div>374 </div>375 </div>376 <div class="grid md:grid-cols-2 gap-4">377 <div class="bg-red-500/5 border border-red-500/10 rounded-xl p-3">378 <p class="text-red-400 font-bold text-sm mb-1">Before: 2 steps</p>379 <p class="text-[var(--text-muted)] text-[11px]">Search → Slice top-k<br><em>No inter-document awareness</em></p>380 </div>381 <div class="bg-emerald-500/5 border border-emerald-500/10 rounded-xl p-3">382 <p class="text-emerald-400 font-bold text-sm mb-1">After: 3 steps</p>383 <p class="text-[var(--text-muted)] text-[11px]">Search (N=30) → Optimize → Return k<br><em>Each pick considers the whole set</em></p>384 </div>385 </div>386 </div>387 388 <!-- Panel 3: Configuration -->389 <div class="code-panel" id="panel-3">390 <div class="bg-black/30 border border-violet-500/20 rounded-xl p-5 mb-4">391 <p class="text-violet-400 text-[11px] font-bold tracking-widest uppercase mb-1">📄 app/core/config.py & .env</p>392 <p class="text-[var(--text-muted)] text-sm mb-6">All optimizer parameters are configurable natively via environment variables.</p>393 394 <p class="text-violet-400 text-xs font-bold tracking-widest uppercase mb-2">config.py Settings Object</p>395 <pre><code class="language-python">class Settings(BaseSettings):396 # ── Optimizer (NEW) ────────────────────────────────397 enable_optimizer: bool = True # Toggle on/off398 optimizer_candidate_n: int = 30 # Pool size (N)399 optimizer_alpha: float = 0.5 # α — Relevance weight400 optimizer_beta: float = 0.3 # β — Coverage weight401 optimizer_gamma: float = 0.2 # γ — Support weight</code></pre>402 403 <p class="text-violet-400 text-xs font-bold tracking-widest uppercase mt-6 mb-2">.env Overrides</p>404 <pre><code class="language-bash"># Change without reloading405ENABLE_OPTIMIZER=true406OPTIMIZER_CANDIDATE_N=30407OPTIMIZER_ALPHA=0.5408OPTIMIZER_BETA=0.3409OPTIMIZER_GAMMA=0.2</code></pre>410 </div>411 412 <div class="bg-violet-500/5 border border-violet-500/20 rounded-xl p-4">413 <p class="text-violet-400 font-bold text-sm mb-2">🎛️ Tuning Guide</p>414 <p class="text-[var(--text-muted)] text-xs leading-relaxed">415 <strong class="text-[var(--text-main)]">High α (e.g. 0.7):</strong> Prioritize relevance — behaves closer to top-k.<br>416 <strong class="text-[var(--text-main)]">High β (e.g. 0.5):</strong> Prioritize diversity — prevents redundant chunks.<br>417 <strong class="text-[var(--text-main)]">High γ (e.g. 0.4):</strong> Prioritize agreement — good for fact-checking queries.<br>418 <strong class="text-[var(--text-main)]">Constraint:</strong> α + β + γ = 1.0 is recommended but not enforced.419 </p>420 </div>421 </div>422 423 <!-- Interactive Simulation Using Plotly.js -->424 <h2 class="text-2xl font-bold text-[var(--text-main)] mt-14 mb-2 border-l-4 border-sky-500 pl-3">📊 Top-K vs Optimized — Visual Comparison</h2>425 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">How the optimizer selects a diverse, balanced document set compared to naive top-k</p>426 427 <div class="grid md:grid-cols-2 gap-8 mb-8">428 <div class="p-6 rounded-2xl bg-[var(--glass-bg)] border border-[var(--glass-border)] shadow-xl relative">429 <div id="chart-topk" style="width:100%; height:320px;"></div>430 </div>431 <div class="p-6 rounded-2xl bg-[var(--glass-bg)] border border-[var(--glass-border)] shadow-xl relative">432 <div id="chart-opt" style="width:100%; height:320px;"></div>433 </div>434 </div>435 436 <!-- Per-Query Real Results -->437 <div class="p-6 rounded-2xl bg-[var(--glass-bg)] border border-[var(--glass-border)] shadow-xl mb-14">438 <div id="chart-perquery" style="width:100%; height:380px;"></div>439 </div>440 441 <!-- Impact on Answer Quality Radar Using Plotly.js -->442 <h2 class="text-2xl font-bold text-[var(--text-main)] mt-14 mb-2 border-l-4 border-teal-500 pl-3">📈 Impact on Answer Quality — Measured Results</h2>443 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">Real metrics from evaluation across 8 queries against 283 indexed chunks</p>444 445 <div class="grid md:grid-cols-2 gap-8 mb-14 items-center">446 <div class="p-6 rounded-2xl bg-[var(--glass-bg)] border border-[var(--glass-border)] shadow-xl">447 <div id="chart-radar" style="width:100%; height:400px;"></div>448 </div>449 <div class="p-6">450 <div class="doc-card p-4 transition-transform hover:-translate-y-1 mb-4">451 <p class="font-bold text-[var(--text-main)] mb-1">🟢 Evidence Diversity — 22.5% <span class="text-emerald-400 text-sm font-semibold">(+2.6% vs Top-K)</span></p>452 <p class="text-sm text-[var(--text-muted)]">Measured as (1 − mean pairwise cosine similarity) × 100. The optimizer consistently selects more diverse chunks from the candidate pool.</p>453 </div>454 <div class="doc-card p-4 transition-transform hover:-translate-y-1 mb-4">455 <p class="font-bold text-[var(--text-main)] mb-1">🟢 Redundancy Reduction — 77.5%</p>456 <p class="text-sm text-[var(--text-muted)]">77.5% of near-duplicate pairs (cosine similarity > 0.85) eliminated compared to standard top-k selection.</p>457 </div>458 <div class="doc-card p-4 transition-transform hover:-translate-y-1 mb-4">459 <p class="font-bold text-[var(--text-main)] mb-1">🟢 Hallucination Resistance — 81.2% <span class="text-emerald-400 text-sm font-semibold">(+24.2% vs Top-K)</span></p>460 <p class="text-sm text-[var(--text-muted)]">LLM-as-judge faithfulness scoring. Optimized retrieval grounds 81% of generated claims in source context vs 57% for top-k.</p>461 </div>462 <div class="doc-card p-4 transition-transform hover:-translate-y-1 mb-4">463 <p class="font-bold text-[var(--text-main)] mb-1">🟢 Answer Reliability — 63.8%</p>464 <p class="text-sm text-[var(--text-muted)]">LLM-as-judge completeness scoring. Comparable to top-k baseline (66.2%), with tradeoff between breadth of coverage and depth of focus.</p>465 </div>466 </div>467 </div>468 469 <!-- Output Comparison -->470 <h2 class="text-2xl font-bold text-[var(--text-main)] mt-14 mb-2 border-l-4 border-cyan-500 pl-3">🧪 Empirical Output Comparison — Baseline vs. Optimized Grounding</h2>471 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">Evaluating the syntactical generation delta across equivalent retrieval pools</p>472 473 <div class="grid md:grid-cols-2 gap-8 mb-8">474 <div class="p-6 rounded-2xl bg-red-500/5 border border-red-500/20 shadow-[0_4px_30px_rgba(239,68,68,0.08)]">475 <div class="inline-flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest bg-red-500/15 text-red-400 px-3 py-1 rounded-full mb-4">❌ Before Optimization (Top-K)</div>476 <p class="text-xs text-[var(--text-muted)] mb-4"><strong class="text-[var(--text-main)]">Query:</strong> "What are the main causes of LLM hallucinations, and how can teams reduce them in production?"</p>477 <div class="doc-card p-4 mb-4 border border-red-500/20 bg-black/20">478 <p class="text-[11px] font-bold uppercase tracking-wider text-red-400 mb-2">Generated Answer</p>479 <p class="text-sm text-[var(--text-muted)] leading-relaxed">LLM hallucinations happen when models guess missing facts, especially with ambiguous prompts and weak context. Teams can reduce this by giving better prompts, setting lower temperature, and adding more checks. In general, better data and model tuning help.</p>480 </div>481 <div class="doc-card p-4 border border-red-500/20 bg-black/20">482 <p class="text-[11px] font-bold uppercase tracking-wider text-red-400 mb-2">Observed Issues</p>483 <ul class="text-sm text-[var(--text-muted)] space-y-2 list-disc pl-5">484 <li>Repeats similarity-focused points from near-duplicate chunks</li>485 <li>Stays high-level and skips concrete production controls</li>486 <li>No structured mitigation plan for real system pipelines</li>487 </ul>488 </div>489 </div>490 491 <div class="p-6 rounded-2xl bg-emerald-500/5 border border-emerald-500/20 shadow-[0_4px_30px_rgba(16,185,129,0.08)]">492 <div class="inline-flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest bg-emerald-500/15 text-emerald-400 px-3 py-1 rounded-full mb-4">✅ After Optimization (Multi-Objective)</div>493 <p class="text-xs text-[var(--text-muted)] mb-4"><strong class="text-[var(--text-main)]">Query:</strong> "What are the main causes of LLM hallucinations, and how can teams reduce them in production?"</p>494 <div class="doc-card p-4 mb-4 border border-emerald-500/20 bg-black/20">495 <p class="text-[11px] font-bold uppercase tracking-wider text-emerald-400 mb-2">Generated Answer</p>496 <p class="text-sm text-[var(--text-muted)] leading-relaxed">Common hallucination causes in LLM systems include weak retrieval quality, missing source citations, stale knowledge, prompt ambiguity, and over-confident decoding. A practical mitigation stack is: <strong class="text-[var(--text-main)]">(1)</strong> retrieval with source attribution, <strong class="text-[var(--text-main)]">(2)</strong> strict grounding prompts, <strong class="text-[var(--text-main)]">(3)</strong> lower-temperature decoding for factual tasks, <strong class="text-[var(--text-main)]">(4)</strong> post-generation verification checks, and <strong class="text-[var(--text-main)]">(5)</strong> human review for high-risk outputs. In production, combine these with evaluation sets and continuous monitoring of factual error rates.</p>497 </div>498 <div class="doc-card p-4 border border-emerald-500/20 bg-black/20">499 <p class="text-[11px] font-bold uppercase tracking-wider text-emerald-400 mb-2">Observed Improvements</p>500 <ul class="text-sm text-[var(--text-muted)] space-y-2 list-disc pl-5">501 <li>Lists concrete causes and actionable safeguards</li>502 <li>Provides a production-ready, ordered mitigation workflow</li>503 <li>Improves factual clarity and operational usefulness</li>504 </ul>505 </div>506 </div>507 </div>508 509 <div class="overflow-x-auto mb-14">510 <div class="min-w-[640px] rounded-2xl border border-[var(--glass-border)] bg-[var(--glass-bg)] shadow-xl">511 <div class="grid grid-cols-4 text-xs font-bold uppercase tracking-widest border-b border-[var(--glass-border)]">512 <div class="p-4 text-[var(--text-main)]">Quality Signal</div>513 <div class="p-4 text-red-400">Before</div>514 <div class="p-4 text-emerald-400">After</div>515 <div class="p-4 text-cyan-400">Delta</div>516 </div>517 <div class="grid grid-cols-4 text-sm border-b border-[var(--glass-border)]">518 <div class="p-4 text-[var(--text-main)] font-semibold">Coverage of key concepts</div>519 <div class="p-4 text-[var(--text-muted)]">2 / 5</div>520 <div class="p-4 text-[var(--text-muted)]">5 / 5</div>521 <div class="p-4 text-emerald-400 font-semibold">+3</div>522 </div>523 <div class="grid grid-cols-4 text-sm border-b border-[var(--glass-border)]">524 <div class="p-4 text-[var(--text-main)] font-semibold">Redundant statements</div>525 <div class="p-4 text-[var(--text-muted)]">High</div>526 <div class="p-4 text-[var(--text-muted)]">Low</div>527 <div class="p-4 text-emerald-400 font-semibold">Improved</div>528 </div>529 <div class="grid grid-cols-4 text-sm border-b border-[var(--glass-border)]">530 <div class="p-4 text-[var(--text-main)] font-semibold">Step-by-step clarity</div>531 <div class="p-4 text-[var(--text-muted)]">Medium</div>532 <div class="p-4 text-[var(--text-muted)]">High</div>533 <div class="p-4 text-emerald-400 font-semibold">+1 level</div>534 </div>535 <div class="grid grid-cols-4 text-sm">536 <div class="p-4 text-[var(--text-main)] font-semibold">Grounding confidence</div>537 <div class="p-4 text-[var(--text-muted)]">57%</div>538 <div class="p-4 text-[var(--text-muted)]">81%</div>539 <div class="p-4 text-emerald-400 font-semibold">+24%</div>540 </div>541 </div>542 </div>543 544 <!-- Evaluation Methodology -->545 <h2 class="text-2xl font-bold text-[var(--text-main)] mt-14 mb-2 border-l-4 border-sky-500 pl-3">🔬 Evaluation Methodology</h2>546 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">How each metric was measured — reproducible with evaluate_quality.py</p>547 548 <div class="overflow-x-auto mb-8">549 <div class="min-w-[640px] rounded-2xl border border-[var(--glass-border)] bg-[var(--glass-bg)] shadow-xl">550 <div class="grid grid-cols-4 text-xs font-bold uppercase tracking-widest border-b border-[var(--glass-border)]">551 <div class="p-4 text-[var(--text-main)]">Metric</div>552 <div class="p-4 text-indigo-400">Method</div>553 <div class="p-4 text-emerald-400">Formula / Tool</div>554 <div class="p-4 text-cyan-400">Cost</div>555 </div>556 <div class="grid grid-cols-4 text-sm border-b border-[var(--glass-border)]">557 <div class="p-4 text-[var(--text-main)] font-semibold">Evidence Diversity</div>558 <div class="p-4 text-[var(--text-muted)]">Embedding Math</div>559 <div class="p-4 text-[var(--text-muted)] font-mono text-xs">(1 − mean pairwise cos sim) × 100</div>560 <div class="p-4 text-emerald-400 font-semibold">Free</div>561 </div>562 <div class="grid grid-cols-4 text-sm border-b border-[var(--glass-border)]">563 <div class="p-4 text-[var(--text-main)] font-semibold">Redundancy Reduction</div>564 <div class="p-4 text-[var(--text-muted)]">Embedding Math</div>565 <div class="p-4 text-[var(--text-muted)] font-mono text-xs">(topk_dupes − opt_dupes) / topk_dupes</div>566 <div class="p-4 text-emerald-400 font-semibold">Free</div>567 </div>568 <div class="grid grid-cols-4 text-sm border-b border-[var(--glass-border)]">569 <div class="p-4 text-[var(--text-main)] font-semibold">Hallucination Resistance</div>570 <div class="p-4 text-[var(--text-muted)]">LLM-as-Judge</div>571 <div class="p-4 text-[var(--text-muted)] font-mono text-xs">Faithfulness score (0–100)</div>572 <div class="p-4 text-amber-400 font-semibold">LLM API</div>573 </div>574 <div class="grid grid-cols-4 text-sm">575 <div class="p-4 text-[var(--text-main)] font-semibold">Answer Reliability</div>576 <div class="p-4 text-[var(--text-muted)]">LLM-as-Judge</div>577 <div class="p-4 text-[var(--text-muted)] font-mono text-xs">Completeness score (0–100)</div>578 <div class="p-4 text-amber-400 font-semibold">LLM API</div>579 </div>580 </div>581 </div>582 583 <div class="bg-sky-500/5 border border-sky-500/20 rounded-2xl p-6 mb-14">584 <p class="text-sky-400 font-bold text-sm mb-2">🔄 Reproduce These Results</p>585 <p class="text-[var(--text-muted)] text-sm leading-relaxed">586 All metrics are generated by <strong class="text-[var(--text-main)] font-mono">tests/evaluate_quality.py</strong>. Run it against your own data:587 </p>588 <pre class="mt-3"><code class="language-bash">python tests/evaluate_quality.py # Full run (all 4 metrics)589python tests/evaluate_quality.py --skip-llm # Embedding metrics only (free)590python tests/evaluate_quality.py --max-queries 3 # Quick test with 3 queries</code></pre>591 </div>592 593 <!-- Pipeline Before After -->594 <h2 class="text-2xl font-bold text-[var(--text-main)] mt-14 mb-2 border-l-4 border-violet-500 pl-3">🔀 Pipeline Before & After</h2>595 <p class="text-[var(--text-muted)] mb-8 text-sm uppercase tracking-wider font-semibold">How the retrieval pipeline changes with optimization</p>596 597 <div class="grid md:grid-cols-2 gap-8 mb-14">598 <div class="p-6 rounded-2xl bg-[var(--glass-bg)] border border-red-500/20 shadow-[0_4px_30px_rgba(0,0,0,0.02)]">599 <div class="text-xs font-extrabold text-red-500 uppercase tracking-widest mb-6 text-center bg-red-500/10 py-2 rounded-lg">BEFORE — Standard Top-K</div>600 <div class="space-y-4">601 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-red-500/10">1. ❓ User Query</div>602 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>603 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-red-500/10">2. 🔢 Embed Query</div>604 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>605 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-red-500/10">3. 🔎 Cosine Search (top-k=10)</div>606 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>607 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-red-500/10">4. 🔀 BM25 Hybrid Fusion</div>608 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>609 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-red-500/10">5. ⚡ CrossEncoder Rerank</div>610 <div class="text-center text-red-500 text-xl font-black">↓</div>611 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-red-400 bg-red-500/10 border-red-500/30 shadow-xl">6. 🤖 LLM Generates (High Redundancy)</div>612 </div>613 </div>614 <div class="p-6 rounded-2xl bg-[var(--glass-bg)] border border-emerald-500/20 shadow-[0_4px_30px_rgba(0,0,0,0.02)] border-t-4 border-t-emerald-500">615 <div class="text-xs font-extrabold text-emerald-400 uppercase tracking-widest mb-6 text-center bg-emerald-500/10 py-2 rounded-lg">AFTER — Optimized Selection</div>616 <div class="space-y-4">617 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-emerald-500/10">1. ❓ User Query</div>618 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>619 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-emerald-500/10">2. 🔢 Embed Query</div>620 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>621 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-emerald-500/10">3. 🔎 Cosine Search <span class="text-emerald-400">(N=30 pool)</span></div>622 <div class="text-center text-[var(--text-muted)] text-xl font-black">↓</div>623 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-emerald-500/10">4. 🔀 BM25 Hybrid Fusion</div>624 <div class="text-center text-emerald-500 text-xl font-black">↓</div>625 <div class="doc-card py-4 px-4 text-center text-sm font-bold text-emerald-400 bg-emerald-500/10 border-emerald-500/50 scale-105 shadow-[0_0_20px_rgba(16,185,129,0.3)]">5. 🧬 Multi-Objective Optimizer <span class="bg-emerald-500/30 text-white rounded px-2 py-0.5 ml-2 text-[10px]">NEW</span></div>626 <div class="text-center text-emerald-500 text-xl font-black">↓</div>627 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-[var(--text-main)] border-emerald-500/10">6. ⚡ CrossEncoder Rerank Validation</div>628 <div class="text-center text-emerald-500 text-xl font-black">↓</div>629 <div class="doc-card py-3 px-4 text-center text-sm font-bold text-emerald-400 bg-emerald-500/5 border-emerald-500/20 shadow-xl">7. 🤖 LLM Generates (Diverse Coverage)</div>630 </div>631 </div>632 </div>633 634 635 <div class="bg-emerald-500/5 border border-emerald-500/20 rounded-2xl p-8 text-center shadow-[0_4px_40px_rgba(16,185,129,0.05)] mx-auto">636 <p class="text-[var(--text-muted)] font-medium max-w-2xl mx-auto text-sm leading-relaxed mb-4 tracking-widest uppercase">🎯 Optimization Objective</p>637 <div class="inline-block bg-black/30 border border-[var(--glass-border)] shadow-2xl rounded-2xl px-10 py-6 font-mono text-xl font-bold text-[var(--text-main)] mb-4 transition-transform hover:scale-105">638 S* = argmax Σ [ α·Rel(q,dᵢ) + β·C(dᵢ|S) + γ·Sup(dᵢ,S) ]639 </div>640 </div>641 </div>642 643 <script>644 // Tab Switching Logic645 function switchTab(index) {646 // Find all tabs and panels647 const tabs = document.querySelectorAll('.code-tab');648 const panels = document.querySelectorAll('.code-panel');649 650 // Remove active class from all651 tabs.forEach(t => t.classList.remove('active'));652 panels.forEach(p => p.classList.remove('active'));653 654 // Add active class to target655 if(tabs[index]) tabs[index].classList.add('active');656 if(document.getElementById('panel-' + index)) {657 document.getElementById('panel-' + index).classList.add('active');658 }659 }660 661 // PLOTLY JS INTEGRATION662 function renderPlots() {663 const txtColor = '#8a8f98';664 const gridColor = 'rgba(255,255,255,0.04)';665 const bgColor = 'rgba(0,0,0,0)';666 const fontFam = 'Inter, sans-serif';667 668 // Simulated doc relevance scores for visual demonstration669 const n_docs = 15;670 const relevance = [0.95, 0.93, 0.91, 0.90, 0.88, 0.86, 0.84, 0.82, 0.80, 0.78, 0.75, 0.72, 0.70, 0.68, 0.65];671 const xVals = Array.from({length: n_docs}, (_, i) => `D${i+1}`);672 const hoverTemp = "<b>Doc %{x}</b><br>Relevance: %{y:.2f}<extra></extra>";673 674 // === TOP-k CHART ===675 const topk_sel = [0, 1, 2, 3, 4];676 const colors_topk = Array(n_docs).fill('rgba(100,116,139,0.2)');677 const borders_topk = Array(n_docs).fill('rgba(100,116,139,0.15)');678 topk_sel.forEach(i => { colors_topk[i] = 'rgba(239,68,68,0.85)'; borders_topk[i] = '#ef4444'; });679 680 const traceTopk = {681 x: xVals, y: relevance, type: 'bar',682 marker: { color: colors_topk, line: { color: borders_topk, width: 2 } },683 text: relevance.map(v => v.toFixed(2)), textposition: 'outside', textfont: { color: txtColor, size: 10, family: fontFam },684 hovertemplate: hoverTemp685 };686 687 const layoutTopk = {688 title: { text: "❌ Top-K Selection", font: {size: 16, color: '#f87171', family: fontFam} },689 paper_bgcolor: bgColor, plot_bgcolor: bgColor, font: {family: fontFam, color: txtColor, size: 11},690 margin: {l:10, r:10, t:50, b:20}, showlegend: false,691 xaxis: { showgrid: false, tickfont: {size: 10} },692 yaxis: { title: "Relevance Score", gridcolor: gridColor, range: [0, 1.1] },693 annotations: [{ text: "⚠️ Picks top-5 by absolute score — highly redundant clumps", xref: "paper", yref: "paper", x: 0.5, y: -0.15, showarrow: false, font: {size: 11, color: '#f87171'} }]694 };695 Plotly.newPlot('chart-topk', [traceTopk], layoutTopk, {displayModeBar: false, responsive: true});696 697 698 // === OPTIMIZED CHART ===699 const opt_sel = [0, 3, 6, 10, 13];700 const colors_opt = Array(n_docs).fill('rgba(100,116,139,0.2)');701 const borders_opt = Array(n_docs).fill('rgba(100,116,139,0.15)');702 opt_sel.forEach(i => { colors_opt[i] = 'rgba(16,185,129,0.85)'; borders_opt[i] = '#10b981'; });703 704 const traceOpt = {705 x: xVals, y: relevance, type: 'bar',706 marker: { color: colors_opt, line: { color: borders_opt, width: 2 } },707 text: relevance.map(v => v.toFixed(2)), textposition: 'outside', textfont: { color: txtColor, size: 10, family: fontFam },708 hovertemplate: hoverTemp709 };710 711 const layoutOpt = {712 title: { text: "✅ Optimized Selection", font: {size: 16, color: '#34d399', family: fontFam} },713 paper_bgcolor: bgColor, plot_bgcolor: bgColor, font: {family: fontFam, color: txtColor, size: 11},714 margin: {l:10, r:10, t:50, b:20}, showlegend: false,715 xaxis: { showgrid: false, tickfont: {size: 10} },716 yaxis: { title: "Relevance Score", gridcolor: gridColor, range: [0, 1.1] },717 annotations: [{ text: "✔️ Picks 5 spread dynamically — maximizes subset coverage", xref: "paper", yref: "paper", x: 0.5, y: -0.15, showarrow: false, font: {size: 11, color: '#34d399'} }]718 };719 Plotly.newPlot('chart-opt', [traceOpt], layoutOpt, {displayModeBar: false, responsive: true});720 721 722 // === RADAR CHART (Real Measured Data) ===723 const categories = ["Evidence<br>Diversity", "Redundancy<br>Reduction", "Answer<br>Reliability", "Hallucination<br>Resistance"];724 const catsCircular = [...categories, categories[0]];725 const opt_vals = [22.5, 77.5, 63.8, 81.2, 22.5];726 const topk_vals = [19.9, 0, 66.2, 57.0, 19.9];727 728 const polarGridColor = 'rgba(255,255,255,0.06)';729 730 const traceRadarOpt = {731 type: 'scatterpolar', r: opt_vals, theta: catsCircular, fill: 'toself',732 name: 'ContextAware (Optimized)', fillcolor: 'rgba(16,185,129,0.2)',733 line: {color: '#10b981', width: 2.5}, marker: {size: 7, color: '#10b981'}734 };735 const traceRadarTopk = {736 type: 'scatterpolar', r: topk_vals, theta: catsCircular, fill: 'toself',737 name: 'Standard Top-K RAG', fillcolor: 'rgba(100,116,139,0.1)',738 line: {color: '#555', width: 2, dash: 'dash'}, marker: {size: 6, color: '#555'}739 };740 741 const layoutRadar = {742 polar: {743 bgcolor: bgColor,744 radialaxis: { visible: true, range: [0, 100], gridcolor: polarGridColor, tickfont: {color: txtColor, size: 9} },745 angularaxis: { gridcolor: polarGridColor, tickfont: {color: txtColor, size: 11, family: fontFam} },746 },747 paper_bgcolor: bgColor, plot_bgcolor: bgColor, font: {family: fontFam, color: txtColor},748 margin: {l:50, r:50, t:30, b:60},749 legend: { orientation: "h", y: -0.15, font: {size: 11} }750 };751 Plotly.newPlot('chart-radar', [traceRadarOpt, traceRadarTopk], layoutRadar, {displayModeBar: false, responsive: true});752 753 754 // === PER-QUERY RESULTS CHART (Real Evaluation Data) ===755 const queryLabels = ['Q1: LLM<br>Hallucinations', 'Q2: Dense vs<br>Sparse', 'Q3: Chunking<br>Strategy', 'Q4: RAG<br>Evaluation', 'Q5: Vector<br>Databases', 'Q6: Reranking<br>Role', 'Q7: Embedding<br>Models', 'Q8: Context<br>Window'];756 const topkDiversity = [19.5, 20.9, 19.2, 18.4, 18.5, 20.9, 22.6, 19.3];757 const optDiversity = [22.6, 22.6, 21.2, 21.4, 23.7, 22.1, 24.1, 22.8];758 const redReduction = [66.7, 100, 25.0, 92.9, 90.0, 87.5, 80.0, 77.8];759 760 const traceTopkDiv = {761 x: queryLabels, y: topkDiversity, type: 'bar', name: 'Top-K Diversity',762 marker: { color: 'rgba(239,68,68,0.7)' },763 hovertemplate: '<b>%{x}</b><br>Top-K Diversity: %{y:.1f}%<extra></extra>'764 };765 const traceOptDiv = {766 x: queryLabels, y: optDiversity, type: 'bar', name: 'Optimized Diversity',767 marker: { color: 'rgba(16,185,129,0.8)' },768 hovertemplate: '<b>%{x}</b><br>Optimized Diversity: %{y:.1f}%<extra></extra>'769 };770 const traceRedLine = {771 x: queryLabels, y: redReduction, type: 'scatter', mode: 'lines+markers',772 name: 'Redundancy Reduction %', yaxis: 'y2',773 line: { color: '#f59e0b', width: 3 }, marker: { size: 8, color: '#f59e0b' },774 hovertemplate: '<b>%{x}</b><br>Redundancy Reduction: %{y:.1f}%<extra></extra>'775 };776 777 const layoutPerQuery = {778 title: { text: '📊 Per-Query Evaluation Results (Real Data)', font: { size: 16, color: txtColor, family: fontFam } },779 paper_bgcolor: bgColor, plot_bgcolor: bgColor, font: { family: fontFam, color: txtColor, size: 11 },780 barmode: 'group', bargap: 0.2, bargroupgap: 0.1,781 margin: { l: 50, r: 50, t: 50, b: 80 },782 xaxis: { tickfont: { size: 9 }, showgrid: false },783 yaxis: { title: 'Diversity %', gridcolor: gridColor, range: [0, 35] },784 yaxis2: { title: 'Redundancy Reduction %', overlaying: 'y', side: 'right', range: [0, 110], gridcolor: 'transparent' },785 legend: { orientation: 'h', y: -0.25, font: { size: 10 } },786 };787 Plotly.newPlot('chart-perquery', [traceTopkDiv, traceOptDiv, traceRedLine], layoutPerQuery, { displayModeBar: false, responsive: true });788 }789 790 // Initial render791 setTimeout(renderPlots, 100);792 793 </script>794 795 <!-- Particle Animation -->796 <script>797 (function() {798 const canvas = document.getElementById('particleCanvas');799 const ctx = canvas.getContext('2d');800 let particles = [];801 const N = 45, D = 100;802 function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }803 resize(); window.addEventListener('resize', resize);804 class P { constructor() { this.x=Math.random()*canvas.width; this.y=Math.random()*canvas.height; this.vx=(Math.random()-0.5)*0.2; this.vy=(Math.random()-0.5)*0.2; this.r=Math.random()*1.2+0.3; this.o=Math.random()*0.2+0.05; } update() { this.x+=this.vx; this.y+=this.vy; if(this.x<0||this.x>canvas.width)this.vx*=-1; if(this.y<0||this.y>canvas.height)this.vy*=-1; } draw() { ctx.beginPath(); ctx.arc(this.x,this.y,this.r,0,Math.PI*2); ctx.fillStyle=`rgba(0,229,255,${this.o})`; ctx.fill(); } }805 for(let i=0;i<N;i++) particles.push(new P());806 function anim() { ctx.clearRect(0,0,canvas.width,canvas.height); for(let i=0;i<particles.length;i++){particles[i].update();particles[i].draw();for(let j=i+1;j<particles.length;j++){const dx=particles[i].x-particles[j].x,dy=particles[i].y-particles[j].y,d=Math.sqrt(dx*dx+dy*dy);if(d<D){ctx.beginPath();ctx.moveTo(particles[i].x,particles[i].y);ctx.lineTo(particles[j].x,particles[j].y);ctx.strokeStyle=`rgba(0,229,255,${(1-d/D)*0.05})`;ctx.lineWidth=0.5;ctx.stroke();}}} requestAnimationFrame(anim); }807 anim();808 })();809 </script>810</body>811</html>812 