Attach a stable id (chunk_id or short tag like [S3]) inline with each chunk, tell the model to cite by that id, and map ids back to sources at render time, so citations survive reranking and trimming.
Imagine you give a kid a stack of recipe cards and ask which card they used to make dinner. If every card has a sticker on the corner that says S1, S2, S3, the kid can just say 'S2' and you know exactly which recipe. If the cards have no stickers, the kid has to describe the recipe back to you, and they will probably get the title slightly wrong. RAG citations work the same way. Give every chunk a sticker before you hand them to the model, tell the model to cite the sticker, and you always know which chunk the answer came from. No stickers and the model has to guess at titles and URLs, and it guesses badly.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Citation linkage is the property that an answer's citation points back, deterministically, to the exact source row it came from. It is one of the few RAG quality properties that depends almost entirely on how the context block is assembled. The model is a passive participant, it cites whatever the system instruction and the chunk headers tell it to cite.
This card walks through the canonical assembly shape, the failure mode it avoids, and the production patterns that have stabilized around it in 2026.
Position is fragile; ids are stable
A natural first attempt is to number the chunks by their assembly position and ask the model to cite by number. 'Document 1 says X' is easy to prompt for and easy to render.
The attempt breaks the first time the assembly order changes. A reranker reorders the candidates so the strongest chunk moves to position one. A trim drops the third chunk to fit budget. A lost-in-the-middle mitigation moves the most important chunk to the head and the second-most to the tail. After any of these the model's old understanding of 'document 3' no longer corresponds to the same source row.
Bind the id to the text, not the slot
The fix is to give every chunk a stable identifier, either the vector-store row id, or a short generated tag like [S1], [S2], and write that identifier inline with the chunk. Now the chunk and the tag travel together. Reranking moves them as a pair. Trimming removes the pair entirely or leaves it; it never decouples them. The model sees the same tag to text binding regardless of what assembly transformations happened upstream.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
def assemble_context(chunks: list[Chunk]) -> str:
blocks = []
for i, c in enumerate(chunks, start=1):
tag = f"S{i}"
header = f"[{tag}] Source: {c.source_path} | section: {c.section}"
blocks.append(f"{header}\n{c.text}")
return "\n\n".join(blocks)
SYSTEM = (
"When you use information from a labelled source block, cite it inline "
"using its tag in square brackets (e.g. [S1]). Cite every claim that "
"depends on a source. Do not invent tags."
)
# Render-time validation
def validate_citations(answer: str, valid_tags: set[str]) -> list[str]:
import re
cited = set(re.findall(r"\[(S\d+)\]", answer))
return list(cited - valid_tags) # hallucinated tagsReal products, models, and research that use this idea.
- Anthropic Claude Opus 4.7 ships a structured citation API: chunks are passed as `documents` and the model emits citation objects with stable doc ids.
- OpenAI's 2026 Responses API exposes a document id based citation field for GPT-5, replacing earlier ad-hoc citation prompting.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow do you handle citation linkage when chunks are summarized before injection?
The summary inherits the source tag; render-time mapping resolves through the summary back to the original chunk(s).
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Asking the model to quote chunk text or paraphrase source titles. The model confabulates and the renderer cannot reliably link back to the underlying source row.
60 second bullets to scan on the way to the call.
Explain what a stable chunk identifier is and why it beats position
Walk through the assembly format (header line plus chunk text)
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.