Query flow
The request path: query rewriting for real user input, retrieval, context assembly, and the grounded generation call — end to end.
Now the read path — what happens in the seconds between a user's question and a cited answer. Four stages, each doing one job.
Stage 1 — Query processing
Users don't submit clean queries; they submit turns in a conversation. 'What about for trade customers?' retrieves nothing by itself — its meaning lives in the previous turn. The standard fix is query rewriting: a small, fast LLM call that turns conversational input into a standalone search query ('trade program return policy differences'). Rewrite when there's history; skip the extra hop for cold single questions. This is also where your identifier-routing regex (Module 3) and any filter extraction ('category: returns') live.
Stage 2 — Retrieval
Your frozen retrieval-v1: hybrid search wide, rerank narrow, pre-filtered by metadata. One addition for the pipeline: return the scores alongside the chunks — downstream stages use them (low top-score → the abstention path in Module 5).
Stage 3 — Context assembly
Everything you built in Prompt Engineering Module 4, now automated: number the chunks [S1]…[Sk], prepend each with its display metadata (title → section), fence the block, and enforce a context token budget — if the chunks exceed it, drop from the bottom of the ranking, never truncate mid-chunk.
Stage 4 — Grounded generation
import anthropic
SYSTEM = """You answer questions about Harbor Lane policies and products
using ONLY the sources provided. Every factual claim must cite its source
as [S#]. If the sources don't contain the answer, say so plainly —
never fill gaps from general knowledge.
Return JSON: {"answer": str, "citations": [{"source_id": str,
"quote": str}], "answerable": bool}"""
def answer(question: str, context_block: str) -> str:
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system=SYSTEM,
messages=[{"role": "user",
"content": f"{context_block}\n\nQuestion: {question}"}],
)
return msg.content[0].textModel IDs change — if you get a model-not-found error, check Anthropic's current model list and update this string. (The claude-sonnet-5 id is used in the rewriter and the reranker too; the same note applies everywhere it appears.)
Recognize every line: the closed-book rule, the citation schema with verbatim quotes, the honest exit — your Prompt Engineering toolkit, wired to a live retrieval feed. The one new decision is the division of labor: retrieval decides what the model may know; the prompt decides how it must behave. When an answer is wrong, that division is your debugging map — and the first question is always the one you learned in the RAG overview: was the right chunk even retrieved?
For every query, log: raw input → rewritten query → retrieved ids + scores → context token count → model output. This trace is Module 6's raw material and Module 7's monitoring feed. Pipelines without traces are debugged by vibes.