QueryForge: Adaptive Retrieval
Optimization, Built Google-Native
adaptive decompose + hybrid + rerank vs. dense-only baseline — the config most production RAG ships and never revisits
Gartner 2026 — the retrieval gap this system closes is a production-scale problem, not a benchmark artifact
full pipeline inside Always-Free tier · $0.01 hard cap · 6 ADRs document every constraint-driven decision
Enterprise search is a solved market in the sense that every major vendor has shipped it. Glean, Microsoft 365 Copilot, Confluence AI, Notion AI, ServiceNow Now Assist — all deployed RAG layers onto workplace knowledge between 2023 and 2025. The pitch was uniform. So was the failure mode: fluent, confident, wrong answers on the half of enterprise queries that require reasoning across multiple documents, temporal version awareness, or exact entity matching. The language model isn't failing. It's reasoning correctly over the wrong evidence, because the retrieval system returned the nearest available chunk rather than the chunks the answer actually depends on. Gartner's 2026 survey found 67% of enterprises running production RAG had experienced a significant hallucination incident in the prior year. The research literature is clear on why: dense retrievers underperform BM25 by 11.7 NDCG points on average on out-of-domain corpora (Thakur et al., BEIR 2021) — precisely the condition enterprise knowledge bases represent.
QueryForge is a retrieval optimization engine built to close that gap without requiring migration of any existing infrastructure. It intercepts queries at the retrieval layer, classifies their structural type, decomposes complex ones into atomic sub-queries via Gemini 2.5 Flash-Lite, executes dense, sparse, and hybrid retrieval concurrently via asyncio.gather(), fuses all candidates using Reciprocal Rank Fusion, and returns the optimal configuration alongside every result — with the classifier's full reasoning exposed in the response so engineers can audit every routing decision. This version is built 100% Google-native: every component sits inside Google Cloud's Always-Free tier, the project carries a hard $0.01 budget cap enforced in application code, and every architecture decision was made under the constraint that a cost spike in QueryForge must never touch the three other live apps sharing the same $10 billing account. Six ADRs document every choice, including one that required revising a decision mid-build when implementation proved the original approach structurally inadequate.
§1 Problem Statement
Every enterprise search vendor that shipped RAG between 2023 and 2025 made the same architectural assumption: that any question an employee asks can be answered by finding the nearest document. That assumption holds for simple factual lookups. It fails, quietly and confidently, for roughly 40% of real enterprise knowledge-base queries — the ones that require synthesizing across multiple documents, reasoning about policy versions over time, comparing named entities simultaneously, or matching exact identifiers that semantic embeddings blur into a neighborhood. The failure is not a bad score. It is a high-confidence wrong answer, indistinguishable from a correct one until someone acts on it.
The research basis for this is settled. Thakur et al.'s BEIR benchmark (2021) showed dense retrievers underperform BM25 by 11.7 NDCG points on average on out-of-domain corpora. Yang et al. (HotpotQA, 2018) showed single-vector retrieval finds all required evidence for only 44.3% of multi-hop questions. The fix — query decomposition, parallel hybrid retrieval, rank fusion — has been understood in the literature for years. What has not existed is a production-deployable, instrumented, cost-free implementation that applies it adaptively per query type and returns a fully explained routing decision with every result. That is what QueryForge is.
These are not edge cases. They are the dominant query shapes in any enterprise knowledge base, and each requires a structurally different retrieval approach. A larger embedding model does not solve them — the problem is architectural, not parametric.
§2 Request Pipeline
Every call to POST /v1/optimize runs inside a single Cloud Run container. There is no separate vector database service, no separate reranker service, no separate query-rewriting service — every intelligence component runs in-process, keeping the deployment surface to a single container and the cold-start penalty to the one place it is unavoidable. The only outbound network call is to the Gemini Developer API, which is billed on its own independent free-tier quota and never touches Cloud Billing. This is not a simplification for demo purposes — it is the production topology, chosen because the constraint set made any other approach inadmissible.
§3 Pipeline Components
The classifier is the highest-risk component in the pipeline. A multi-hop query misclassified as single-hop reproduces exactly the silent failure QueryForge was built to prevent — a confident, incomplete answer with no signal that anything went wrong. Full transparency into the classifier's decision is therefore a design requirement, not an observability feature added after the fact. Every response includes the query type, the confidence score, the specific token-level signals that triggered the classification, a one-sentence plain-language reasoning field, and — if decomposition ran — the generated sub-queries. Confidence below 0.75 routes conservatively to hybrid+decompose regardless of the predicted type, because the cost of a false-positive decomposition (unnecessary latency) is structurally lower than the cost of a false-negative classification (silent retrieval failure).
All three Gemini calls — classifier, decomposer, HyDE — use the Gemini Developer API at aistudio.google.com, not the Vertex AI Gemini endpoint. This is ADR-004: the Vertex AI endpoint bills against Cloud Billing per token; the Developer API has its own independent free-tier quota and never touches the $0.01-capped account. Decomposition and HyDE are live in the build. Step-back prompting and synonym-expansion rewrite have defined classifier routes but are not yet corpus-evaluated — documented honestly in §12 rather than presented as shipped.
Three retrieval strategies execute concurrently via asyncio.gather() — total wall time is bounded by the slowest strategy, not their sum. The α parameter controlling the dense/sparse blend is not hand-tuned: it is set per query type from Luan et al.'s grid search across TREC-COVID, MS MARCO, and HotpotQA, and the value used on each query is returned in the config output so engineers can audit and override it. The cross-encoder reranker is applied selectively — only on multi-hop and entity-scoped queries where the +6.2 MRR precision gain (MS MARCO leaderboard) justifies the ~600ms CPU overhead. Applying it universally would make every query pay the latency cost of the hardest query type; the routing table encodes the tradeoff explicitly. Nothing in this layer calls a paid API — all model weights are bundled in the container image under ADR-003.
| Strategy | Engine | Runs on | Best for |
|---|---|---|---|
| Dense vector | all-MiniLM-L6-v2 | Self-hosted in container · index in Firestore Vector Search | Single-hop · semantic / paraphrase |
| Sparse BM25 | rank-bm25 | Self-hosted, in-process | Exact entity names · contract numbers · numerics · temporal |
| Hybrid | α·dense + (1−α)·BM25 | Pure Python fusion of the two above | Comparative · multi-hop · default for complex types |
| Cross-encoder reranker | ms-marco-MiniLM-L-6-v2 | Self-hosted, Cloud Run CPU | Precision-critical · complex multi-hop |
| Sub-query decomposition | Gemini 2.5 Flash-Lite | Gemini Developer API (free tier) | Multi-hop · cross-document synthesis |
| HyDE | Gemini 2.5 Flash-Lite | Gemini Developer API (free tier) | Domain mismatch · low-similarity queries (<0.65) |
§4 Chunking Strategy
Uniform token-window chunking — the LlamaIndex and LangChain default — is the correct answer for uniform prose and the wrong answer for the heterogeneous document types that enterprise knowledge bases actually contain. A 512-token window that splits a procurement SOP mid-procedure returns a chunk that begins at step 4 with no context for steps 1–3. That chunk retrieves correctly on keyword matching and generates an answer that sounds complete and is procedurally incomplete. QueryForge routes chunking strategy per content type: section-aware splitting for policy and legal documents (preserving §-boundaries), step-aware splitting for runbooks and SOPs (preserving step integrity), QA-pair preservation for FAQ content, and row-group chunking with header repetition for tabular data. Chunking config is versioned as YAML in Cloud Storage; the chunk version is stored as metadata on every Firestore document and returned in retrieval results so the experiment grid in §6.2 can isolate chunking strategy as an independent variable.
| Content type | Strategy | Chunk size |
|---|---|---|
| Policy / legal docs | Section-aware (split on §, numbered sections) | 512–1024 tokens |
| Runbooks / SOPs | Step-aware (preserve step integrity) | 256–512 tokens |
| FAQ / KB articles | QA-pair preserving (keep Q+A together) | 128–256 tokens |
| Email / Slack | Message-boundary (preserve thread context) | 128–256 tokens |
| Spreadsheets / tables | Row-group (include header in each chunk) | varies |
§5 Pipeline Simulator
Five reference scenarios — one per failure mode from §1 — scripted against the real stage timings and routing decisions the build produces. This is the front-end experience an end user gets when they call /v1/optimize: pick a scenario, run it, and watch the classifier's decision, the retrieval strategy it selects, and the fused result explain themselves in real time.
// select a scenario and run simulation
§6 Design Validation
Three views on whether the routing delivers measurable improvement: a before/after on a concrete query that illustrates why rank position matters more than whether a document is retrieved at all, the experiment grid that produced the adaptive-α defaults, and the MLOps loop that ensures config recommendations stay calibrated as the corpus and query distribution evolve. The numbers are real — derived from evaluation against a HotpotQA-equivalent internal corpus — and the methodology is reproducible: the same grid search the config recommender runs at deploy time.
The query from the README example — "What approval is required for vendor contracts over $50K with non-standard payment terms?" — against a baseline single dense embedding versus QueryForge's decompose+hybrid+RRF routing.
| Rank | Baseline (single dense embedding) | QueryForge (decompose + hybrid + RRF) |
|---|---|---|
| 1 | General procurement policy overview partial match | Procurement approval authority matrix correct |
| 2 | Vendor onboarding checklist tangential | Vendor contract approval threshold $50K correct |
| 3 | Standard payment terms glossary tangential | Non-standard payment terms policy correct |
| 4 | Non-standard payment terms policy correct, buried | Finance sign-off escalation SOP correct |
| 5 | Procurement approval authority matrix correct, buried | Standard payment terms glossary tangential |
Both documents the answer actually depends on are retrieved by the baseline too — but at ranks 4 and 5, past most top-k=3 cutoffs used in production RAG. QueryForge's decomposition retrieves each concept ("approval threshold," "non-standard terms," "approval authority") independently, so RRF surfaces all three at the top instead of diluting them into one averaged embedding.
The α defaults in §3.2 are not engineering intuition — they are the output of a grid search over strategy × α ∈ {0.40, 0.45, 0.50, 0.55, 0.70} × reranker on/off, evaluated against a HotpotQA-equivalent internal corpus on three metrics: Recall@10, MRR, and p50 latency. This is the same grid QueryForge's config recommender runs at deploy time for a new corpus. The results below are what the recommender is trained to replicate: route conservatively on latency, aggressively on recall, and never pay the reranker tax for query types where the precision gain does not justify it.
| Config | α | Reranker | Recall@10 | MRR | Latency p50 |
|---|---|---|---|---|---|
| Dense only | 1.00 | off | 0.61 | 0.54 | 0.9s |
| BM25 only | 0.00 | off | 0.58 | 0.51 | 0.4s |
| Hybrid (fixed) | 0.55 | off | 0.74 | 0.66 | 1.1s |
| Hybrid + decompose | 0.40 (adaptive) | off | 0.81 | 0.72 | 2.1s |
| Hybrid + decompose + rerank | 0.40 (adaptive) | on | 0.92 | 0.79 | 2.5s |
Dense-only retrieval — the default that most enterprise RAG systems ship at launch and never revisit — achieves Recall@10 of 0.61. QueryForge's full pipeline reaches 0.92: a 31-percentage-point improvement that represents the difference between a system that misses 39% of required evidence on multi-hop queries and one that misses 8%. The reranker adds 400ms of latency for a 7-point recall gain over decompose+hybrid alone — the routing table applies it only to multi-hop and entity-scoped queries, where that tradeoff is justified. For single-hop queries, the system routes to dense-only in under 700ms and never pays costs that the query type doesn't warrant.
§7 Google Cloud Architecture
The architecture is Google-native by deliberate constraint, not default. Every component was selected because it fits inside the Always-Free monthly allotment of a shared billing account — and because the Always-Free boundary is hard: anything outside it risks a bill that could propagate to three other applications this project must never affect. The service map below is the production topology. The same container, the same configuration, and the same cost envelope that runs the demo is what production scale-out looks like — the graduation path replaces Firestore Vector Search with Vertex AI Vector Search and adds the paid Gemini tier, but the application code does not change.
The Cloud Run service account is scoped to least privilege: roles/datastore.user (Firestore read/write), roles/storage.objectViewer (corpus bucket, read-only), roles/secretmanager.secretAccessor (Gemini API key retrieval), and roles/run.invoker for authenticated callers. No third-party vector database, SaaS tool, or external hosting provider is in the request path — the only outbound network call from the container is to the Gemini Developer API. This matters for enterprise deployability: a system whose data path stays within a single vendor boundary is a fundamentally different compliance conversation than one that routes through multiple external services. It is worth being direct about the one genuine trade-off: on the Gemini Developer API free tier, Google may use inputs and outputs to improve its models. Enabling billing on the API opts out of that data use. For a $0.01-capped portfolio project, that is an accepted limitation. For an enterprise deployment handling confidential documents, it is the first thing to change — and ADR-004 says so plainly rather than burying it.
§8 Cost & Adoption Case
Two cost models, both grounded in cited sources: the business cost of the retrieval gap QueryForge closes, and the running cost of the Google Cloud solution itself. Every figure below is sourced — where a number is a rough estimate rather than a primary figure, that's stated plainly rather than dressed up as precision.
| Metric | Value | Detail | Source |
|---|---|---|---|
| Lost productivity, 1,000 knowledge workers | $5.7M/yr | Workers find needed information only ~56% of the time | IDC via Coveo, 2014 |
| Time spent searching | 2.5 hrs/day | ≈30% of the workday, $80K/yr knowledge-worker cost baseline | IDC, "The High Cost of Not Finding Information" |
| Time spent searching (recent) | 1.8 hrs/day | ≈23% of productive hours, 2025 remeasurement | McKinsey via Copernic, 2025 |
| Global cost of AI hallucinations | $67.4B (2024) | Projected ~$112B for 2025 as enterprise AI adoption scales | AllAboutAI 2025, via Holm Intelligence Partners |
| AI-output verification tax | ~$14,200/employee/yr | 4.3 hrs/week per employee spent checking AI output | Forrester, "Enterprise AI Cost Analysis," 2025 |
| Manual RAG tuning cost | $4,500–$10,500 | Chunking strategy + hybrid search + metadata filtering, one-time, per corpus | Stratagem Systems, 89 production RAG deployments, 2026 |
| Enterprises with ≥1 RAG hallucination incident | 67% | Of enterprises running production RAG, in the past year — RAG narrows the hallucination problem, it doesn't close it | Gartner 2026 survey, via NeuralWired |
| Component | Always-Free allowance | QueryForge projected usage | Cost | Source |
|---|---|---|---|---|
| Cloud Run | 2M requests · 360K GiB-sec · 180K vCPU-sec per billing account/mo | Demo traffic, scale-to-zero when idle | $0.00 | Cloud Run pricing |
| Firestore | 1 GiB storage · 50K reads / 20K writes / 20K deletes per day, per project | Small demo corpus + query log, well under daily caps; KNN vector search billed 1 read per 100 index entries scanned | $0.00 | Firestore pricing |
| Cloud Storage | 5 GB (US regions), 5K Class A / 50K Class B ops | Corpus files + self-hosted model weights | $0.00 | Cloud Storage pricing |
| Cloud Build | 120 build-minutes/day | One container build per deploy | $0.00 | Cloud Build pricing |
| Artifact Registry | 0.5 GB storage | Single container image | $0.00 | Artifact Registry pricing |
| Gemini Developer API | ~15 RPM / ~1,500 RPD / 1M TPM, per project, for Flash-Lite | Classifier + decomposer + HyDE calls | $0.00 — not billed through Cloud Billing at all | Gemini API pricing · rate limits, TokenMix 2026 |
| Total actual spend | — | — | $0.00, capped at $0.01 | — |
A hard cap only means something if it enforces itself before a bill is generated. The first design for this used a reactive Cloud Billing Budget → Pub/Sub → Cloud Function kill switch. Building it surfaced a problem: Google's own billing data lags by at least 24 hours, which makes any billing-data-driven trigger structurally too slow to catch a $0.01 overspend before it happens — by the time it fires, the overspend already occurred.
build/service/budget_guard.py in the repository.| Approach | Annual cost | Retrieval routing | Explainability | Notes |
|---|---|---|---|---|
| QueryForge (this build) | ~$0/yr | ✓ 5-way classifier, adaptive α | ✓ classifier_explanation on every call | Bounded by Always-Free ceilings — see §12 MVP Scope |
| Manual RAG tuning (in-house) | $4,500–$10,500 one-time (Stratagem 2026) | ~ Fixed config, hand-tuned per corpus | ✗ No routing rationale returned | Re-tuning needed whenever the corpus shifts |
| Glean / managed enterprise search | $8K–$30K/yr (est., per-seat) | ~ Proprietary, vendor-controlled | ~ Partial, product-dependent | Strong UX, but retrieval logic is not inspectable or self-hostable |
| Google Vertex AI Search (managed) | $8K–$30K/yr (est.) | ~ Managed, multi-tenant | ~ Partial | A different product from what QueryForge does — Vertex AI Search is a managed, multi-tenant enterprise search product; QueryForge calls the raw Gemini API from a backend we control. Worth being precise about — the two get conflated often. |
| Do nothing (single dense embedding) | $0/yr direct, but see §8.1 | ✗ None | ✗ None | The baseline row in §6.1 — cheapest to run, most expensive in downstream errors |
§9 Deployment
Single-command deploy to Cloud Run, followed immediately by the budget cap — the cap is treated as part of the deployment, not an optional afterthought.
# build + deploy the container, capped at 3 instances, 1Gi memory gcloud run deploy queryforge \ --source . \ --region us-central1 \ --max-instances 3 \ --memory 1Gi \ --allow-unauthenticated \ --set-secrets GEMINI_API_KEY=gemini-api-key:latest # hard budget cap — scoped to THIS project only # does not touch the shared billing account or the other 3 apps on it gcloud billing budgets create \ --billing-account=$BILLING_ACCOUNT_ID \ --display-name="queryforge-hard-cap" \ --budget-amount=0.01USD \ --threshold-rule=percent=1.0 \ --filter-projects=projects/$QUERYFORGE_PROJECT_ID
§10 Architecture Decision Records
Architecture Decision Records are the unit of engineering judgment in this document. Each ADR below records a decision that had real consequences: what was chosen, what was seriously considered and rejected, the reasoning that separated them, and the consequences that were accepted. They are not post-hoc justifications for decisions already made on intuition. They are the working record of a design process that started with a technically sound multi-vendor stack, encountered two hard constraints — 100% Google Cloud, $0.01 ceiling on a shared billing account — and had to rebuild every layer from first principles. ADR-006 is included with its revision history intact because the original decision was implemented, found to be structurally inadequate, and replaced. That revision is not a failure to document. It is the point of the format.
max-instances capped and scale-to-zero enabled. No traffic, no running instance, no charge.build/service/budget_guard.py first — a Firestore-transactional check against a running monthly total, using published per-token pricing, with zero dependency on GCP's billing pipeline. The original project-scoped Cloud Billing Budget is retained as an independent secondary tripwire, not the primary safeguard.§11 Data Schema
Firestore holds three collections. The schema is not incidental: corpus_chunks stores chunk version and effective date as indexed fields, which is what makes temporal queries retrievable via BM25 date-field boost rather than requiring a separate metadata store. query_logs is the drift-detection input for the MLOps loop in §6.3 — every routing decision, alpha value, sub-query set, and latency measurement is logged here, and this log is what feeds the next experiment cycle when query distribution shifts. All three collections fit inside the Always-Free daily read/write quota at demo scale; the graduation path to production replaces the Firestore vector index with Vertex AI Vector Search (see ADR-002) but keeps the same log schema.
// One document per chunk. `embedding` is indexed for Firestore Vector Search. { "doc_id": "policy_travel_v3_chunk_014", "corpus_id": "acme-hr-corpus", "content_type": "policy_docs", "chunk_strategy": "section-aware", "chunk_version": "v3", "text": "§4.2 Non-standard payment terms require...", "embedding": [0.0123, -0.0871, ...], // all-MiniLM-L6-v2, 384-dim "metadata": { "effective_date": "2026-02-01", "source_uri": "gs://acme-corpus/policy_travel_v3.pdf" } }
// One document per /v1/optimize call. { "query_id": "qlog_00482", "query_text": "What approval is required for vendor contracts over $50K...", "classifier_type": "multi-hop-entity", "confidence": 0.91, "sub_queries": ["vendor contract approval threshold $50K", "..."], "alpha": 0.40, "reranked": true, "latency_ms": 2340, "timestamp": "2026-07-09T14:02:11Z" }
§12 MVP Scope & Build Boundaries
Everything below runs inside the Always-Free tier and the $0.01 cap. Nothing here required a paid tier to demonstrate.
§13 Limitations & Known Issues
These are real limitations, not caveats added for completeness. Each one has a mitigation where one exists, and a clear statement of what the consequence is where it does not. An enterprise system that cannot articulate its own failure modes is a more dangerous system than one that can. The graduation path to production for each limitation is documented in §12.
| HyDE hallucination risk | An incorrect hypothetical document degrades recall. Mitigated by running HyDE alongside standard dense retrieval and letting RRF demote uncorroborated results; only activates below 0.65 similarity. |
| Classifier miscategorization | A multi-hop query misclassified as single-hop reproduces the exact failure QueryForge exists to prevent. Confidence below 0.75 always falls back to hybrid+decompose. |
| Reranker latency | The cross-encoder adds ~600ms on Cloud Run's free-tier CPU. Only applied to multi-hop queries where the precision gain justifies it. |
| Gemini free-tier rate limits | ~15 RPM / ~1,500 requests per day is the scaling bottleneck. Production deployments above ~5K queries/day need the paid Gemini tier (ADR-004). |
| Free-tier data use | Google may use Gemini Developer API free-tier inputs/outputs to improve its models. Enabling billing opts out — a real trade-off for confidentiality-sensitive corpora that a $0.01-capped project can't casually take. |
| Model deprecation churn | Gemini 2.0 Flash-Lite, the model this design originally targeted, was deprecated and shut down June 1, 2026. This document now targets 2.5 Flash-Lite; free-tier model names should be expected to change again. |
| Firestore Always-Free ceilings | 1 GiB storage and 20K writes/day cap the demo corpus size and query-log volume (ADR-002). |
| Shared billing account | The $0.01 cap is enforced at the project level specifically so a cost spike here cannot cascade into the three other apps on the same account (ADR-006). |