Query Engines
A QueryEngine is the high-level “ask the index, get an answer” API. The one-liner is index.as_query_engine(). Querying is three stages: retrieval → postprocessing → response synthesis. If you only need hits and will build the prompt yourself, use a Retriever from Retrieval.
Pin synthesis with Settings.llm = OpenAI(model="gpt-4o-mini"). Generation historically defaulted to gpt-3.5-turbo; follow official docs.
as_query_engine
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do in college?")
print(response)
print(response.source_nodes) # which chunks were used
Common knobs:
query_engine = index.as_query_engine(
similarity_top_k=5,
response_mode="compact",
verbose=True,
)
Default top-k is small (docs often show 2). Raise similarity_top_k before adding a reranker. Async: await query_engine.aquery(...)—handy inside FunctionAgent tools.
response_mode
After retrieval, the synthesizer decides how chunks reach the LLM:
qe = index.as_query_engine(response_mode="tree_summarize", similarity_top_k=8)
print(qe.query("Summarize the data/ folder in three paragraphs."))
Low-level: RetrieverQueryEngine
When you need custom top-k and score cutoffs, assemble explicitly:
from llama_index.core import get_response_synthesizer
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor
retriever = VectorIndexRetriever(index=index, similarity_top_k=10)
synth = get_response_synthesizer(response_mode="compact")
query_engine = RetrieverQueryEngine(
retriever=retriever,
response_synthesizer=synth,
node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.7)],
)
response = query_engine.query("What did the author do growing up?")
A strict similarity_cutoff often means “no answer”—that is the point: empty is better than stuffing irrelevant chunks. Tune with no_text or by printing scores.
Chat Engine: multi-turn over data
QueryEngine is stateless by default. For follow-ups and coreference:
chat_engine = index.as_chat_engine()
print(chat_engine.chat("What did the author do in college?"))
print(chat_engine.chat("Go on, more specifically."))
chat_mode values (see current docs) include condensing history into a standalone retrieval query, or stuffing retrieved text into the system prompt. Streaming:
streaming = chat_engine.stream_chat("One-sentence summary.")
for token in streaming.response_gen:
print(token, end="", flush=True)
When the model must decide whether to retrieve, prefer FunctionAgent + Context from Quick Start over stretching Chat Engine into an agent.
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
tool = QueryEngineTool.from_defaults(
query_engine=index.as_query_engine(similarity_top_k=4),
name="handbook",
description="Search the employee handbook. Use for policy questions.",
)
agent = FunctionAgent(
tools=[tool],
llm=OpenAI(model="gpt-4o-mini"),
system_prompt="Prefer the handbook tool for factual policy answers.",
)
You can also wrap aquery in async def search_documents(query: str) like the official starter. Multiple indexes (API ref vs tutorials) → multiple QueryEngineTools and let the model pick.
Next steps