Skip to main content

Prompt injection — how documents enter the LLM

After optional query-time machinery has run — summarization, fetch check, HyDE, retrieval, rerank, rewrite, Re2 — RAG Me Up still has to do what users think of as "the AI": build a prompt and call the LLM. This page is about how evidence is injected, with concrete examples of the resulting message thread.

The three prompt roles

RAG Me Up separates concerns that beginners often jam into one mega-string:

  1. rag_instruction — system prompt: role, grounding rules, and {context} (formatted chunks).
  2. rag_question_initial — how the first user question is presented ({question}).
  3. rag_question_followup — how subsequent turns are presented ({question}).

Formatting documents into context

Chunks are not dumped raw. format_documents wraps each one in an explicit block with filename and metadata:

[Document] *Filename* `data/hr/partner-leave.json`
*Content*: Partners employed for at least 26 weeks are entitled to ...
*Metadata* source: data/hr/partner-leave.json, dataset: hr, distance: 0.82 [/Document]

[Document] *Filename* `data/hr/expense-policy.json`
*Content*: Expense claims above €500 require director approval ...
*Metadata* source: data/hr/expense-policy.json, dataset: hr, distance: 0.71 [/Document]

Those markers make citation instructions enforceable: the filename sits next to the content in a predictable schema.

Worked example — first turn

Suppose the user asks "Who approves expense claims over €500?", Re2 is on, and two chunks were retrieved.

System message (rag_instruction with {context} filled):

Instruction: You are a digital librarian that can answer generic questions on
relevant content quickly and succinctly. Here are a few documents from the
library that you can use to answer the user's question...

[Document] *Filename* `data/hr/expense-policy.json`
*Content*: Expense claims above €500 require director approval...
...
[/Document]

User message (rag_question_initial with Re2-enhanced {question}):

The initial question you have to answer:

Who approves expense claims over €500?
Read the question again:
Who approves expense claims over €500?

LLMHelper.generate_response then builds the chat thread:

[system]  <rag_instruction + documents>
[user] <rag_question_initial>

and returns (answer, thread).

Worked example — follow-up that needs new documents

User continues: "And what about partner leave?" The fetch check says yes → retrieve again → rebuild system prompt with the new context, strip the old system message from history:

[system]  <rag_instruction + NEW documents about partner leave>
[user] <earlier user turns without old system>
[assistant] <earlier answers>
[user] <rag_question_followup for partner leave>

This prevents the model from seeing two conflicting document sets in one thread.

Worked example — follow-up that reuses documents

User: "Quote the sentence that says that." Fetch check says no → system_prompt=None, full prior history (including the earlier system prompt with documents) is reused:

[system]  <still the previous documents>
...history...
[user] The follow-up question you have to answer:

Quote the sentence that says that.

What belongs in rag_instruction

  • Role and domain ("digital librarian for internal HR policies").
  • Grounding rules ("answer only from the documents"; "say when you cannot find it").
  • Citation rules ("always mention the filename you used").
  • Style (length, match the user's language).

Place {context} where documents should be treated as reference material — typically after the role description.

Temperature

temperature from .env is applied inside LLMHelper. For factual RAG, start low (0–0.3). Streaming (/chat_stream) uses the same prompt construction; only token transport changes.