Add semantic deduplication for ranked crawl chunks - #51
Merged
Conversation
Implements issue TinySuiteHQ#12. Adds an optional embedding-based semantic deduplication stage to research chunk selection, layered after the existing lexical Jaccard filter, so syndicated or paraphrased chunks with low literal token overlap are removed. It reuses the dense embeddings already computed during ranking, so no extra embedding calls are made. - chunk_pool_selection_service: cosine-similarity dedup against already-accepted chunks in ranked order, keeping the highest-ranked member of a duplicate group; cooperates with per-source quotas and the fill pass; records single per-chunk rejection diagnostics. - hybrid_embed_search_service: attach the dense document embedding to each ranked chunk so downstream selection can reuse it. - config: add chunk_semantic_dedupe_enabled (default true) and chunk_semantic_dedupe_threshold (default 0.92), with coercion and annotated JSON config comments. - research pipeline: wire the new settings through selection and drop the reused embeddings once selection is done. - tests and README for the new behavior.
Collaborator
|
Thank you so much for the implementation, sorry for the delay, I will merge this in today 😄 ! |
MarcellM01
approved these changes
Aug 22, 2026
MarcellM01
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed against issue #12 and current main. The implementation reuses ranked chunk embeddings, preserves ranked-order and quota/fill behavior, adds focused coverage and documentation, merges cleanly, and passes the full local suite (293 tests, 1 skipped). Thank you, Ben!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an optional embedding-based semantic deduplication stage to research
chunk selection, layered after the existing lexical Jaccard filter. It removes
syndicated, paraphrased, or reworded chunks that share the same meaning but have
low literal token overlap, which the Jaccard filter cannot catch.
How
chunks in ranked order, so the highest-ranked member of a duplicate group is
kept. Cooperates with per-source quotas and the fill pass, and records one
rejection diagnostic per dropped chunk (chunk_id, reason, similarity,
matched_chunk_id).
computed during ranking to each ranked chunk, so semantic dedup reuses it with
no extra embedding calls. The embedding is dropped again after selection so it
never bloats trace files or public payloads.
chunk_semantic_dedupe_threshold (default 0.92, conservative) with coercion and
annotated config comments. SearchResult and existing shapes are unchanged.
Design notes
Lexical Jaccard stays as a cheap first pass; semantic dedup ru
already-bounded oversampled pool, comparing each candidate to the running
selected output. A conservative default threshold avoids mergi
that happen to be phrased similarly, and the stage can be disabled entirely.
Acceptance criteria
variation, disabled state, embedding reuse: done
Testing
python -m unittest discover tests -> 293 passed (1 skipped).
Also drove the real research pipeline end-to-end with the ONNX
a paraphrase pair measured cosine 0.57 with Jaccard token overlap 0.18; with
semantic dedup enabled the lower-ranked paraphrase was dropped
both were kept.