Token-Level Hallucination Detection and Surgical Correction for LLMs
SourCandy is a production-grade framework that transforms unreliable LLM outputs into factually grounded responses through granular, token-level diagnostic analysis and surgical correction. Unlike traditional approaches that evaluate at the sentence or document level, SourCandy audits every single token using a novel Modified Cross Attention scoring mechanism.
SourCandy implements a research-driven Sourness Index that scores each token using:
S_t = 1 - tanh(wβΒ·G_t + wβΒ·P_t + wβΒ·R_t)
Where:
- G_t (Knowledge Grounding): Max softmax alignment between token and knowledge base
- P_t (Prompt Correspondence): Alignment between token and input prompt
- R_t (Relational Connectivity): Neurosymbolic graph path verification
- Token-Level Diagnostics: Granular sourness scores for every generated token
- Surgical Correction: Replace only hallucinated tokens while preserving context
- Knowledge Graph Integration: Neurosymbolic grounding with entity relations
- Domain Verification: Specialized validators for different knowledge domains
- Prompt Optimization: Auto-generate improved prompts from diagnostic feedback
- Visual Debugging: Heatmaps and distributions for sourness analysis
- Production Ready: Type-safe, well-documented, zero-comment clean code
# Basic installation
pip install -e .
# With visualization support
pip install -e ".[viz]"
# With all optional dependencies
pip install -e ".[all]"from sourcandy import Guard, KnowledgeGraph, SimpleEmbedder
# Initialize components
embedder = SimpleEmbedder()
knowledgeGraph = KnowledgeGraph()
# Populate knowledge graph
knowledgeGraph.addEntity(
entityId='python',
embedding=embedder.embedText('Python programming language'),
label='Python'
)
# Create Guard instance
guard = Guard(
baseLlm=yourLlm, # Any LLM with .invoke() method
knowledgeGraph=knowledgeGraph,
embedder=embedder,
sournessThreshold=0.6
)
# Add knowledge documents
guard.addKnowledgeDocuments([
'Python was created by Guido van Rossum in 1991.',
'Python is used for data science and web development.'
])
# Process query
response = guard.invoke('Tell me about Python')
print(f'Original: {response.diagnosticReport.rawOutput}')
print(f'Corrected: {response.finalContent}')
print(f'Is Sweet: {response.isSweet}')
print(f'Sourness: {response.diagnosticReport.sournessMap.aggregateSournessScore:.3f}')Input Prompt
β
Knowledge Retrieval
β
Base LLM Call β Raw Output
β
Token-Level Sourness Calculation
β
Domain Verification
β
Surgical Token Correction
β
Final Sweet Output + Diagnostics
- Guard: Main orchestrator managing the entire pipeline
- SournessCalculator: Implements the Modified Cross Attention formula
- KnowledgeGraph: Neurosymbolic graph for entity grounding and path verification
- KnowledgeStore: Vector store for document similarity matching
- SurgicalFixer: Token-level correction with LLM-based infilling
- DomainVerifier: Specialized validators for domain-specific constraints
- PromptRefiner: Auto-generates optimized prompts from diagnostics
from sourcandy import plotSournessHeatmap
response = guard.invoke('Your query here')
# Generate heatmap
plotSournessHeatmap(
response.diagnosticReport.sournessMap,
title='Token Sourness Analysis',
savePath='heatmap.png'
)from sourcandy import BaseEmbedder
import numpy as np
class CustomEmbedder(BaseEmbedder):
def embedText(self, text: str) -> np.ndarray:
# Your embedding logic
return np.array([...])
def embedBatch(self, texts: list) -> np.ndarray:
return np.vstack([self.embedText(t) for t in texts])from sourcandy import DomainVerifier
# Create domain verifier
medicalVerifier = DomainVerifier(
domain='medical',
knowledgeGraph=knowledgeGraph,
knowledgeStore=knowledgeStore,
embedder=embedder
)
# Add domain keywords
medicalVerifier.addDomainKeywords([
'diagnosis', 'treatment', 'symptoms', 'medication'
])
# Add constraints
medicalVerifier.addConstraint(
'forbidden',
r'guaranteed cure|100% effective'
)
# Register with guard
guard.addDomainVerifier('medical', medicalVerifier)# Process multiple queries
for prompt in prompts:
response = guard.invoke(prompt)
# Get aggregate metrics
metrics = guard.getMetrics()
print(f"Sweet Rate: {metrics['sweetRate']:.1%}")
print(f"Average Sourness: {metrics['averageSourness']:.3f}")# Initial query
response = guard.invoke('What are the latest Python features?')
# Generate optimized prompt
refinedPrompt = guard.refinePrompt(
prompt='What are the latest Python features?',
diagnosticReport=response.diagnosticReport
)
# Use refined prompt for better results
betterResponse = guard.invoke(refinedPrompt)- camelCase: All identifiers use camelCase convention
- Single Quotes: All strings use single quotes
- No Comments: Self-documenting code with comprehensive docstrings
- Type Safety: Pydantic models for all data structures
- Production Grade: Error handling, validation, efficient algorithms
Main orchestrator class.
Methods:
invoke(prompt, enableCorrection=True, enableVerification=True): Process single querybatchInvoke(prompts, ...): Process multiple queriesaddKnowledgeDocuments(documents, metadata): Add factual documentsaddDomainVerifier(domain, verifier): Register domain validatorgetMetrics(): Get performance statisticsrefinePrompt(prompt, diagnosticReport): Generate optimized prompt
Token-level sourness scoring.
Methods:
calcG(tokenEmbedding, kgMatrix): Calculate knowledge grounding scorecalcP(tokenEmbedding, promptEmbedding): Calculate prompt correspondencecalcR(currentGrounding, previousGrounding): Calculate relational connectivitygenerateSournessMap(outputText, promptText): Generate full diagnostic map
Neurosymbolic entity graph.
Methods:
addEntity(entityId, embedding, label): Add entity with vectoraddRelation(subject, predicate, object): Add triplet relationgetRelatedSubGraph(entityIds, maxDepth): Extract subgraphhasPath(fromEntity, toEntity, maxHops): Check connectivityfindClosestEntity(queryEmbedding, topK): Similarity search
See examples.py for comprehensive demonstrations including:
- Basic usage with hallucination detection
- Sourness visualization
- Metrics tracking across multiple queries
- Automatic prompt refinement
Run examples:
python examples.pyWe welcome contributions! Please ensure:
- Follow camelCase naming conventions
- Use single quotes for strings
- Provide comprehensive docstrings (no inline comments)
- Add type hints to all functions
- Include tests for new features
MIT License - see LICENSE file
Built with:
- Pydantic: Data validation and settings management
- NumPy: Efficient numerical operations
- Matplotlib/Seaborn: Visualization (optional)
- Authors: Soumya Sourav Das, Devyanshi Bansal
- Issues: GitHub Issues
Make your LLM outputs sweet, not sour! π¬