Skip to content

Keep each phrase signature's vector so an alignment run embeds only texts it has not seen - #1186

Merged
WaylandYang merged 2 commits into
deeplethe:devfrom
evolv3ai:fix/signature-vectors-are-embedded-once
Oct 10, 2026
Merged

WaylandYang merged 2 commits into
deeplethe:devfrom
evolv3ai:fix/signature-vectors-are-embedded-once

Conversation

@evolv3ai

Copy link
Copy Markdown
Contributor

Why

Cut 2 of #1097, on top of cut 1 (#1107). Once properties have vectors, shortlist re-embeds every wide signature on every run, although a signature's text (phrase · first example · its quote) hardly changes between runs and a run may have only a few dozen signatures to decide. The embedding pass becomes a fixed cost per run that grows with the base.

What changes

  • Migration 0110_a_signature_is_embedded_once.sql: table signature_vectors (kb_id, model, text_hash, embedding), primary key (kb_id, model, text_hash), ON DELETE CASCADE from knowledge_bases. embedding is a dimensionless vector, as chunks.embedding is. It's read only by key, so there's no HNSW and no vector_index registration. It's a cache, not ledger data, so it stays outside the 0070 surface and the export.
  • CURRENT_SCHEMA_VERSION 98 → 99 (99 files in migrations/). The migration was drafted as 0107; dev has since moved to 0109 and version 98, and Explore table alignments and adopt their ontology together #1179 and Govern conflicts in the typed graph and preserve human decisions #1131 both claim 0108, so it takes the next number after 0109 rather than the gap. That renumbering is the only change from the draft besides keeping dev's new mod line next to this one in tests/store/main.rs.
  • utopia_store::signature_vectors: get, put (one transaction, ON CONFLICT DO NOTHING), and prune, which deletes this base's rows whose hash no longer occurs and rows embedded with another model.
  • shortlist: hashes (SHA-256) the text of every wide signature, prunes with that full set (not only what this run embeds, so a failed batch doesn't evict good rows), loads the cached vectors, and embeds only the misses, deduplicated, in the same batches of 32. The log line reports reused and embedded.
  • Kind words are not cached. shortlist_kind_words already embeds only fresh kind words, and sharing the table would need a source column to keep the two prunes apart. The model key is embed_model. If two endpoints serving the same model name is a concern, embed_base_url could join the key.

How it was checked

  • a_second_run_over_an_unchanged_base_makes_no_embedding_call (phrase_alignment_tests.rs): with property vectors present, run 1 embeds once, and run 2 over the unchanged base makes no embedding call and asks the model nothing. After switching embed_model, run 3 embeds again and only the new model's row is left. Against cut 1's shortlist it fails with left: 2, right: 1.
  • a_signature_vector_lasts_while_its_text_does (store): get keyed by model, collisions don't overwrite, prune drops gone hashes and other models' rows and leaves other bases alone.
  • cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings: clean.
  • With UTOPIA_DATABASE_URL and UTOPIA_TEST_REQUIRE_DB=1 against a scratch pgvector:pg16 migrated through 0110: the whole utopia-store suite (including the 0070 drift guard and the export guards), utopia-cli (including schema_version_policy_compares_against_current), and utopia-server -- phrase_alignment all pass. I didn't run the whole workspace locally; CI covers it.

This change was drafted with Claude Code (Anthropic) and reviewed by me before submitting.

Before review

  • Every commit is signed off (git commit -s)
  • cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings and cargo test --workspace pass
  • SQL under crates/utopia-store/ was tested with UTOPIA_DATABASE_URL set (those tests skip without it)
  • A new migration takes the next free number on dev, and CURRENT_SCHEMA_VERSION in crates/utopia-cli/src/main.rs equals the number of files in migrations/

…exts it has not seen

Signed-off-by: evolv3ai <hello@evolv3.ai>

@WaylandYang WaylandYang 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.

Thank you for cut 2. It is the shape we discussed: vectors kept by base, model and text hash, a run embeds only the texts it has not seen, and the prune takes the full set of hashes so a failed batch does not evict good rows. Taking 0110 instead of the gap is the right call now that dev has moved. Checked here on a fresh database: the store test and a_second_run_over_an_unchanged_base_makes_no_embedding_call pass, and the branch merges cleanly. Merging now. Thanks again for seeing #1097 through.

@WaylandYang
WaylandYang merged commit b82f5e2 into deeplethe:dev Oct 10, 2026
6 checks passed
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.

2 participants