How LangExtract Handles Fuzzy Text Alignment: A Deep Dive into the Algorithm
LangExtract performs fuzzy text alignment using a two-stage pipeline that first attempts exact token matching with difflib.SequenceMatcher, then falls back to a sliding-window fuzzy search with normalized tokens and configurable similarity thresholds when exact matches fail.
LangExtract is an open-source Python library developed by Google for extracting structured information from unstructured text. When working with noisy or paraphrased extractions, fuzzy text alignment becomes essential for mapping extracted values back to their original positions in the source document. The library implements a sophisticated two-stage alignment system that balances precision and recall through configurable fuzzy matching algorithms.
The Two-Stage Alignment Architecture
LangExtract aligns extracted values to the original source text using a resolver that prioritizes accuracy before attempting approximate matching.
Stage 1: Exact Token-Level Matching
The alignment process begins in langextract/resolver.py within the WordAligner.align_extractions method (lines 842-855). By default, the system uses difflib.SequenceMatcher to compare token sequences between the source text and concatenated extraction texts. This stage identifies perfect matches or subsequence matches without allowing for typographical variations or paraphrasing.
Stage 2: Fuzzy Alignment Fallback
When exact matching fails to locate an extraction, the resolver checks the enable_fuzzy_alignment flag defined in ALIGNMENT_PARAM_KEYS (lines 45-49 in resolver.py). If enabled, the system invokes WordAligner._fuzzy_align_extraction (lines 537-562) to perform a fuzzy search across sliding windows of the source text. This fallback mechanism handles cases where the extraction contains minor variations, different word order, or normalized forms of the original text.
Inside the Fuzzy Alignment Algorithm
The core fuzzy logic lives in WordAligner._fuzzy_align_extraction and employs several optimization strategies to balance accuracy with performance.
Tokenization and Normalization
Before comparison, the fuzzy aligner normalizes text to improve match rates. The _tokenize_with_lowercase method converts extraction text to lowercase and splits it into tokens, while _normalize_token (lines 69-76) handles morphological variations like plural forms. This preprocessing ensures that "heart problems" and "heart problem" can align correctly despite grammatical differences.
Pre-Filtering with Token Count Intersection
To optimize performance, the algorithm implements an early exit strategy using token count intersections. The system builds a Counter of normalized extraction tokens (extraction_counts) and compares it against token counts for each candidate window in the source text. If the intersection size is smaller than min_overlap = int(len_e * fuzzy_alignment_threshold), the expensive SequenceMatcher computation is skipped (lines 92-110). This pre-filter dramatically reduces the search space for long documents.
Sliding-Window Search with SequenceMatcher
For windows passing the pre-filter, the algorithm employs a sliding-window approach with difflib.SequenceMatcher configured with autojunk=False to prevent aggressive filtering of short matches. The matcher computes a similarity ratio based on matching tokens divided by the extraction length (matches / len_e). The system examines every possible window with lengths ranging from the extraction size up to the full source length, tracking the best ratio and its corresponding span (lines 115-122).
Threshold Validation and Status Assignment
The fuzzy match is accepted only if the best ratio meets or exceeds the fuzzy_alignment_threshold, which defaults to 0.75 as defined by _FUZZY_ALIGNMENT_MIN_THRESHOLD (line 40). Upon successful alignment, the algorithm sets the token and character intervals on the Extraction object and updates its alignment_status to MATCH_FUZZY (defined in langextract/core/data.py, lines 42-48). If no window satisfies the threshold, the method returns None and the extraction remains unaligned.
Configuring Fuzzy Text Alignment in LangExtract
The alignment behavior is controlled through parameters passed to WordAligner.align_extractions or encapsulated in an AlignmentPolicy when using the prompt-validation utilities.
Basic usage with direct parameters:
from langextract import resolver, data
source = "The patient suffered from severe heart problems complications."
extractions = [
data.Extraction("Disease", "heart problems"),
data.Extraction("Complication", "severe heart problems complications"),
]
aligner = resolver.WordAligner()
aligned = aligner.align_extractions(
extraction_groups=[extractions],
source_text=source,
enable_fuzzy_alignment=True, # Enable fuzzy fallback
fuzzy_alignment_threshold=0.75, # Default similarity threshold
)
for e in aligned[0]:
print(f"{e.extraction_text}: {e.alignment_status}")
Using AlignmentPolicy for prompt validation:
from langextract import resolver, prompt_validation, data
policy = prompt_validation.AlignmentPolicy(
enable_fuzzy_alignment=True,
fuzzy_alignment_threshold=0.80, # Stricter matching
accept_match_lesser=True,
)
report = prompt_validation.validate_prompt_alignment(
examples=[data.ExampleData(text=source, extractions=extractions)],
policy=policy,
)
for issue in report.issues:
print(issue.short_msg())
Summary
- LangExtract implements a two-stage alignment pipeline that prioritizes exact token matches before attempting fuzzy alignment.
- The fuzzy alignment algorithm uses token normalization, counter-based pre-filtering, and sliding-window
SequenceMatchersearches to locate paraphrased or noisy extractions. - Key configuration parameters include
enable_fuzzy_alignment(defaultTrue) andfuzzy_alignment_threshold(default 0.75), defined inresolver.py. - Successful fuzzy matches are marked with
AlignmentStatus.MATCH_FUZZYincore/data.py, distinguishing them from exact matches.
Frequently Asked Questions
What is the default fuzzy alignment threshold in LangExtract?
The default threshold is 0.75, defined by the constant _FUZZY_ALIGNMENT_MIN_THRESHOLD in langextract/resolver.py (line 40). This means at least 75% of the extraction tokens must match a window in the source text for the alignment to be accepted. You can override this value by passing a different float between 0 and 1 to the fuzzy_alignment_threshold parameter.
How does LangExtract normalize tokens during fuzzy alignment?
The fuzzy aligner normalizes tokens through two main functions in resolver.py: _tokenize_with_lowercase converts text to lowercase and splits it into tokens, while _normalize_token (lines 69-76) handles morphological variations like plural forms. This normalization ensures that extractions like "heart problem" can align to source text containing "heart problems" despite grammatical differences.
Can I disable fuzzy text alignment entirely?
Yes, fuzzy alignment can be disabled by setting enable_fuzzy_alignment=False when calling WordAligner.align_extractions or when constructing an AlignmentPolicy in prompt_validation.py. When disabled, the resolver only performs exact token-level matching using difflib.SequenceMatcher, and any extraction that cannot be matched exactly will remain unaligned with a status of MATCH_NONE.
What is the difference between MATCH_EXACT and MATCH_FUZZY?
MATCH_EXACT indicates that the extraction was aligned to the source text through perfect token-level matching or subsequence matching without requiring the fuzzy fallback algorithm. MATCH_FUZZY indicates that the extraction was aligned using the _fuzzy_align_extraction method in resolver.py, meaning it required normalized token comparison and sliding-window similarity search to locate a match meeting the threshold. Both statuses indicate successful alignment, but MATCH_FUZZY signals that the extraction text differed from the source in spelling, morphology, or word order.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →