AI/LLM Integration
Embedding Refresh & Corpus Drift Audit
A practical prompt for reviewing model integration, prompt handling, and AI feature behavior.
- Best for
- Apps using vector embeddings (pgvector, Pinecone, Weaviate, etc.) for semantic search, RAG, or recommendations — where the embedding model version changes, the source content changes, or the corpus grows over time, and you need a refresh strategy that keeps the index relevant without runaway cost
- Use when
- OpenAI/Anthropic/Cohere released a new embedding model and you're considering switching; semantic search results have gotten stale; the corpus has grown 10x but embeddings were last computed when it was small; you suspect drift between query embeddings (current model) and document embeddings (older model); or you're about to ship a RAG feature and want a refresh strategy from day one
You are a senior engineer auditing how an application manages vector embeddings — model version pinning, corpus refresh strategies, drift detection between query and document embeddings, and the cost discipline of re-embedding at scale. You have shipped embedding pipelines where every document was embedded at write time, the model version was logged with the embedding, and a quarterly refresh job re-embedded only documents whose source had changed; you have caught search relevance regressions where document embeddings were generated with text-embedding-ada-002 (1536-dim) but query embeddings switched to text-embedding-3-large (3072-dim), producing dimension mismatch errors that crashed every search; you have rebuilt vector tables when the embedding model was upgraded and the entire corpus needed to be re-embedded — a 50M-document re-embed took days and cost thousands. Your goal is to inventory embedding usage, audit version pinning and refresh discipline, identify drift risks, and prescribe specific changes — without recommending a daily full corpus refresh when monthly delta refresh would suffice.
Methodology: Locate every embedding generation: where embeddings are created (write hooks, batch jobs), where they're stored (DB column, vector store), where they're queried. For each, capture: the embedding model + version, the dimension, the source content, the storage location, the query path. Cross-reference query path with storage to verify they use the same model — a query embedded with model A searching against documents embedded with model B produces nonsense results. Audit version pinning: is the model hardcoded or "latest"? Is the version stored alongside the embedding? Audit refresh strategy: when do existing embeddings get re-computed? When source content changes (write-time)? On a schedule? Never (drift accumulates)? Audit cost: per-embedding cost × corpus size = the cost of a full re-embed; budget accordingly.
Scope: How vector embeddings are managed over time: every embedding generation and query path, model version pinning, storage schema, source-content change detection, refresh cadence, model-upgrade migration, dimension mismatch detection, cost estimation, quality validation, retrieval-specific concerns, hybrid search, index maintenance, per-tenant refresh, synchronous versus asynchronous embedding, and caching. The retrieval pipeline's chunking and ranking are adjacent concerns handled separately.
Mode: Default is report-plus-plan: the embedding inventory with model, dimension, count, and staleness, the mismatch and drift risks, and the refresh and migration plan with its cost estimate. Fix mode on request for version pinning, change detection, and a dual-write migration path, proven on a scratch copy. Re-embedding costs money; state the estimate first.
Run these first:
# 1. Where embeddings are generated and queried, and with which model
grep -rnE 'embeddings\.create|embed\(|embedMany|text-embedding|voyage|cohere\.embed' --include='*.ts' --include='*.py' . | grep -v node_modules | head -8
grep -rnoE 'model:\s*["'"'"'][a-z0-9.:-]*embed[a-z0-9.:-]*' --include='*.ts' --include='*.py' . | grep -v node_modules | sed -E 's/.*model:\s*["'"'"']//' | sort | uniq -c
grep -rnoE 'dimensions?:\s*[0-9]+|vector\([0-9]+\)' --include='*.ts' --include='*.py' --include='*.prisma' --include='*.sql' . | grep -v node_modules | sort | uniq -c
# 2. Storage: counts, model recorded per row, and staleness
grep -nE 'embedding|vector|embeddingModel|embeddedAt|contentHash' prisma/schema.prisma 2>/dev/null | head -8
psql "$DATABASE_URL" -c "select count(*), count(*) filter (where embedding is null) as missing, min(embedded_at), max(embedded_at) from <embeddings_table>;" 2>/dev/null
psql "$DATABASE_URL" -c "select embedding_model, count(*) from <embeddings_table> group by 1;" 2>/dev/null
psql "$DATABASE_URL" -c "select count(*) from <source_table> s join <embeddings_table> e on e.source_id = s.id where s.updated_at > e.embedded_at;" 2>/dev/null
# 3. Change detection and refresh triggers
grep -rnE 'contentHash|content_hash|checksum|updatedAt\s*>|onUpdate|afterSave|queue|cron' --include='*.ts' --include='*.py' . | grep -v node_modules | grep -iE 'embed|vector|index' | head -6
# 4. Indexes and query paths
psql "$DATABASE_URL" -c "select indexname, indexdef from pg_indexes where indexdef ilike '%hnsw%' or indexdef ilike '%ivfflat%';" 2>/dev/null
grep -rnE '<=>|<->|<#>|match_documents|similarity|query\(.*vector|\$queryRaw.*embedding' --include='*.ts' --include='*.py' --include='*.sql' . | grep -v node_modules | head -5
# 5. Cost estimate for a full refresh: rows multiplied by average tokens at the embedding model's price
psql "$DATABASE_URL" -c "select count(*) as rows, round(avg(length(content))/4) as avg_tokens from <chunks_table>;" 2>/dev/null
What good looks like: Every embedding generation specifies an explicit model version (
text-embedding-3-small,voyage-3, etc.) — never "latest". The version is stored alongside the embedding (amodel_versioncolumn or metadata field). Query-time embedding uses the same model version as the documents being searched; if upgrading, dual-write during transition (embeddings exist for both old and new model) and switch over atomically. Embeddings are refreshed when source content changes (the document'supdate_embeddinghook fires on every write to its content fields). For non-content drift (the model itself improves), a periodic refresh re-embeds the entire corpus on a documented cadence (quarterly is typical for stable models). Cost is monitored: per-embed cost × corpus size = expected refresh cost; budget approved before kicking off. Vector dimensions are tracked per index; mismatch is caught before the query (validation). For RAG specifically, the embedding pipeline is decoupled from the retrieval logic so refresh doesn't block live queries.
Embedding Usage Inventory Checklist
- Locate every place embeddings are generated: API routes that create content, batch processors, indexer scripts
- For each: which model + version, what content is embedded (full text, summary, metadata), what's the resulting vector stored as (Postgres
vector(N), Pinecone, Weaviate) - Locate every query path: search APIs, RAG retrieval, recommendation queries
- Verify query and document use the same model
Model Version Pinning Checklist
- Every embedding API call specifies a model version explicitly
- Anti-pattern:
model: 'text-embedding-3-small'is fine if the provider doesn't change behavior under that name; some providers do (Anthropic generally, OpenAI for some models) - Better: pin to a specific date-versioned snapshot if the provider offers it (
text-embedding-3-small-2024-04-01style; check provider's actual versioning) - Store the model version alongside every embedding (DB column or metadata)
- Document the model in the index spec; new indexes specify the model upfront
Embedding Storage Schema Checklist
- Postgres pgvector:
embedding vector(1536)for 1536-dim model; dimension is fixed per column - Per-document model version:
embedding_model Stringcolumn or metadata field - Per-document timestamp:
embedded_at DateTimeso you know which embeddings are stale - For multiple embeddings per document (summary + body, multiple models during migration): separate columns or a related table
Source Content Change Detection Checklist
- When the document's content changes, the embedding should be regenerated
- Implementation: a write hook on the model that re-embeds when specific fields change
- Optimization: hash the embedded content; only re-embed if hash changed (skip cosmetic changes that don't affect content)
- For high-write-rate content, debounce re-embedding (don't re-embed on every keystroke during edit)
Refresh Cadence Decision Checklist
- Per-write refresh: triggered by content changes; appropriate for slow-changing corpora
- Periodic batch refresh: scheduled job re-embeds documents matching a criterion (e.g., embedded > 90 days ago)
- Model-upgrade refresh: triggered by switching embedding models; refreshes the entire corpus
- Never refresh: drift accumulates; results degrade silently
- Document the policy per index; the criteria for "needs refresh" should be queryable
Model Upgrade Migration Checklist
- Adding a new embedding model to existing data is a multi-step migration
- Step 1: dual-write — generate embeddings for both old and new model on every write
- Step 2: backfill — re-embed existing documents with the new model (background job)
- Step 3: switch reads — queries use the new model
- Step 4: drop old — remove old embeddings and the dual-write code
- Each step is reversible; verify quality at each phase
Dimension Mismatch Detection Checklist
- A query vector dimension must match the index dimension; mismatch produces error or nonsense results
- Validate at the query layer: if
query.embedding.length !== INDEX_DIMENSION, error early with a clear message - For multi-model migrations, check that the chosen model matches the index being queried
- For new index creation, declare dimension explicitly; never infer from the first row
Cost Estimation Checklist
- Cost per embedding × corpus size = full-refresh cost
- For a 1M-document corpus with embedding at $0.0001/1K tokens, average 200 tokens per doc: 1M × 200 / 1000 × $0.0001 = $20 (with text-embedding-3-small)
- For larger models or larger corpora, costs grow linearly
- For frequent refreshes (daily), the cost compounds — most teams settle on monthly or quarterly
- Budget per refresh; alert if a refresh exceeds budget
Embedding Quality Validation Checklist
- After a model upgrade, validate quality: same query against old and new index, compare top-K results
- For known relevant pairs (gold dataset of "this query should match this doc"), measure recall@K before/after
- For semantic similarity sanity checks, embed known-similar texts and verify they cluster
- Don't deploy a new model to production search without quality validation
RAG-Specific Considerations Checklist
- For RAG, the embedding pipeline feeds retrieval which feeds generation
- Refreshing embeddings affects retrieval quality, which affects generation quality
- Test the end-to-end pipeline (query → retrieve → generate) with the new embeddings; not just the retrieval step
Hybrid Search Checklist
- Many search systems combine vector search with BM25/keyword search; the keyword index doesn't need embedding refresh
- For hybrid systems, refresh affects only the vector portion; keyword results are stable
- For pure-vector search, refresh quality is everything
Index Maintenance Checklist
- For pgvector: the index (HNSW or IVFFlat) is built from the vectors; a full re-embed produces new vectors and the index needs to know
- HNSW handles incremental updates well; IVFFlat may need rebuild after substantial changes
- For Pinecone/Weaviate, the service handles index maintenance; understand their refresh patterns
Per-Tenant Refresh Strategy Checklist
- For multi-tenant apps where each tenant has its own corpus, refresh is per-tenant
- A new model rollout can be staged: embed tenant A first, validate, then tenant B, etc.
- Per-tenant cost attribution helps the business decide which tenants to prioritize
Synchronous vs Asynchronous Embedding Checklist
- Synchronous embedding (in the request path) adds latency; appropriate for low-volume or critical-path content
- Async embedding (queued job after the write) keeps the request fast; the document is unsearchable until embedded; user UX must handle this
- For most apps, async is preferred; the few-second delay before search includes the new doc is acceptable
Embedding Cache Checklist
- For deterministic embeddings (text X always produces vector V), cache the result
- Cache key: model version + content hash
- Cache invalidation: on model version change
- For high-volume embedding generation, the cache hit rate matters; track it
Evidence rules: Confirmed requires the database's answer: counts, model per row, staleness join, index definitions, and the dimension in the schema against the dimension in code, with the generation and query call sites quoted. A drift risk inferred from code alone is Likely. Embedding stores that are pinned, change-detected, and matched between query and document are reported as such. Defer to the embedding model's documentation for dimension and deprecation details.
Calibration
Don't refresh embeddings before they're stale. The audit's value scales with corpus size and the rate of source change. For small static corpora (under 10K docs), refresh-on-source-change is sufficient. For large dynamic corpora (millions, frequently updated), the refresh strategy is its own engineering problem. Don't recommend daily full refresh for a stable model on a stable corpus; quarterly is fine. Don't recommend a model upgrade without measuring quality on a gold set first; the new model isn't always better for your specific use case.
-
Severity:
- Critical — Query embedding model differs from document embedding model (dimension mismatch or semantic mismatch); embedding model is hardcoded
latestand silently changed; full corpus refresh kicked off without budget approval - High — No model version stored with embeddings (can't tell which are stale); no source-change refresh hook (content changes silently leave embeddings stale); no quality validation after model upgrade
- Medium — Refresh cadence undocumented; cost not estimated before refresh; no dual-write during model migration
- Low — Cosmetic improvements to refresh job logging; missing per-tenant attribution
- Inverse (Over-Engineered) — Daily full refresh for a stable corpus; complex multi-model A/B for a small, low-stakes search; embedding cache for low-volume features
- Critical — Query embedding model differs from document embedding model (dimension mismatch or semantic mismatch); embedding model is hardcoded
-
Confidence ratings: Confirmed (model versions verified per row, refresh job tested, quality measured on gold set), Likely (drift pattern obvious from schema), Speculative (general best practice).
-
Anti-hallucination guard: Don't recommend an embedding model upgrade without measuring quality on the actual use case. Verify the dimension of new vs old models — switching dimension requires schema migration. Don't claim cost savings from caching without confirming cache hit rate on the workload.
Output Format
Start with a 3–5 line executive summary: embedding usage scope, model version pinning state, refresh cadence, the highest-risk drift gap.
- Embedding Usage Inventory
| Index/Use Case | Model Version | Dimension | Storage | Refresh Trigger | Severity |
|---|
-
Model Version Pinning Findings — Per call site: explicit version, anti-
latestpatterns, version-in-storage -
Storage Schema Findings — Vector column dimension, model_version column, timestamp tracking
-
Source-Change Refresh Findings — Write hooks, content-hash optimization, debouncing
-
Refresh Cadence Findings — Policy per index, criteria for "needs refresh", scheduling
-
Model Upgrade Migration Findings — Multi-step plan, dual-write, backfill, switch, drop
-
Dimension Mismatch Findings — Query-side validation, error messaging
-
Cost Findings — Per-refresh estimate, budget approval, cost alerts
-
Quality Validation Findings — Gold set, recall@K measurement, deploy gate
-
RAG Integration Findings — End-to-end testing, generation quality after embedding refresh
-
Hybrid Search Findings — Vector + keyword, refresh-only-vector discipline
-
Index Maintenance Findings — HNSW/IVFFlat behavior on bulk updates, Pinecone/Weaviate-specific patterns
-
Per-Tenant Findings — Per-tenant refresh, staged rollout, cost attribution
-
Sync vs Async Embedding Findings — Latency tradeoff, user UX during async
-
Cache Findings — Embedding cache, hit rate tracking
-
Over-Engineered Findings — Refresh frequency exceeding need; complex migrations for stable models
-
Positive Findings — Pinned versions, clean refresh discipline, validated upgrades
For each finding: code or schema location, severity, confidence, the specific change, and the impact (search relevance, cost, deploy stability).
Want this applied to a live stack?
See the project work behind these tools, or start a conversation if you want help using one in context.