Knowledge > Runbooks > Technical Ops > Regenerate Vector Embeddings
Regenerate Vector Embeddings
Regenerate OpenAI vector embeddings for content rows in unified_rag_content when embeddings are missing, stale, or the embedding model has changed.
Prerequisites
- Access to embedding generation scripts in
sermon-illustrations/scripts/or ChurchWiseAI scripts - OpenAI API key available (this is a legitimate API call — embeddings are not batch content generation)
- Scope of regeneration identified: specific rows, a content type, or full table
Cost Estimate Before Starting
text-embedding-3-small costs $0.02 per 1M tokens.
| Scope | Approx Tokens | Approx Cost |
|---|---|---|
| 100 rows (~500 tokens avg) | 50K | <$0.01 |
| 10K rows | 5M | ~$0.10 |
| Full table (327K rows) | 163M | ~$3.26 |
Confirm scope with founder before running full-table regeneration.
Steps
-
Identify what needs regeneration
Find rows with null embeddings:
SELECT count(*) FROM unified_rag_content WHERE embedding IS NULL;Find rows where content was updated after embedding was generated (if tracking
updated_at):SELECT count(*) FROM unified_rag_contentWHERE updated_at > embedding_generated_atAND embedding IS NOT NULL;Find rows by content type (e.g., only illustrations):
SELECT count(*) FROM unified_rag_contentWHERE content_type = 'illustration' AND embedding IS NULL; -
Locate the embedding script
Check these locations for existing scripts:
C:\dev\sermon-illustrations\scripts\C:\dev\churchwiseai-web\scripts\If no script exists for the needed scope, write one following the pattern below.
-
Run embedding regeneration in batches
Always process in batches of 100–500 rows to avoid rate limits and allow progress tracking. Do NOT use
claude -pfor embeddings — the OpenAI Embeddings API is a legitimate real-time infrastructure call (not batch content generation).Example batch approach:
# From the script directorynode generate-embeddings.js --content-type illustration --batch-size 100 --dry-run# Review output, then run without --dry-runnode generate-embeddings.js --content-type illustration --batch-size 100 -
Verify embeddings are stored correctly
After regeneration, confirm the
embeddingcolumn is populated with a vector of the expected dimension (1536 fortext-embedding-3-small):SELECT id, length(embedding::text) as embedding_lengthFROM unified_rag_contentWHERE embedding IS NOT NULLLIMIT 5; -
Test similarity search returns correct results
Run a test similarity query to confirm the regenerated embeddings produce sensible results:
-- Find content similar to a test embedding (run from your app's RAG endpoint)-- Or use the chatbot/voice agent in demo mode and verify it retrieves relevant contentTest via the demo church (
00000000-0000-4000-a000-000000000001) — ask the chatbot a question that should retrieve the regenerated content. -
Update the vector index if needed
If the embedding model changed (different dimensions), drop and recreate the vector index:
-- Check existing indexSELECT indexname, indexdef FROM pg_indexesWHERE tablename = 'unified_rag_content' AND indexname LIKE '%embed%';-- Recreate if model changed (requires founder approval — affects 327K rows)-- DROP INDEX CONCURRENTLY idx_unified_rag_content_embedding;-- CREATE INDEX CONCURRENTLY idx_unified_rag_content_embedding-- ON unified_rag_content USING ivfflat (embedding vector_cosine_ops)-- WITH (lists = 100);
Verification
SELECT count(*) FROM unified_rag_content WHERE embedding IS NULLreturns 0 (or the expected count for excluded rows)- Chatbot RAG responses for the demo church return relevant content
- No new errors in
ops_error_reportsrelated to embedding or similarity search
See Also
- Supabase Migration Runbook
- Database Performance Runbook
- Chatbot RAG logic:
C:\dev\churchwiseai-web\src\app\api\chatbot\chat\route.ts - Voice agent RAG:
C:\dev\churchwiseai-web\voice-agent-livekit\core\