Build1 publisher3 min readPublished
A RAG pipeline injected itself: no attacker, just a book about LLMs in the index
A developer's noise filter and reranker fixed a retrieval bug and exposed a worse one. If ordinary retrieved text can hijack a prompt, injection is a property of your corpus, not your threat model.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- The author reports finding "a real prompt injection, triggered entirely by accident, by a book about LLMs" while testing a fix to an earlier retrieval bug.
- The pipeline compares two ways of answering questions about a document: RAG, which searches the document for the most relevant snippets and feeds only those to the model, and Direct, where the model reads the whole document (or as much as fits) and answers from that.
- The stack is BGE-M3 for retrieval and Qwen3 for answer generation, running for free on a Google Colab GPU.
- In part 1, asked what a book about large language models was about, the RAG answer said it was about "machine learning research communication via illustrated web articles", which the author calls nonsense.
- The cause of the part 1 error was that retrieval had grabbed a footnote buried in the book's dedication page instead of content about the book's real subject.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A developer testing a small open-source retrieval pipeline reports that retrieved document text took over his prompt with nobody on the other end: in his own words, the pipeline hit "a real prompt injection, triggered entirely by accident, by a book about LLMs" [1]. That detail matters more than the fix, because it relocates prompt injection from the adversarial-input column to the retrieval-surface column, where it becomes a corpus problem you already own.
The setup is deliberately modest. The pipeline compares two ways of answering a question about a document: retrieve the most relevant snippets and feed only those to the model, or hand the model the whole document and ask directly [2]. Retrieval is BGE-M3, generation is Qwen3, all on a free Google Colab GPU [3].
The precedent from part 1 is the same mechanism without the instruction-following. Asked what a book about large language models was about, the RAG path confidently answered that it concerned "machine learning research communication via illustrated web articles" [4]. The cause was not the model: retrieval had grabbed a footnote buried in the book's dedication page [5]. The original patch only stripped bibliographies at the end of academic papers and never touched front matter, which is exactly where the failure lived [6].
The rewrite is two layers. A noise filter now runs on every chunk before it is embedded, flagging anything structurally shaped like a table of contents, an index, or a block of footnotes [7]. The signals are a digit ratio above 0.12, two or more ". . ." runs or three or more "..." runs, and four or more non-empty lines where more than 70 percent are shorter than 40 characters [8]. That digit threshold amounts to roughly one character in eight being a numeral [3]. The author is candid that these are cheap pattern matches on what junk looks like after a PDF becomes plain text [9]. Then retrieval pulls 20 candidates and a cross-encoder, bge-reranker-v2-m3, scores each question-chunk pair, keeping the top 5 [10], which discards 15 of every 20 candidates [2]. The stated purpose is to catch noise that slips past the filter [11].
Both layers judge relevance, not trust, and that is the gap. None of the three filter checks inspects meaning, so a chunk of well-formed prose that happens to contain instructions passes all of them [4]. The reranker scores how well a chunk answers the question, so an instruction-bearing chunk that is genuinely on-topic gets promoted rather than dropped [5]. A book about LLMs, queried about LLMs, is the worst case: the injecting text is also the most relevant text.
The regression test is a useful control. On the English-Nepali legal machine translation paper, both paths agreed on what the document was about [12], with RAG citing roughly 125,000 parallel sentences and the direct path reporting BLEU scores of 7.98 and 6.63 [13]. RAG even surfaced a confidentiality restriction the direct answer omitted [14]. The noise filter flagged 0 of that paper's 27 chunks [15], a 0 percent flag rate [1], which tells you the filter is quiet on clean prose and therefore no defence at all against clean prose that gives orders.
Two things to watch. The supplied text breaks off before the injection itself is walked through [16], so the actual trigger string and the model's response are still unpublished. And the direct-reading baseline is the natural control: if the same document steers the whole-document path too, this is a model problem; if only the retrieval path breaks, the fix belongs in ingestion, where chunks would need provenance and instruction screening, not just tidier formatting.