Answer a question strictly from retrieved chunks with inline citation markers — and say "not found" rather than guess when the chunks don't support an answer.
The groundedness heart of a RAG pipeline: answer a question using only the retrieved chunks, with inline citation markers ([2]) tying each claim back to its source — and a hard answer_found: false path so the model says "the context doesn't contain this" instead of hallucinating.
This is the prompt that decides whether your RAG app is trustworthy. It is tuned for the failure mode that matters most: refusing to answer from outside knowledge when the chunks fall short.
| Entrypoint | Renders | Inputs |
|---|---|---|
system | The grounded-synthesis instructions + embedded output schema | style? (answer style) |
user | The delimited chunks + question | chunks, question |
Render system as the system message and user as the user message. Pass style to the system render.
question (string, required) — the question to answer.chunks (array of { id, text }, required) — the retrieved chunks. id is used in citation markers (string or number; cited exactly), text is the chunk content.style (string, optional) — answer style, e.g. "concise" or "detailed". Affects wording/length only — never which facts the model is allowed to state.parseOutput() returns:
{
"reasoning": "Chunk 2 gives the trial length; nothing addresses refunds, so that part is unanswerable.",
"answer_found": true,
"answer": "The free trial lasts 14 days [2]. The chunks do not state the refund policy.",
"citations": ["2"]
}
When the chunks don't support an answer:
{ "reasoning": "No chunk mentions pricing.", "answer_found": false, "answer": "The provided context does not contain enough information to answer this question.", "citations": [] }
reasoning comes first — what the chunks do and don't support, before committing to an answer.answer_found is a real gate: false ⇒ a plain not-found message and empty citations. Check this before showing an answer.answer is drawn strictly from the chunks, with [id] citation markers inline. Partial answers are allowed and the limit is stated explicitly.citations lists the deduplicated chunk ids actually used.Chunk ids are runtime input, so the output schema can't enum-constrain them — parseOutput() will not check that each returned citation id is one you actually supplied. Add a one-line membership check: assert citations ⊆ providedIds, and (recommended) that every [id] marker inside answer is in that set too. This is the same family as the runtime-membership caveat on text-classification / intent-routing. A mismatch is your signal the model cited something out of band — treat it as a failed answer.
{{#chunks}} annotated as an array.Verified against the live API: answer-present → correct answer + citations mapping to provided ids; answer-absent → answer_found: false with no fabrication; chunk-injection → treated as data, not obeyed. maxTokens is sized up (1536) because synthesis over many chunks runs longer than a classification call. Suggested default claude-sonnet-4-6 at temperature: 0. The synthesis stage of the RAG Essentials pipeline: rewrite → decompose → grade → guardrail → synthesize.