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.
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)evaluate.pyhandbook.jsonharness.pyquestions.json