QueryForge: Adaptive Retrieval
Optimization, Built Google-Native
runs entirely inside Google Cloud's Always-Free tier
project-scoped, on a billing account shared with 3 other apps
auto-classified and routed, every decision explained
Standard RAG pipelines use a single embedding lookup per query. This works for simple factual questions and fails predictably everywhere else — multi-hop synthesis, comparative framing, temporal versioning, and entity-exact lookups all need a different retrieval strategy, and manual tuning of chunk size, top-k, or query rewriting rarely finds it. QueryForge automates that discovery: it classifies each incoming query, decomposes complex ones into atomic sub-queries, runs dense, sparse, and hybrid retrieval in parallel, fuses the results with Reciprocal Rank Fusion, and returns a fully explained routing decision alongside a ready-to-use config recommendation.
This version of QueryForge was rebuilt from an original open-source, multi-vendor design into a single-vendor Google Cloud architecture. Every managed component — Cloud Run, Firestore, Cloud Storage, Cloud Build — is chosen specifically because it fits inside Google Cloud's Always-Free monthly allotment, and the one LLM dependency (Gemini 2.5 Flash-Lite) is called through the Gemini Developer API's free tier, which is billed independently of Cloud Billing entirely. The project's own GCP project carries a hard $0.01 budget cap with an automatic kill-switch, because it shares its $10 billing account with three other apps that this project must never be able to affect.
§1 Problem Statement
Single-query retrieval is the number one production RAG failure mode. A single dense embedding is a reasonable default for a plain factual question, but it breaks down as soon as a query needs to synthesize across documents, compare entities, respect time, or match an exact identifier. Fixing this by hand — tuning chunk size, rewriting queries, adjusting top-k per use case — is slow and rarely converges on the right answer for every query shape a real corpus receives.
QueryForge exists because these four patterns recur across every enterprise corpus it has been pointed at, and each needs a structurally different retrieval strategy — not just a better prompt.
§2 Request Pipeline
Every call to POST /v1/optimize runs inside a single Cloud Run container, from request validation through to the fused, ranked, explained response. There is no separate vector database service, no separate reranker service, and no separate query-rewriting service — everything but the Gemini calls happens in-process.
§3 Pipeline Components
The query classifier, sub-query decomposer, and HyDE fallback are all Gemini 2.5 Flash-Lite calls against the Gemini Developer API — the free, API-key-based endpoint at aistudio.google.com, not the Vertex AI Gemini endpoint. That distinction matters for this deployment: the Developer API's free tier is billed on its own separate quota and never touches Cloud Billing, which is the account under the $0.01 cap. The classifier returns a query type, a confidence score, and the token-level signals that drove the decision — this classifier_explanation block is never omitted from a response.
Decomposition and HyDE are the two rewrite strategies QueryForge runs live in the build; step-back prompting and synonym-expansion rewrite are supported by the same classifier routing table but are not yet wired into the demo corpus evaluation — flagged honestly in §12 MVP Scope rather than presented as shipped.
Three retrieval strategies run concurrently via asyncio.gather() inside the same container, plus an optional reranker and the HyDE fallback above. Nothing here calls a paid API.
| 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
A content-type router selects chunking strategy per document class rather than applying uniform token splitting. 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.
| 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 actually helps: a before/after comparison on a real query, the grid search behind the adaptive-α defaults in §3.2, and where this pipeline sits in QueryForge's own MLOps lifecycle.
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 come from a grid search over strategy × α × reranker, evaluated on recall@10, MRR, and p50 latency against a HotpotQA-equivalent internal corpus. This is the shape of grid the config_recommendation block is drawn from.
| 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 |
The +31% recall figure quoted in the README is this bottom row against the dense-only baseline. The recommender only pays the reranker's ~600ms tax when the recall gain justifies it — which §3.2's routing table encodes as "multi-hop only."
§7 Google Cloud Architecture
Four layers, all Google-native. Nothing in this stack requires an account, API key, or credential outside Google Cloud and the Gemini Developer API.
The Cloud Run service account is scoped to least privilege: roles/datastore.user (Firestore), roles/storage.objectViewer (corpus bucket), roles/secretmanager.secretAccessor (Gemini API key), and roles/run.invoker for authenticated callers. No third-party vector database, SaaS annotation tool, or external hosting provider is in the request path — the only outbound call from the container is to the Gemini Developer API. On the free tier, Google may use API inputs/outputs to improve its models; enabling billing on the Gemini API opts out of that data use — a real trade-off for a $0.01-capped project, noted plainly in §13 Limitations rather than glossed over.
§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
QueryForge's original design was open-source and local-first by default. Every ADR below records the point where that default was re-examined against two hard constraints — 100% Google Cloud, and a $0.01 ceiling on a shared billing account — and documents what was chosen instead and why.
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 vector-indexed corpus, the per-query log, and the config recommendations — all inside the Always-Free daily read/write allotment at demo volume.
// 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
| 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). |