Dayward AI
Week 2 · D10About 5 hours

Query-Side Optimization: Rewriting, Hypothetical Document Embeddings, Multi-Query, Step-Back Prompting, and Intent Routing

A user's question is often short and vague, and a retrieval miss isn't always the index's fault. Today put the effort into what happens after the query comes in and before retrieval happens: rewriting, generating a hypothetical answer and retrieving on that, splitting into multiple sub-queries, stepping back to ask a more general question, and judging intent first to decide which path to take.

Today's goals 0/3

Sign in to tick these off and save your progress.

Today's Goals

  1. Explain what shortfall each of query rewriting, hypothetical document embeddings, multi-query, and step-back prompting patches
  2. Implement an intent router that sends chat, retrieval-needed questions, and multi-hop questions down different handling paths
  3. Prove the net benefit of each query-side technique with evaluation, and work out the extra latency and call count each one adds

Yesterday's effort all went into the retriever: two-path recall, fusion, reranking. Today switches ends — not one character of the retriever changes, only the sentence going into it. Come back and tick off the three goals.

Plain-Language Walkthrough

The patient says it hurts, and the doctor has to ask where

At a hospital you are not wheeled into the CT room on arrival. The doctor asks first: where does it hurt, when did it start, does pressing make it worse. That round of questioning produces not a diagnosis but an accurate chief complaint — "persistent dull pain in the lower right abdomen, two days, worse under pressure." With that sentence, the later imaging knows where to look.

In a retrieval-augmented generation system, the sentence a user types is the patient's opening "it hurts": short and vague. Everything of the first nine days — chunking, indexing, hybrid search, reranking — is equipment in the CT room. However good the equipment, an inaccurate complaint gets an image of the wrong place.

Today's work is adding a round of questioning after the query comes in and before retrieval happens. It has a collective name: query transformation. Four common techniques each patch one shortfall:

  • Query rewriting patches an incomplete statement: unresolved references, colloquialism, two questions packed into one sentence.
  • Hypothetical document embeddings patch a question and an answer not looking alike: the two were never in the same register.
  • Multi-query patches one phrasing not fetching everything: the documents may use a different word for the same thing.
  • Step-back prompting patches a question asked too specifically, so specifically that the corpus contains no such sentence.

Plus one more layer, intent routing: judge first whether this question needs retrieval at all.

One thing must be said in advance: today's product is not turning all five on but a default configuration. Each technique costs at least one extra model call and most cost several extra retrievals. So each one reports three bills along the way: how much the metric rose, how much latency rose, and how many extra calls. The scale was built on day eight.

Query rewriting: completing what was left unsaid

Query rewriting is the plainest and most valuable of the four. It does three things: resolving references, splitting compound questions, and turning colloquialism into retrieval-friendly phrasing.

Resolving references: the last turn asked who must approve a production database failover in writing, and this turn asks what that person's name is. "That person" sent to BM25 fetches every chunk discussing people. The rewriter must turn it into "what is the platform team lead's name" — with the antecedent coming from the previous turn.

Splitting compound questions: "how many annual leave days carry over and by when must the carried-over portion be used" is two questions, and combined, the two topics' terms dilute each other and neither ranks.

Colloquial to retrieval-friendly: in "could you tell me roughly what that reimbursement limit is," the filler words appear everywhere in the corpus with near-zero inverse document frequency, serving only to lengthen the query and skew BM25's length normalization.

In implementation it is one very short model call. Two details: set temperature to 0, or the same sentence rewrites differently each time and evaluation numbers drift; and explicitly forbid the model from answering the question, or it helpfully writes the answer in too, and that passage sent to retrieval is pure pollution.

rewrite.js
const REWRITE_SYSTEM = [
  'You are a retrieval system query rewriter. Rewrite the user\'s current question into a search query.',
  '1. Resolve references from the conversation history (this person, he, that one become specific names); leave as-is when it cannot be resolved.',
  '2. Remove filler words and pleasantries, keeping only content words with retrieval value.',
  '3. Do not answer the question and do not add facts absent from the history.',
  'Output only the rewritten line, with no explanation.',
].join('\n')
 
export async function rewriteQuery(client, history, question) {
  const context = history
    .slice(-4)
    .map((t) => `${t.role === 'user' ? 'User' : 'Assistant'}: ${t.content}`)
    .join('\n')
  const res = await client.messages.create({
    model: 'claude-haiku-4-5-20251001', // rewriting is a short task; the cheap fast tier suffices
    max_tokens: 512,
    temperature: 0, // one sentence must rewrite identically, or evaluation numbers drift
    system: REWRITE_SYSTEM,
    messages: [{ role: 'user', content: `History:\n${context || '(none)'}\n\nThis turn: ${question}` }],
  })
  return res.content.find((b) => b.type === 'text').text.trim()
}

Three bills: one extra model call per question, with one line of output on the cheap fast tier; one extra round trip of latency; and the metric barely moves on single-turn question answering — the lab's metrics are identical with it on or off. Its value is entirely in multi-turn, expanded at the end of this section.

Hypothetical document embeddings: invent an answer first and retrieve on that

The most counter-intuitive of the four, known as HyDE: have the model invent an answer to the question first, then retrieve using that invented answer rather than the question. Hearing it first sounds absurd — what the model invents may be entirely wrong, so how does retrieving on something wrong help?

The key is that question and answer do not live in the same semantic space. A user asks what the nightly accommodation cap is for a first-tier city, which is an interrogative; the corpus's passage reads "the nightly accommodation standard for first-tier cities is such-and-such, with any excess requiring the department head's signature" — declarative, with concrete numbers, in official register. Two passages about the same thing, entirely different in register, syntax, and vocabulary, and vector search compares exactly semantic similarity.

HyDE turns the interrogative into a stretch of fake text that looks like a policy clause: "Regarding first-tier city accommodation standards, per company policy, the nightly cap is such-and-such." The numbers in it may be invented, and that is fine — we never show it to the user and only use its vector to find neighbors. It sits in the same register with the same official vocabulary as the real passage, and is naturally much closer.

Three bills: one extra model call per question, and the most expensive of the four — a hypothetical document runs 80 to 150 words, more than ten times the rewriter's output tokens; retrieval count doubles; and our offline lab cannot answer the metric bill. In offline mode this step is a rule-based stand-in, only layering register words on, unable to invent something like a concrete cap — which is exactly where HyDE works. Offline movements show only that the path connects and prove neither that HyDE helps nor that it does not. This bill must be run yourself against a real model.

Multi-query: one question, several angles, results fused by reciprocal rank

Multi-query's idea is simple: the documents may use a different word for the same thing. A user asks what process a consumer scale-up follows, and the meeting note says it is a class-two change requiring a ticket at the change review board per the standard. Scale-up and change, process and standard — not one character overlaps.

So have the model generate two or three differently angled search queries: one keyword-leaning (nouns and numbers only), one document-register-leaning, plus the original question, each searched and then fused into one list.

Fusion uses day nine's reciprocal rank fusion with a constant of 60 and an id tie-break for reproducibility — the principle is on day nine and the same function is reused. Note one thing: day nine fused the ranks of two different retrievers, and today fuses the ranks of one retriever over different queries. The axis of fusion changed and the function did not — precisely what makes it better than weighting by score: it does not look at scores at all.

multi-query.js
const RRF_K = 60 // the value common in the original paper, uniform across this course
 
export function rrfFuse(rankings, k = RRF_K) {
  const scores = new Map()
  for (const ranking of rankings) {
    ranking.forEach((id, index) => {
      // Ranks start at 1
      scores.set(id, (scores.get(id) ?? 0) + 1 / (k + index + 1))
    })
  }
  return [...scores.entries()]
    .map(([id, score]) => ({ id, score }))
    .sort((a, b) => b.score - a.score || (a.id < b.id ? -1 : 1)) // tie-break by id for reproducibility
}
 
export async function multiQuerySearch(retriever, variants) {
  const rankings = []
  for (const q of variants) {
    const hits = await retriever.retrieve(q) // one search per variant, the fastest-growing cost here
    rankings.push(hits.map((h) => h.chunkId))
  }
  return rrfFuse(rankings)
}

Three bills: one extra model call per question (generate all variants in one call, not one call per variant); retrieval count multiplied by the variant count, so three variants is triple; and the metric is a net loss — recall did not move and nDCG fell from 0.671 to 0.665. A thirty-document corpus has no long tail requiring a different angle to fetch, and the two extra paths merely reshuffled the same results. Retest at ten thousand documents and the conclusion may well invert.

Step-back prompting: ask a more general question first, get the background, come back

Step-back prompting targets a different failure: a question asked so specifically that the corpus contains no such sentence.

A user asks whether temporarily raising a workspace's trace sampling rate to one hundred percent is a class-something change and how many working days in advance a ticket must be filed. No passage in the corpus discusses what class a sampling rate change is — change classification is in one policy document and sampling rates in a monitoring document, and retrieving this long question directly has both sides' terms diluting each other so that neither ranks.

Step back and ask: what are the change review classification rules and ticket lead times? That is a more general question, and the corpus happens to have a whole section on it; get the background and return to the specific question, and the answer is in the background.

Implementation is isomorphic to multi-query and differs only in the prompt — multi-query wants different phrasings at the same level and step-back wants the level above's overview.

Three bills: one extra model call per question (one line of output); retrieval count doubles; and the metric bill shares HyDE's affliction, since it too is a rule-based stand-in offline. But it has one advantage HyDE lacks: its output is short, so whatever its effect, it is the cheapest of the four expansion techniques.

Intent routing: not every question needs retrieval

The four techniques above all optimize retrieving more accurately. Intent routing asks a different question: does this one need retrieval at all?

A user says "hello" or "thanks, what else can you look up for me," and no amount of retrieval finds an answer. But if the pipeline sends every input through retrieval, those still trigger vectorization, a database query, and context assembly, and then stuff a pile of irrelevant chunks into the prompt from which the model has to struggle free to reply "hello." Money spent and accuracy down.

So add a very light classifier at the front of the pipeline, splitting three ways:

  • Answer directly: chat, greetings, questions about the system's capabilities. Zero retrievals.
  • Single-hop retrieval: one document holds the answer. The standard flow.
  • Multi-hop retrieval: an intermediate fact must be found first (who holds a role, which standard applies), then used to search a second document. Two rounds of retrieval.

The classifier can be very cheap — one short call outputting one of three words.

router.js
const INTENT_SYSTEM = [
  'You are a retrieval system intent classifier. Decide which path this question takes, outputting one word:',
  'direct     — greetings, thanks, or asking what you can do, where searching the knowledge base is pointless;',
  'single-hop — one document holds the answer;',
  'multi-hop  — an intermediate fact must be found first and used to search a second document.',
].join('\n')
 
export async function route(client, retriever, query, budgets) {
  const res = await client.messages.create({
    model: 'claude-haiku-4-5-20251001',
    max_tokens: 16, // one word only; do not give it room to write an essay
    temperature: 0,
    system: INTENT_SYSTEM,
    messages: [{ role: 'user', content: query }],
  })
  const text = res.content.find((b) => b.type === 'text').text
  // Undecidable falls back to single-hop: the fallback must lean toward spending a little more, never toward not answering
  const intent = text.includes('direct') ? 'direct' : text.includes('multi') ? 'multi-hop' : 'single-hop'
  if (intent === 'direct') return { intent, candidates: [], budget: 0 }
  const candidates = await retriever.retrieve(query)
  // Multi-hop must fit two documents, and 600 tokens often fits only the first — routing is not only about saving money
  return { intent, candidates, budget: intent === 'multi-hop' ? budgets.multiHop : budgets.default }
}

Note that comment about multi-hop needing to fit two documents. Day eight's context budget is 600 tokens, ample for a single-document question and often enough for only the first document of a multi-hop one. Recognizing multi-hop and giving it a higher budget measurably took multi-hop recall from 50% to 100%. So routing pays off twice: you stop spending where you should not, and you still have budget left where you should.

Those two originally failing multi-hop questions are worth taking apart, because they broke in different places and were fixed by different mechanisms. The first's second answer document never entered the candidate pool — the two documents share not one character, and no amount of reordering conjures what was not fetched; the bridging round retrieving on "platform team lead" is what brought it in. The second is the opposite: that document already ranked 4th in the baseline, and neither path's raw score cleared the gate (0 on keyword, 0.151 on vector, just short), so it was blocked by the refusal gate; the bridging round searching on the change review standard gave the same document a high keyword score, so it cleared the gate on its own raw score and rose to 1st along the way. Not in the candidate pool and in the pool but blocked are two ailments, and one medicine does not treat both.

How the fallback is designed: undecidable falls back to single-hop. Judging multi-hop as single-hop only searches one round fewer and answers incompletely, and judging chat as single-hop only wastes one retrieval; conversely, judging something needing retrieval as direct leaves the model with no material and only invention, the most expensive error. The fallback must lean toward spending a little more and never toward not answering.

Multi-turn conversation: without resolving references, retrieval will go astray

Query rewriting barely moves the metric on single-turn question answering, and the moment multi-turn arrives it goes from a nicety to the line between working and not. The lab runs this conversation:

Turn one asks who must approve a production database failover in writing, hitting the on-call procedure document stating it must be the platform team lead in writing. Turn two asks what that person's name is.

Retrieving those five words unresolved gives, measurably: not one candidate clears the gate and the system can only refuse. Note the failure mode — not answering wrongly but saying it cannot find an answer that is plainly in the corpus. A user who just asked and is told on the next line that nothing was found experiences a collapse of trust.

After rewriting, the sentence becomes "what is the platform team lead's name." Same retriever, same index, and this time the organization chart document enters the context — only that document states who the platform team lead is. The question went from unanswerable to answerable, and we changed only that sentence.

That also raises a deeper point: the refusal itself was correct (retrieval genuinely fetched nothing), and the reason was not that the corpus lacks the answer but that we asked the question badly. Generation-side refusal policy was settled on day six and governs having material insufficient to support an answer; today is about going astray before any material is retrieved.

Source Reading

Hands-On Lab

🧪 D10 lab: implementing four query transformations plus intent routing, with their benefits and costs on the evaluation set

Code location: labs/rag-14days/day-10-query-transformations

Acceptance criteria:

  1. MOCK=1 pnpm start finishes with all 8 acceptance items green (starter/ as-is has 6 crosses).
  2. The comparison table's multi-query row shows 60 retrievals against the baseline's 20 — multi-query's cost must be visible.
  3. The intent routing with multi-hop budget row shows 100% multi-hop recall with no drop in single-hop recall.
  4. Without rewriting, turn two of the conversation has not one candidate clearing the gate, and after rewriting the organization chart document enters the context.
  5. The chat turn shows 0 retrievals.

Confirm three things before starting. One, you have day eight's evaluation script running, since today reuses its 20 questions, metric criteria, and context budget. Two, do not use mean reciprocal rank as evidence: day eight's baseline already has it at 1.0000 because questions written backwards from the corpus put the answer document first — metric saturation rather than a perfect system, and no technique toggled today will move it; single-document recall is likewise already 100%. All of today's evidence must come from multi-hop recall, nDCG, and the refusal rate. Three, read the lab README's MOCK note — offline, all five techniques are deterministic rule-based stand-ins, and the metrics only prove the code path connects rather than representing a real model's effect.

  1. Implement query rewriting and reference resolution, run the three-turn conversation, and watch turn two go from can only refuse to the organization chart document entering the context.
  2. Implement hypothetical documents and multi-query, fuse the several rankings with day nine's reciprocal rank fusion, and confirm retrievals rise from 20 to 60.
  3. Write the intent classifier splitting three ways and give multi-hop its own higher context budget, watching multi-hop recall jump from 50% to 100%.
  4. Toggle the five techniques one at a time, recording each configuration's recall, nDCG, model calls, retrievals, and context tokens in one table.
  5. Write down your default configuration, explaining line by line why three of them are off — that is today's real assignment.

Interview Questions

Today's 4 questions are in the question bank below, weighted toward choosing among query understanding techniques, reference resolution in multi-turn conversation, and the latency bill of extra calls. Expand each one and read the analysis before the key points — practicing the derivation beats memorizing bullets. The high-frequency labels for the domestic and overseas markets are there so you can pick by target market.

Checklist and Tomorrow

  • Explain what shortfall each of query rewriting, hypothetical document embeddings, multi-query, and step-back prompting patches
  • Implement an intent router that sends chat, retrieval-needed questions, and multi-hop questions down different handling paths
  • Prove the net benefit of each query-side technique with evaluation, and work out the extra latency and call count each one adds
  • Explain why rewriting comes before routing, and why mean reciprocal rank cannot be evidence today
  • All 5 acceptance criteria of the lab pass, with a default configuration and the reasons for switching several off written down
  • Answer at least 3 of the 4 interview questions without looking at the key points

Tomorrow (D11) returns to the index side for parent-child documents, summary indexes, and contextual retrieval. Why that order: today proved that with the retriever unchanged, editing only the query takes multi-hop recall from 50% to 100% — earn the query side's cheap money first, and only then is it the index structure's turn. Tomorrow handles precisely what the query side cannot solve: chunks that lost their locating information when cut, which no amount of query editing restores.

Interview questions

  • Why does HyDE (hypothetical document embeddings) work, and when does it steer retrieval in the wrong direction?假设文档嵌入(HyDE)为什么有效?它在什么情况下会把检索带偏?
    Common in ChinaCommon overseasIntermediate#hyde#query-transformation#retrieval-quality

    How to reason about it · think before answering

    1. The tell is in the second half. Anyone can recite why HyDE works; only someone who has run it on real data can say when it hurts.
    2. Give the mechanism first: dense retrieval compares semantic similarity, but a user's question and a policy paragraph differ in register, syntax and vocabulary. HyDE has the model draft a fake passage that looks like the target document, then retrieves with that vector — effectively moving the query into the documents' register.
    3. Then kill the common misreading: the factual accuracy of the draft does not matter, because it is never shown to the user. It only contributes a direction in embedding space.
    4. Two failure modes. The model invents an over-specific field or process name that does not exist in the corpus, and the vector chases something imaginary. Or the corpus genuinely has no answer, and the fabricated passage finds plausible-looking neighbors anyway — abstention rate drops and hallucination rate climbs.
    5. Pair the risk with a mitigation: gate admission on each retriever's raw score, never on the fused score (fused scores are relative, so even the worst batch tops out at 1.0); and treat the hypothetical document as a second query fused with the original rather than a replacement, so a bad draft can only dilute the signal, not erase it.
    6. Expect the follow-up on cost. The draft runs to a hundred-plus output tokens, an order of magnitude more than a rewrite, and it doubles retrieval calls. That is why it belongs in an A/B queue, not in the default config.

    分析过程 · 先想清楚再作答

    1. 这题的题眼在后半句。前半句网上到处都能抄到,能不能说清「什么时候不该用」才是区分度所在——只答前半句的人,多半没在真实语料上跑过。
    2. 先给机制:向量检索比的是语义相似度,而用户的疑问句和文档里的制度条文在文体、句式、用词上都不同类。HyDE 先让模型编一段「长得像目标文档」的假文本,用它的向量去找邻居,等于把查询搬进了文档所在的那个语域。
    3. 紧接着点破一个常见误解:这段假文本的**事实对不对根本不重要**,因为它不给用户看,只贡献一个向量方向。理解到这一层,才算真懂它为什么不怕模型瞎编。
    4. 带偏有两种典型情况。一是模型编得太具体,给出语料里根本不存在的字段名或流程名,向量朝着一个不存在的方向去了;二是语料里压根没有答案,本该拒答的问题被编出来的假文档匹配到几个「看起来挺像」的邻居,拒答率掉下去、瞎编率涨上来。
    5. 说完风险要给对策,这一步最见工程经验:门槛卡在**每一路检索器的原始分**上而不是融合分上(融合分是相对的,最不相干的一批也能拿最高分);以及把假设文档当成**第二个检索式与原问题融合**,而不是直接替换原问题——替换在模型编歪时会把原问题的信号一起丢掉。
    6. 可预期的追问是「它多花多少钱」。答:假设文档要写上百字,输出 token 是查询改写的十几倍,是查询侧四种手法里最贵的一次调用,而且检索次数翻倍。所以它通常不该默认打开,应该进 A/B 队列。

    Key points

    • It works by register alignment: a question and a policy paragraph sit in different neighbourhoods, and the fake passage moves the query into the document's.
    • The draft's factual accuracy is irrelevant — it only supplies a direction and is never shown to the user.
    • It misfires when the model invents over-specific details, or when the corpus has no answer and the fabrication finds plausible neighbors anyway.
    • Two guardrails: gate on raw per-route scores, not fused ones; fuse the hypothetical document with the original query instead of replacing it.
    • It is the most expensive query-side technique (long output plus doubled retrievals), so keep it off by default and A/B it.

    答题要点

    • 有效的原因是语域对齐:疑问句和制度条文本来不在一个语义邻域,假设文档把查询搬到了文档那一侧。
    • 假文本的事实对错不重要,它只贡献一个向量方向,不展示给用户。
    • 带偏的两种情况:编得太具体,追一个语料里不存在的方向;本该拒答的问题被假文档匹配上,拒答率下降。
    • 两条护栏:门槛卡原始分不卡融合分;把假设文档当第二个检索式融合,而不是替换原问题。
    • 成本上它是查询侧最贵的一项(长输出加检索次数翻倍),默认关闭、按场景 A/B。
  • How do you handle coreference in multi-turn RAG, and what is the classic failure when you skip it?多轮对话里怎么处理指代?不做指代消解最典型的翻车场景是什么?
    Common in ChinaCommon overseasBasic#coreference#multi-turn#query-rewriting

    How to reason about it · think before answering

    1. This is a warm-up question, but there is still a gap between answers. Saying "just concatenate the history into the query" invites a follow-up about growing histories that most candidates cannot handle.
    2. State the mechanism: insert a short rewrite call before retrieval that takes the last few turns plus the current question and returns one retrieval-ready line. Set temperature to 0 so the same input always yields the same query, and forbid the model from answering the question in the prompt.
    3. Explain why concatenation is worse: history grows without bound, filler words dilute inverse document frequency, and the previous answer leaks in — you end up retrieving an answer with an answer. The rewriter emits one sentence, not a transcript.
    4. Make the failure concrete. Turn one: "who must sign off on this operation?" Answer: "the platform team lead." Turn two: "what is that person's name?" Retrieved unresolved, not a single candidate clears the admission gate and the system refuses — even though the corpus contains the answer. The failure is not a wrong answer, it is a false "not found" right after the user's own question.
    5. Add the ordering trap: rewrite before intent routing. A pronoun is a classic multi-hop signal, so an unresolved query gets routed to the expensive path for nothing; after rewriting it is an ordinary single-hop question. Multi-query and step-back must also sit downstream of the rewrite, or one unresolved pronoun becomes three.
    6. Expect "how do you decide when to rewrite?" Trigger on short queries, pronouns and elliptical follow-ups; skip on a clearly new topic. The check is nearly free and removes most of the calls.

    分析过程 · 先想清楚再作答

    1. 这是一道送分题,但送分题也有高下之分:只说「把历史拼进查询里」的答案,会被追问一句「历史越拼越长怎么办」就卡住。
    2. 先把做法说清:在检索之前加一次很短的改写调用,输入是最近几轮对话加本轮问题,输出是一行可以直接检索的检索式;温度设 0 保证同一句话每次改成同一个结果,并在提示词里明确禁止模型顺手回答问题。
    3. 为什么不是「把历史整个拼进查询」:历史越拼越长,噪声词把逆文档频率摊薄,检索反而更差;而且历史里包含上一轮的答案,等于拿答案去检索答案。改写的产出是一句话,不是一段历史。
    4. 最典型的翻车场景要举实例:上一轮问「这个操作必须由谁审批」,答「必须由某某组组长审批」;这一轮问「这个人叫什么名字」。不消解直接检索这七个字,实测是**一条候选都过不了门槛,系统只能拒答**。注意失败方式不是答错,是「明明语料里有答案却说找不到」,用户体验是崩塌式的。
    5. 补一条顺序上的坑:改写必须在意图路由**之前**。「这个人」是典型的多跳信号词,路由看到它会判成多跳、白跑一轮;改写之后它只是个普通单跳问题。同理,多路查询、后退提问也都要建立在改写后的那句话上,否则错误被放大好几倍。
    6. 可预期的追问是「怎么知道要不要改写」。答:短问题、含指代词、含省略(「那审计日志呢」)时才触发,纯新话题跳过——这一步很便宜,但能省掉一大半调用。

    Key points

    • Add a short rewrite call before retrieval: last few turns plus current question in, one retrieval line out, temperature 0, answering explicitly forbidden.
    • Do not splice the whole history into the query — it grows unbounded, dilutes IDF, and leaks the previous answer into the search.
    • Classic failure: an unresolved pronoun means no candidate clears the gate, so the system refuses a question the corpus can answer.
    • That false "not found" hurts more than a wrong answer, since the user just asked about the same thing.
    • Order matters: rewrite first, then route; multi-query and step-back both build on the rewritten query.

    答题要点

    • 在检索前加一次短改写调用,输入最近几轮加本轮问题,输出一行检索式,温度 0,禁止模型回答问题。
    • 不要把历史整段拼进查询:越拼越长、噪声稀释逆文档频率,还会拿上一轮的答案去检索。
    • 典型翻车:上一轮的「这个人 / 他 / 那个」不消解,检索一条都过不了门槛,系统在有答案的情况下拒答。
    • 失败方式是「假的查不到」,比答错更伤体验,因为用户刚刚才问过同一件事。
    • 顺序:先改写、再路由,多路查询与后退提问都建立在改写后的查询上。
  • Query rewriting adds a model call per question and doubles end-to-end latency. How do you decide whether it is worth paying?上线查询改写之后每问多了一次模型调用,端到端延迟涨了一倍,你怎么判断这笔开销值不值?
    Common in ChinaCommon overseasDeep dive#cost-tradeoff#latency#query-rewriting

    How to reason about it · think before answering

    1. This question is about turning an engineering judgment into numbers. "Rewriting obviously helps quality" is a fail — the candidate never measured the gain.
    2. Start with one question that nearly settles it: which slice of traffic does the gain land on? Query rewriting buys almost nothing on single-turn questions (we measured identical metrics with it on and off across 20 single-turn items); the entire payoff is in follow-up turns. So step one is to pull the share of multi-turn sessions from production logs.
    3. Step two is to lay out all three ledgers, because one alone cannot support a decision: how much the metrics moved on a fixed golden set, how much latency grew (a rewrite is a short-output task, so a cheap fast model often costs a few hundred milliseconds rather than doubling anything), and how many extra calls were added — one model call for rewriting versus one call plus several retrievals for multi-query is a completely different cost shape.
    4. Step three is to price the cheaper variants before deciding: rewrite only when a trigger fires (short query, pronoun, ellipsis), cache rewrites per session, and run a small model instead of the main one. These usually remove most of the cost while keeping the gain.
    5. Land on a usable rule: gain times affected traffic share, divided by added latency and cost, ranked against your other candidate optimizations. Rewriting usually ranks high because its failure mode is a false "not found" immediately after the user's own question — an abandonment-grade experience bug, not a few metric points.
    6. Expect "what if the latency genuinely is unacceptable?" Fire the rewrite and the first retrieval in parallel: search with the raw query immediately, search again when the rewrite returns, and fuse both rankings. You pay a max instead of a sum, at the cost of one extra retrieval.

    分析过程 · 先想清楚再作答

    1. 这题考的是「能不能把工程判断落到数字上」。凡是回答「改写当然要做,能提升效果」的,一律判为没做过——他连收益是多少都没量。
    2. 先问自己一句:**收益出现在哪一类流量上**。这一条几乎决定了答案。查询改写在单轮问答上的收益接近零(我们在 20 道单轮题上实测开关它指标一模一样),收益全在多轮追问。所以第一步是去线上日志里查多轮会话占比,占比很低的话这笔钱不该花在全量流量上。
    3. 第二步是把三笔账摆齐,缺一笔就不能下判断:指标涨了多少(用固定的标准答案集跑,不要用感觉)、延迟涨了多少(改写是短输出任务,可以换便宜快的那一档模型,往往只多两三百毫秒而不是翻倍)、多了几次调用(改写是一次,多路查询是一次调用加几次检索,成本结构完全不同,别混着算)。
    4. 第三步是找**便宜的替代路径**再比一次:只在命中触发条件时才改写(短问题、含指代词、含省略),纯新话题直接跳过;改写结果按会话缓存;用小模型跑改写而不是主模型。这三招通常能把这笔开销压掉一大半,而收益几乎不掉。
    5. 结论要落成一条可执行的判据:**收益乘以受影响流量占比,除以增加的延迟与成本**,跟你手上其他候选优化排个序。改写通常能排到很前面,因为它的失败方式是「用户明明追问同一件事却被告知查不到」,那是会直接导致弃用的体验故障,不只是指标掉几个点。
    6. 可预期的追问是「延迟真的不能接受怎么办」。答:把改写和第一次检索**并行发**,用原查询先检索一路,改写回来后再补一路,两路用倒数排名融合合起来——延迟只多一个 max 而不是一个加法,代价是多一次检索。

    Key points

    • Locate the gain first: rewriting is near-zero on single-turn traffic and pays off on follow-ups, so start from the share of multi-turn sessions.
    • All three ledgers are mandatory: metric delta on a golden set, added latency, added calls and token cost.
    • Try the cheap variants before deciding: conditional triggering, per-session caching, and a small model for the rewrite.
    • Decide on gain times affected traffic share over added latency and cost, then rank it against your other optimizations.
    • If latency is a hard constraint, fire the rewrite in parallel with the first retrieval and fuse both rankings, turning a sum into a max.

    答题要点

    • 先定位收益落在哪一类流量:改写在单轮上接近零收益,价值全在多轮追问,先查多轮会话占比。
    • 三笔账缺一不可:标准答案集上的指标变化、增加的延迟、增加的调用次数与 token 成本。
    • 先试便宜的替代路径:条件触发、按会话缓存、用小模型跑改写,通常能压掉大半开销。
    • 判据是「收益 × 受影响流量占比 ÷ 增加的延迟与成本」,再和其他候选优化排序。
    • 延迟真的卡死时,把改写与首次检索并行发,两路名次用倒数排名融合,延迟从加法变成取最大值。
  • What happens when intent routing misclassifies, and how would you design the fallback?意图路由判错了会怎样?你会怎么设计兜底?
    Common in ChinaCommon overseasIntermediate#intent-routing#fallback#observability

    How to reason about it · think before answering

    1. This tests whether you have thought about the direction of the error. A router is a classifier and classifiers misfire; "add more training data" is not a fallback design.
    2. Break the errors down by direction — that is the backbone of the answer. Across three routes (direct answer, single-hop, multi-hop) the six confusions carry wildly asymmetric costs. Routing a retrieval-worthy question to a direct answer leaves the model with no material at all, so it fabricates: the most expensive error. Routing chit-chat to single-hop merely wastes one retrieval. Routing multi-hop to single-hop just yields an incomplete answer.
    3. The conclusion follows: bias the fallback toward spending a little more, and default to single-hop retrieval whenever the classifier is unsure. Single-hop is the cheapest error to make, and it is recoverable — with partial material the model can still say it only found half the answer; with no material it can only invent one.
    4. Add a runtime fallback, which beats better up-front classification: after a direct-answer routing, if the draft reply contains figures, amounts or dates that need a source, fall back to retrieval and answer again; after a single-hop routing, if no candidate clears the admission gate, escalate to multi-hop or abstain. Correcting the earlier decision with the later observation is the single most useful pattern in routing systems.
    5. Mention observability: log every routing decision with the raw question, the label, and whether a fallback fired. Without that log you know neither how accurate the router is nor what to train the next version on.
    6. Expect "when should you skip routing entirely?" When chit-chat is a small share of traffic and multi-hop questions are rare, the classification call costs more than it saves. In our 30-document lab the real gain from routing was not saved retrievals but the ability to give recognized multi-hop questions a larger context budget.

    分析过程 · 先想清楚再作答

    1. 这题在考「有没有想过错误的方向」。路由是分类器,分类器一定会错;只答「多加训练数据提高准确率」的,等于没回答兜底怎么设计。
    2. 先把错误按方向拆开,这一步是整题的骨架:三条路(直接回答、单跳检索、多跳检索)两两误判,代价完全不对称。把该检索的判成直接回答,模型手里一点材料都没有,只能编,这是最贵的一种错;把闲聊判成单跳,只是白花一次检索;把多跳判成单跳,只是少查一轮、答得不全。
    3. 结论顺势就出来了:**兜底方向要偏向「多花一点钱」,判不出来一律退回单跳检索。** 单跳是三条路里错得最轻的一条,而且它的错误是可恢复的——材料不全模型还能说「资料里只查到一半」,材料为空它就只能编。
    4. 再补一层运行时兜底,比事前分类更管用:分类成直接回答之后,如果模型的回答里出现了具体数字、金额、日期这类需要出处的内容,就回退去检索一次再答;分类成单跳之后,如果检索侧一条都没过门槛,就升级走多跳或直接拒答。**用后一步的观测结果纠正前一步的判断**,这是路由系统最实用的一条设计。
    5. 还要提一句可观测性:路由的每一次判定都要落日志,带上原始问题、判定结果、后续是否发生了兜底升级。没有这份日志,你既不知道路由准不准,也没法攒出下一版的训练集。
    6. 可预期的追问是「什么时候干脆别做路由」。答:流量里闲聊占比很低、且多跳问题很少时,路由省下的钱还不够付分类调用的钱,这时候直接全部走单跳更划算——我们在 30 篇语料的实验里就看到,路由真正的收益并不在省检索,而在于认出多跳之后给它更高的上下文预算。

    Key points

    • The three routes have asymmetric error costs: sending a retrieval-worthy question to a direct answer is the worst, while routing chit-chat to single-hop only wastes one retrieval.
    • Bias the fallback toward spending more: default to single-hop whenever the classifier is unsure, since that error is the mildest and is recoverable.
    • Add runtime fallbacks: re-retrieve if a direct answer contains figures that need a source; escalate or abstain if no single-hop candidate clears the gate.
    • Log every routing decision — raw question, label, whether a fallback fired — for both monitoring and the next training set.
    • When chit-chat and multi-hop are both rare, the classification call costs more than it saves; route everything to single-hop instead.

    答题要点

    • 三条路的误判代价不对称:把该检索的判成直接回答最贵(模型没材料只能编),把闲聊判成单跳只是白花一次检索。
    • 兜底方向偏向多花钱:判不出来一律退回单跳检索,它是错得最轻且可恢复的一条路。
    • 加运行时兜底:直接回答里出现需要出处的数字就补一次检索;单跳检索一条都没过门槛就升级或拒答。
    • 每一次路由判定都落日志(原始问题、判定结果、是否触发兜底),既用于监控也用于攒下一版训练集。
    • 闲聊与多跳占比都很低时,路由省的钱付不起分类调用,直接全走单跳更划算。

Comments