Skip to content

Collect the same fuzzy-index terms on both ingestion paths (#346) - #360

Open
Bernhard Merkle (bmerkle) wants to merge 1 commit into
microsoft:mainfrom
bmerkle:fix-fuzzy-term-collection
Open

Bernhard Merkle (bmerkle) wants to merge 1 commit into
microsoft:mainfrom
bmerkle:fix-fuzzy-term-collection

Conversation

@bmerkle

Copy link
Copy Markdown
Collaborator

Part of #346 (item 3). Items 1 and 2 are in #358; the two PRs are independent and can merge in either order. Together they fix #346.

The fuzzy related-terms index got different terms depending on how messages were ingested, so related-term expansion depended on the ingestion path:

add_messages_with_indexing (before) add_messages_streaming (before)
Entity types, facets, action subjects/objects/params not embedded embedded
Multi-word verbs each verb separately ("looks", "up") joined ("looks up"), like the term index
Types/facets of entities without a name — embedded, though the term index skips them
Terms from message metadata (speakers, recipients, …) embedded never embedded

Change

  • New semantic_ref_index.collect_knowledge_terms and collect_related_terms_for_fuzzy_index return exactly the normalized, deduplicated terms a knowledge item adds to the semantic ref index.
  • ConversationBase._update_related_terms_incremental and add_messages._collect_related_terms_for_fuzzy_index both use them.
  • The streaming commit indexes metadata knowledge only at commit time, so those terms have no precomputed embedding. It now calls _update_related_terms_incremental(..., exclude=<precomputed terms>) after adding the precomputed ones, which embeds only the remaining (metadata) terms.

After this, on both paths the fuzzy index holds the same terms as the semantic ref index, which is also what build_related_terms_index produces when rebuilding from scratch.

Cost: add_messages_with_indexing embeds more terms per message, and add_messages_streaming embeds metadata terms at commit time. Repeated terms are served from the embedding cache (memory) or skipped as already stored (SQLite).

Tests (each fails on main):

  • test_fuzzy_terms_match_semantic_ref_index_terms: both collectors return exactly the term-index terms for knowledge with multi-word verbs, a nameless entity, an inverse action, params and facets.
  • test_add_messages_with_indexing_fuzzy_terms_match_term_index (with and without knowledge extraction) and test_streaming_fuzzy_terms_match_term_index: after ingesting on SQLite, fuzzy-index terms equal semantic-ref-index terms.

Not addressed (pre-existing): the in-memory TermEmbeddingIndex.add_terms_with_embeddings doesn't skip terms it already holds, so repeated batches can store duplicate vectors; this change makes that more frequent for common terms such as person.

Testing: make locally: format, ruff and pyright (3.12 and 3.15) clean, build OK; 912 passed. 7 tests fail locally because of a broken jiter native module on my machine (plus an MCP sampling test that depends on it); they fail identically on main, so CI is the reference for those.

🤖 Generated with Claude Code

…#346)

add_messages_with_indexing (ConversationBase._update_related_terms_incremental)
only embedded entity names, topic text and individual verbs, while
add_messages_streaming used collect_entity_terms / collect_action_terms and
also embedded types, facets, subjects, objects and params. So related-term
expansion depended on the ingestion path. The incremental path also embedded
each verb on its own, while the term index key is the joined verbs, and the
streaming path embedded types and facets of nameless entities, which the term
index skips. The streaming path also never embedded terms from message
metadata (e.g. speakers and recipients), which are indexed at commit time.

Add semantic_ref_index.collect_knowledge_terms and
collect_related_terms_for_fuzzy_index, which return exactly the normalized,
deduplicated terms a knowledge item adds to the semantic ref index, and use
them on both paths. The streaming commit now also embeds the terms of the
new semantic refs that have no precomputed embedding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 20:14
@bmerkle

Bernhard Merkle (bmerkle) commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author

Kevin Turcios (@KRRT7) could you take a look? This is the other half of #346: both ingestion paths now add the same terms to the fuzzy related-terms index, including metadata terms on the streaming path. Independent of #358.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Commit-time embedding failures can leave retries with missing persisted terms, and nameless metadata entities still produce inconsistent index contents.

2 open issues
What changed in this PR

Addresses #346’s ingestion-path inconsistency by sharing fuzzy-index term collection between incremental and streaming ingestion.

Changes:

  • Centralizes normalized, deduplicated knowledge-term collection.
  • Adds streaming metadata terms at commit time.
  • Adds term-parity regression tests and documents the behavior change.
File Description
tests/​test_add_messages_with_indexing.py Tests incremental ingestion term parity.
tests/​test_add_messages_streaming.py Tests streaming term parity, including metadata.
tests/​test_add_messages_pipeline.py Tests shared collectors against semantic-index terms.
src/​typeagent/​storage/​memory/​semantic_ref_index.py Adds shared knowledge-term collectors.
src/​typeagent/​knowpro/​conversation_base.py Uses shared collection and embeds remaining metadata terms.
src/​typeagent/​knowpro/​add_messages.py Delegates pipeline collection to the shared helper.
CHANGELOG.md Documents ingestion behavior and embedding changes.

Review effort: Balanced

Comment on lines +442 to +444
await self._update_related_terms_incremental(
start_points.semref_count, exclude=set(related_terms)
)
Comment on lines +590 to +591
if isinstance(knowledge, ConcreteEntity):
return collect_entity_terms(knowledge) if validate_entity(knowledge) else []

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Apply shared term normalization to search terms, query matchers and the alias index

2 participants