FaithfulnessEvaluator gating
WorkedFaithfulness-Drop Gating for Glossary Generation
This worked example shows how to use LlamaIndex's FaithfulnessEvaluator to gate a generated definition — dropping the answer when the retrieved documents do not support it. This ensures only grounded definitions enter the glossary, preventing hallucinated or unsupported claims.
The Problem
When a retriever returns irrelevant nodes, the LLM may produce a plausible-sounding but ungrounded answer. In a glossary, such noise is unacceptable. The repository uses a faithfulness-drop discipline: after generating a definition from retrieved contexts, it evaluates faithfulness. If the definition is not faithful to the contexts, the answer is discarded. Additionally, a transient judge error (e.g., LLM hiccup) must not crash the whole run — it should be treated as a drop-and-continue.
The Worked Solution
Below is the complete implementation. The function generate_with_faithfulness_gate:
- Retrieve relevant nodes using
retriever.retrieve(query). - Synthesize an answer using
synth.synthesize(query, nodes), producing a raw string. - Clean the answer (optional, but shown as in the source).
- Check for non-answer markers (e.g., retrieval miss) – skip early if detected.
- Extract contexts from the retrieved nodes:
contexts = [n.node.get_content() for n in nodes]. - Evaluate faithfulness using
faithfulness.evaluate(response=definition, contexts=contexts).passing. - Handle errors in the evaluation call: wrap in try/except, print a warning, and treat as a drop (return
None). - Drop if
passingisFalse; otherwise return the cleaned definition.
The function returns None when the answer is dropped, making it easy for the caller to skip that term.
Code Walkthrough
def generate_with_faithfulness_gate(term: str, retriever, synth, faithfulness) -> str | None:
"""Return a faithful definition for `term`, or None if unsupported."""
# 1. Retrieve nodes
nodes = retriever.retrieve(f"{term} in LlamaIndex")
if not nodes:
print(f"WARNING no nodes for {term!r}")
return None
# 2. Synthesize answer
raw = str(synth.synthesize(f"{term} in LlamaIndex", nodes))
if not raw:
return None
# 3. Clean the answer (simple whitespace flatten; full _clean in source is more thorough)
definition = " ".join(raw.split()).strip()
# 4. Check for non-answer patterns (simplified)
import re
if re.search(r"(does not|don't|not.*(contain|mention|found|covered))", definition, re.IGNORECASE):
print(f"WARNING non-answer for {term!r}; skipped")
return None
# 5. Extract contexts for faithfulness evaluation
contexts = [n.node.get_content() for n in nodes]
# 6. Evaluate faithfulness with error handling
try:
passing = faithfulness.evaluate(response=definition, contexts=contexts).passing
except Exception as exc:
print(f"WARNING faithfulness eval error for {term!r}: {exc}; skipped")
return None
# 7. Drop if not faithful
if not passing:
print(f"WARNING unfaithful definition for {term!r}; skipped")
return None
# 8. Return the faithful definition
return definition
Key Points
- The evaluator is instantiated once and reused:
FaithfulnessEvaluator(llm=llm). - The
responseparameter is the cleaned definition string. - The
contextsparameter is a list of strings (one per retrieved node). - The return value
.passingis a boolean;Truemeans the answer is faithful. - The try/except ensures a single term's evaluation failure does not abort the entire pipeline.
Now examine the starter code and fill in the missing parts to implement the same logic.
- roadmap-kg/kg/glossary_llamaindex.py:215-223
- roadmap-kg/kg/glossary_llamaindex.py:267-311
- roadmap-kg/kg/glossary_llamaindex.py:384-420