Hypothetical Answers, Real Retrieval — How HyDE Fixes the Query-Document Mismatch in RAG

Standard RAG embeds a short question and matches it against long, declarative documents, and that mismatch quietly breaks retrieval more often than teams realize. HyDE fixes it by retrieving with a hypothetical answer instead of the raw query.

Hypothetical Answers, Real Retrieval — How HyDE Fixes the Query-Document Mismatch in RAG
Hypothetical Document Embeddings

Most RAG pipelines embed the user's question and search for it directly against a vector store of document chunks. It feels obvious enough that nobody questions it. But a question and its answer do not actually look alike in embedding space, and that quiet mismatch is responsible for a meaningful chunk of the retrieval failures teams spend weeks debugging.

The mismatch nobody talks about

Take a query like "What caused the 2008 financial crisis?" and the passage that actually answers it, something like "Subprime mortgage defaults, driven by lax underwriting standards and excessive leverage in mortgage-backed securities, triggered a cascading collapse across the banking sector." The two sentences share almost no surface vocabulary. One is short, interrogative, and vague. The other is long, declarative, and specific. Embedding models are trained mostly on semantic similarity between similar types of text, so a question-to-answer comparison is already working against the grain of how the encoder learned to represent meaning.

This is the same retrieval quality problem I went into more broadly in Context Engineering, but the query-document asymmetry deserves its own treatment because it has a surprisingly clean fix.

What HyDE actually does

HyDE stands for Hypothetical Document Embeddings, introduced by Gao et al. in 2022. Instead of embedding the raw query, you ask an LLM to write a hypothetical answer to it first, a short passage that looks like it could plausibly be the real answer. You embed that hypothetical passage, and you use its embedding to search the vector store, not the original question's embedding.

The flow looks like this:

  1. User asks a question.
  2. An LLM generates a hypothetical answer, with no access to the actual corpus, using only its parametric knowledge.
  3. That hypothetical answer gets embedded.
  4. The embedding is used to retrieve the top-k real chunks from your vector store.
  5. Those real chunks, not the hypothetical answer, get passed to the generation step as grounding context.

The hypothetical document is disposable. It never gets shown to the user and it is never treated as a source of truth. Its only job is to convert a short question into something that lives in the same semantic neighborhood as the real answer.

Why it works

The intuition is that a hypothetical answer, even a factually wrong one, tends to be written in the same register, length, and vocabulary as a real answer to that question. A model asked to answer "What caused the 2008 financial crisis?" will naturally reach for words like "subprime," "leverage," and "mortgage-backed securities," even without seeing your documents. That vocabulary overlap is what closes the gap in embedding space.

The original paper showed HyDE improving retrieval across multiple benchmarks without any task-specific fine-tuning of the retriever, which is part of why it caught on. Later benchmarking work through 2024 and 2025 has generally confirmed the gain holds up best on exactly the cases you would expect: short queries, domain-specific terminology, and zero-shot settings where you have no labeled query-document pairs to fine-tune against.

Standard RAG pitfalls HyDE actually avoids

A few concrete failure modes where this matters in practice:

  • Short, ambiguous queries. "How do I reset it?" embeds almost nothing useful on its own. A hypothetical answer forces the model to guess at what "it" is and what a reset procedure looks like, giving retrieval something to actually match against.
  • Domain jargon gaps. A user asking in plain language about a concept your documents describe in technical terms (legal, medical, financial) rarely uses the same words the documents do. The hypothetical answer, generated by a model with broad world knowledge, often bridges that vocabulary gap on its own.
  • Cold-start retrieval. If you have no query logs to fine-tune a retriever on, HyDE gives you a zero-shot improvement without needing labeled data.
  • Multi-hop questions. Questions that require connecting two ideas ("How did policy X affect metric Y two years later?") tend to produce hypothetical answers that name both concepts explicitly, which helps retrieval surface chunks that a literal keyword or embedding match on the question alone would miss.

Implementation with LangGraph

Here is a minimal HyDE node you can drop into a LangGraph retrieval pipeline. It sits before your existing retrieval step and simply swaps out what gets embedded.

from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from typing import TypedDict

class RAGState(TypedDict):
    query: str
    hypothetical_doc: str
    retrieved_chunks: list[str]
    answer: str

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")

def generate_hypothesis(state: RAGState) -> RAGState:
    prompt = (
        "Write a short, plausible passage that could answer this question. "
        "Do not hedge or say you're unsure. Write it as if it were an excerpt "
        f"from a real document.\n\nQuestion: {state['query']}"
    )
    response = llm.invoke(prompt)
    return {**state, "hypothetical_doc": response.content}

def retrieve(state: RAGState, vectorstore) -> RAGState:
    hyde_embedding = embeddings.embed_query(state["hypothetical_doc"])
    results = vectorstore.similarity_search_by_vector(hyde_embedding, k=5)
    return {**state, "retrieved_chunks": [r.page_content for r in results]}

def generate_answer(state: RAGState) -> RAGState:
    context = "\n\n".join(state["retrieved_chunks"])
    prompt = f"Answer using only this context:\n\n{context}\n\nQuestion: {state['query']}"
    response = llm.invoke(prompt)
    return {**state, "answer": response.content}

graph = StateGraph(RAGState)
graph.add_node("generate_hypothesis", generate_hypothesis)
graph.add_node("retrieve", retrieve)
graph.add_node("generate_answer", generate_answer)
graph.set_entry_point("generate_hypothesis")
graph.add_edge("generate_hypothesis", "retrieve")
graph.add_edge("retrieve", "generate_answer")
graph.add_edge("generate_answer", END)

app = graph.compile()

Two things worth calling out in production. First, this adds one extra LLM call per query, so latency and cost go up, usually by 200 to 500 milliseconds and a small fraction of a cent depending on the model. Second, you can hedge the risk of a bad hypothesis by embedding both the raw query and the hypothetical document, retrieving from each, and merging the results with reciprocal rank fusion, which is worth considering if you're already running a hybrid retrieval setup like the one I covered in The Retrieval Multiverse.

Where HyDE breaks down

It is not a universal fix, and it is worth being direct about where it can hurt you.

If the LLM's hypothetical answer is confidently wrong in a way that pulls it into a completely different semantic region than the real answer, retrieval can get worse, not better. This shows up most on questions involving facts the model has memorized incorrectly, recent events past its training cutoff, or highly specific numerical details it has to fabricate. In those cases, the hypothesis actively misleads retrieval rather than helping it.

It also does nothing for a corpus quality problem. If the right chunk simply is not in your vector store, or your chunking strategy has split it in a way that destroys context, HyDE cannot retrieve what isn't there or isn't retrievable in a coherent form. And the added latency and cost are real, even if small, and they compound if you're running this at scale with tight response-time budgets.

When to reach for it

HyDE earns its cost on short or ambiguous queries, domain-specific corpora with vocabulary gaps between how users ask and how documents are written, and zero-shot settings where you don't have query logs to fine-tune against. It is a weaker fit for factual lookup questions where the query already contains most of the specific terms you need, and for latency-sensitive applications where an extra LLM round trip is hard to justify.

If you're deciding between this and other retrieval architectures, it's worth reading it alongside Architectures of Choice, since HyDE composes cleanly with adaptive and corrective RAG patterns rather than competing with them. It is a retrieval-time technique, not a replacement for the surrounding architecture.

Subscribe to Vivek Wisdom

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe