In March 2025 a trade compliance system with 16,597 verified tariff rows in its database returned brake callipers when a procurement analyst asked about cotton shirts. Not a wrong rate. The wrong product entirely. With full confidence and a source citation.
The database was correct. The API was correct. The problem was upstream — in the six lines of code between the user's message and the search call. This is a story about query poisoning, why it is the dominant failure mode in retrieval systems, and how a three-module pipeline eliminates it structurally.
The Failure
The user typed: "What's the duty on cotton shirts from India to UK?"
The system passed this string, unchanged, to a SQLite LIKE query:
SELECT * FROM tariff_rows WHERE description LIKE '%cotton shirts from India to UK%'
That query returns zero rows. No tariff description contains the phrase "cotton shirts from India to UK" — because descriptions don't include corridor words. Zero results triggered a stub fallback that returned hardcoded brake callipers codes from a test fixture no one had removed.
The correct query was:
SELECT * FROM tariff_rows WHERE description LIKE '%cotton shirts%'
Which returns six rows — men's cotton shirts, women's cotton blouses, batik-printed shirts. Exactly what the user needed. The data was always there. The query construction was wrong.
Why This Is Not a RAG Problem
The reflexive response to retrieval failures is to blame the retrieval system — add embeddings, tune the vector index, switch models. None of those would have helped here. The failure was in corridor stripping — or rather, its complete absence. The raw query was passed directly to the search executor without any preprocessing.
Chip Huyen's framing in AI Engineering Chapter 6 is precise: retrieval quality is determined before the retrieval call, not during it. The query that reaches the search tool is the only thing the tool can work with. A correct index with a poisoned query produces worse results than a mediocre index with a clean query.
The search tool is not the problem. The question you ask it is.
This is the distinction the three-module design enforces: extraction, query construction, and execution are separate responsibilities. Mixing them is what caused the failure.
The Three-Module Pipeline
The fix is architectural, not a patch. Three modules with explicit input/output contracts:
Module 1 — Entity Extractor
Receives the raw query. Returns an
ExtractionResult —
a structured dict with product_terms, origin,
destination, hs_code_provided, and _lang.
The critical rule: product_terms must never contain corridor words,
question words, or stopwords. Only the product noun phrase passes through.
"cotton shirts from India to UK"
↓ Module 1
ExtractionResult(
product_terms = ["cotton shirts"], ← corridor stripped
origin = "IN",
destination = "GB",
hs_code_provided = None,
_lang = "en"
)
Module 2 — Query Builder
Receives the ExtractionResult. Returns a SearchPlan — an ordered list of SearchStrategy objects. The Query Builder's key decision is chapter prediction: a FTS5 chapter filter constrains the search space before any FTS5 query runs. "Cotton shirts" maps to Chapter 62 (garments of woven fabric). The search never touches Chapters 39 (plastics) or 87 (vehicles). This is what makes retrieval precise.
Module 3 — Search Executor
Executes strategies in order until sufficient results are found. FTS5 with chapter hint first (~2ms). UK Trade Tariff live API second (~300ms). LIKE fallback third. The executor is dumb by design — it runs strategies, checks results, stops when done. No business logic.
The cotton shirts query now flows through Module 1 (strips corridor words), Module 2 (predicts Chapter 62, builds FTS5 strategy), and Module 3 (executes FTS5 MATCH on "cotton shirts" filtered to Chapter 62) — returning 6205200010, 6205200090, 6206300090 in under 5ms total.
The concepts above are linked — click any highlighted phrase to open Vidhi and go deeper on that specific part of the architecture. The panel is grounded in the full Vanik architecture document.
The Principle
Every retrieval failure worth investigating turns out to be a query construction failure. The search tool received a query it could not possibly answer correctly — and answered anyway, with false confidence.
The fix is not a smarter search tool. It is a named, tested, boundary-respecting layer between user intent and search execution. The query that reaches the tool must contain only the signal the tool needs. Everything else — corridor words, question syntax, enterprise metadata — is stripped before the boundary.
Module 1 strips. Module 2 constructs. Module 3 executes. Three modules, three contracts, one correct result.