Measure hallucination rates with a factuality scorer and retrieval recall; reduce them by tightening hybrid search, using low‑temperature prompting, and adding a verification step.
1. Build a gold‑standard eval set – queries, expected answers, and source IDs.
2. Compute metrics:
- Recall@k = (relevant docs retrieved ÷ total relevant) for k=10.
- Faithfulness = LLM‑based score (e.g., gpt‑4o with function call) returning 0‑1. Formula: score = avg(token‑level similarity).
- Verifier pass rate = fraction of answers with verification score ≥ 0.8.
3. Instrument the pipeline – log source_ids, retriever_score, and llm_confidence for each request.
4. Reduce hallucinations:
- Hybrid search: combine FAISS vector store with BM25, lexical weight 0.6, k=10, score_threshold=0.2.
- Prompt tuning: set temperature=0.0, top_p=0.9, prepend "Answer using only the provided context:".
- Rerank: cross‑encoder sentence‑transformers/msmarco-MiniLM-L-12-v3 with threshold 0.75.
- Post‑generation verification: run answer through a self‑check LLM; if score < 0.8, fall back to a retrieval‑only response.
5. Monitor – alert when Recall@10 < 0.85 or Faithfulness < 0.9 for two consecutive batches.
Metric comparison
Metric | Tool | Target
--- | --- | ---
Recall@k | FAISS + BM25 | ≥0.85
Faithfulness | OpenAI gpt-4o scorer | ≥0.9
Verifier score | Self‑check function | ≥0.8
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.vectorstores import FAISS
from langchain.retrievers import BM25Retriever
vector = FAISS.from_documents(docs, embedding_model)
bm25 = BM25Retriever.from_documents(docs)
hybrid = vector.as_retriever(search_kwargs={"k":10, "score_threshold":0.2})
hybrid.combine(bm25, weight=0.6) # lexical weight 0.6
qa = RetrievalQA.from_chain_type(
llm=OpenAI(model="gpt-4o-mini", temperature=0.0, top_p=0.9),
retriever=hybrid,
return_source_documents=True,
)def verify(answer, sources):
prompt = f"Check factuality of the answer against the sources. Return a score 0‑1."
resp = openai.ChatCompletion.create(
model="gpt-4o",
messages=[{"role":"user","content":prompt}],
temperature=0,
)
return float(resp.choices[0].message.content.strip())