Query Rewriting and HyDE: Better Queries for the Search You Have
Hands-on lab · IDE in your browser

Query Rewriting and HyDE: Better Queries for the Search You Have

Improve retrieval without touching the index: rewrite follow-up questions into standalone ones, search several model-written phrasings and fuse them, search with a hypothetical answer (HyDE), then fold rewriting and HyDE into one model call per question and prove it on unseen questions.

Time
50 min
Checked steps
4
Level
Intermediate
Setup
None
Read step 1

Hands-on labs require Pro · $29.99/mo · cancel anytime

Lab cockpit50 min · 4 stepsSession running
3 / 4 steps passingShip it in one call · step 4 of 4
rewrite.py▶ Run✓ Check
# ---------- Step 4: the pipeline you ship ----------ONE_CALL_PROMPT = """Write the paragraph of a company staff handbook that answers the employee's latest question. Use formal policy language, two or three sentences. Invent plausible details if you do not know them.{conversation}Latest question: {question}"""  def search_text(history, question):    """One chat call: the question followed by a hypothetical handbook paragraph written with the conversation in view."""   
TerminalOutput

The job

Northwind Freight's HR assistant answers from a formal staff handbook, and the handbook sits behind a keyword index the company cannot replace this quarter. Employees ask about side gigs, sick kids and working from Portugal; the handbook talks about secondary employment, dependant care and overseas jurisdictions. Follow-up questions say "it" and "that". You make the queries good enough for the index you have, within a budget of one model call per question.

4 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    Make follow-ups standalone

    Northwind Freight's HR assistant answers from the staff handbook (handbook.json, 50 clauses).

    You writestandalone()
  2. 2

    Search several phrasings

    Most first questions still miss: "I've got a little side gig doing weddings" shares no words with the clause titled "Secondary employment".

    You writeparse_lines()multi_search()
  3. 3

    Search with a hypothetical answer (HyDE)

    Rewordings of a question still read like questions.

    You writehypothetical()hyde_search()
  4. 4

    Ship it in one call

    HyDE on standalone questions spends two model calls on every follow-up: one to rewrite it, one to write the paragraph.

    You writesearch_text()

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: Make follow-ups standalone

Northwind Freight's HR assistant answers from the staff handbook (handbook.json, 50 clauses). The handbook sits behind a keyword index: harness.keyword_search(query, k) ranks clauses with BM25, and it is the search you have. Everything in this lab changes the query and nothing else.

questions.json holds 28 questions, each with the clause that answers it. Sixteen are first questions in an employee's own words. Twelve are follow-ups that only make sense with the conversation before them: "And can I roll any over into next year?" contains no word about holiday, so a keyword search cannot find it.

The fix is to rewrite a follow-up into a standalone question before searching. The rewrite should carry the topic over from the conversation and leave out the assistant's figures, which would pull the search back to the clause that was already answered.

Do this

1. Write standalone(history, question): with no history, return the question as it is, without a model call. Otherwise write each history message as a line role: content, fill STANDALONE_PROMPT, call harness.chat(prompt, 80) and return the reply stripped of spaces and quotes. If the reply is empty, return the question.

2. Run. It prints the question-as-typed score first, then yours. Compare the follow-up rows.

rewrite.py, the file you edit70 lines
"""Better queries for a keyword index. Every function that asks the model goes through harness.chat()."""
import re

import harness

# ---------- Step 1: standalone questions ----------
STANDALONE_PROMPT = """Here is a conversation between an employee and the HR assistant.

{conversation}

The employee now asks: "{question}"

Rewrite this as one standalone question about company policy. Name the policy topic it is about. Keep the employee's meaning exactly, and do not include the assistant's figures or details. Reply with the question only."""


def standalone(history, question):
    """The question rewritten so it makes sense without the conversation. With no history, the question itself."""
    # TODO (Step 1): with no history, return the question unchanged (no model call).
    # Otherwise write the history as lines "role: content", fill STANDALONE_PROMPT, call harness.chat(prompt, 80)
    # and return the reply stripped of spaces and quotes (or the question, if the reply is empty).
    raise NotImplementedError("Step 1: write standalone()")


# ---------- Step 2: multi-query ----------
VARIANTS_PROMPT = """An employee asked this question about the staff handbook:
{question}

Write {n} different versions of the question in the formal language an HR handbook would use. One per line, no numbering."""


def parse_lines(reply, n):
    """The first n distinct non-empty lines of the reply, with list markers ("1.", "2)", "-", "*") removed."""
    raise NotImplementedError("parse_lines() arrives in Step 2")


def multi_search(history, question, n=3, k=3):
    """Search the standalone question and n variants of it, fuse the rankings with harness.rrf(), keep k."""
    raise NotImplementedError("multi_search() arrives in Step 2")


# ---------- Step 3: HyDE ----------
HYDE_PROMPT = """Write the paragraph of a company staff handbook that answers this question. Use formal policy language, two or three sentences. Invent plausible details if you do not know them.

Question: {question}"""


def hypothetical(question):
    """A made-up handbook paragraph that would answer the question."""
    raise NotImplementedError("hypothetical() arrives in Step 3")


def hyde_search(history, question, k=3):
    """Keyword-search the standalone question followed by its hypothetical paragraph."""
    raise NotImplementedError("hyde_search() arrives in Step 3")


# ---------- Step 4: the pipeline you ship ----------
ONE_CALL_PROMPT = """Write the paragraph of a company staff handbook that answers the employee's latest question. Use formal policy language, two or three sentences. Invent plausible details if you do not know them.
{conversation}
Latest question: {question}"""


def search_text(history, question):
    """One chat call: the question followed by a hypothetical handbook paragraph written with the conversation in view."""
    raise NotImplementedError("search_text() arrives in Step 4")


def answer_ids(history, question, k=3):
    """The shipped query pipeline: the clause ids to hand to the answering model."""
    return harness.keyword_search(search_text(history, question), k)
Provided for you:evaluate.pyhandbook.jsonharness.pyquestions.json

Frequently asked questions

What is HyDE?

Hypothetical Document Embeddings: the model writes the answer it imagines for a question, and you search with that text. The details are invented, but it is phrased like the documents you want, so it matches them better than the question does.

What is query condensation?

Rewriting a follow-up question such as 'And can I roll any over?' into a standalone question that names its topic, using the conversation so far, so that search can find the right document.

Does multi-query retrieval help?

It raises recall by searching several phrasings and fusing the results, but in the lab it also lowers how often the best document comes first, and it costs a model call and several searches per question.

Can query rewriting help keyword search?

Yes. In the lab the index is BM25, and rewriting plus a hypothetical answer takes the right clause from the top three for under half the questions to the top three for most of them.

Query rewriting and HyDE, measured on a keyword index

When users and documents use different words, retrieval fails before the model ever sees the question. Query rewriting fixes this on the query side: turn follow-ups into standalone questions, search several phrasings, or search with a hypothetical answer the model writes first (HyDE). In this lab you build each technique against a BM25 index over a staff handbook, measure what it adds and what it costs in model calls, and ship a pipeline that does the rewriting and HyDE in a single call.