Re-embedding a Knowledge Base

6 min read

Re-embedding a Knowledge Base (Switching the Embedding Model)

Audience: Unique Solution Engineering (running on customer-managed tenants) and platform administrators with chat.admin.all. This is an advanced maintenance operation that re-processes a tenant's entire vector store and can run for hours on large tenants. Coordinate with Unique before running on a production tenant.

Overview

Re-embedding regenerates the vector embeddings for all of a company's ingested content using a different embedding model, then atomically switches the company's live vector collection to the new model. Use it when you need to:

  • Upgrade to a higher-quality / higher-dimensional embedding model (e.g. text-embedding-ada-002text-embedding-3-large).

  • Change the embedding dimension of a tenant's vector store.

  • Re-baseline embeddings after a model or provider change.

What it does under the hood (operation type SWITCH_EMBEDDING_MODEL):

  1. preparing — creates a new, temporary vector collection sized for the new model's dimension.

  2. reembedding — streams every chunk, re-embeds it with the new model, and writes the vectors into the new collection (in batches).

  3. swap — once the new collection is complete, atomically points the company's live collection at it and updates the relational store.

  4. cleanup — removes the old collection.

The job is checkpointed (a worker restart resumes from the last position) and non-destructive by default: the old collection and live search keep working throughout the run, and the swap only happens at the very end.


Who can run it

  • Permission:chat.admin.all (Zitadel) and CONTENT:MANAGE.

  • Scope: the companyId is taken from the authenticated admin user — you run it as an admin of the company being re-embedded.


Before you start (prerequisites)

#

Prerequisite

Why it matters

1

Target model is registered & deployed for the tenant

The new embedding model must be available in the tenant's embedding configuration. Confirm with Unique which model keys are available.

2

Sufficient embedding quota (TPM/RPM)

Re-embedding pushes the entire corpus through the model. Low quotas make large tenants impractical (jobs spend their time rate-limited). See Sizing.

3

Per-request token limit respected

Embedding providers cap tokens per request (commonly 300,000 tokens/request). batch size × average chunk tokens must stay under that cap or requests fail with a 400. See Configuration.

4

Run a dry run first

Always preview with dryRun: true to validate the plan and get a chunk-count estimate before committing.

5

Plan for duration

Large tenants take hours. Plan an overnight/weekend window and monitor.

The operation is resilient to concurrent ingestion and to transient vector-DB / rate-limit errors (they are retried automatically). For the cleanest, fastest run you may still choose to pause heavy ingestion for the company during the switch.


Step 1 — Dry run (preview, makes no changes)

graphql
mutation {
  createMaintenanceJob(input: {
    operationType: "SWITCH_EMBEDDING_MODEL"
    newEmbeddingModel: "text-embedding-3-large"
    newEmbeddingDimension: 3072
    dryRun: true
  }) { id status }
}

A dry run validates the request, estimates the number of chunks to process, and completes without changing any data. Read the job back (Step 3) — its result contains the plan, including estimatedChunks, the current collection, and the target model/dimension. Use the estimate to size the run (see Sizing).


Step 2 — Run the switch

graphql
mutation {
  createMaintenanceJob(input: {
    operationType: "SWITCH_EMBEDDING_MODEL"
    newEmbeddingModel: "text-embedding-3-large"
    newEmbeddingDimension: 3072
    destructive: false
    dryRun: false
  }) { id status createdAt }
}

Input fields

Field

Required

Description

operationType

"SWITCH_EMBEDDING_MODEL"

newEmbeddingModel

The model key as registered for the tenant (e.g. text-embedding-3-large).

newEmbeddingDimension

The vector dimension of the new model (e.g. 3072 for text-embedding-3-large). Must match the model's actual output dimension.

destructive

➖ (default false)

false = keep the old collection until the new one is durable (recommended, allows rollback). true = delete the old collection up front (no rollback).

dryRun

➖ (default false)

true = preview only (Step 1).

Invoking it (cURL) — authenticate with a chat.admin.all bearer token; companyId is derived from the token:

bash
curl -X POST "$INGESTION_GRAPHQL_URL" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"mutation { createMaintenanceJob(input:{operationType:\"SWITCH_EMBEDDING_MODEL\",newEmbeddingModel:\"text-embedding-3-large\",newEmbeddingDimension:3072,destructive:false,dryRun:false}){ id status } }"}'

$INGESTION_GRAPHQL_URL is the tenant's ingestion GraphQL endpoint and $ADMIN_TOKEN is a Zitadel token for a chat.admin.all user — ask Unique for the endpoint if you don't have it. The mutation returns the new job's id.


Step 3 — Monitor

A background worker claims the job within ~60 seconds of creation. Track it with:

graphql
query {
  maintenanceJob(jobId: "mjob_xxx") {
    id status phase processed total failed errorMessage startedAt completedAt result
  }
}

List all jobs for the company with maintenanceJobs { id status phase processed total operationType createdAt }.

Field

Meaning

status

PENDINGRUNNINGCOMPLETED (or FAILED / CANCELLED).

phase

preparingreembeddingswapcleanup.

processed / total

Progress (chunks re-embedded vs. estimated total).

failed

Per-item failures (non-fatal).

errorMessage

Populated when status = FAILED.

Cancel a running job with cancelMaintenanceJob(jobId: "mjob_xxx").


Sizing & duration

Throughput is bounded by the embedding model's tokens-per-minute (TPM) quota:

none
estimated minutes  ≈  (total_chunks × avg_tokens_per_chunk) ÷ TPM_quota
chunks per minute  ≈  TPM_quota ÷ avg_tokens_per_chunk

Worked example: ~750,000 TPM quota, ~400 tokens/chunk → ~1,800 chunks/min → a 1.9M-chunk tenant ≈ ~17 hours. A few-thousand-TPM quota would make the same tenant take many days — raise the quota for large tenants.

Re-embedding is processed sequentially (one batch at a time). The single biggest lever on duration is the embedding deployment's TPM quota.


Safety, atomicity & rollback

  • Non-destructive by default: users keep searching on the existing model for the entire run; the switch is atomic at the very end. If the job fails before the swap, nothing changes for users.

  • Checkpoint / resume: progress is recorded continuously; a worker restart resumes from the last checkpoint rather than starting over.

  • destructive: true deletes the old collection up front — only use it when you cannot keep both collections and you accept that there is no rollback.

  • Rollback after a completed switch: run the switch again, targeting the previous model and dimension (this is a full re-embed back to the old model).


Troubleshooting

Symptom (in errorMessage / logs)

Cause

Resolution

429 ... exceeded call rate limit ... S0 pricing tier

Embedding deployment quota too low

Raise the model deployment's TPM/RPM quota, then re-run. Transient 429s are retried automatically; a persistently throttled deployment will still exhaust retries.

400 Invalid 'input': maximum request size is 300000 tokens per request

Batch is larger than the provider's per-request token cap

Reduce the batch size (MAINTENANCE_OPS_CHUNK_BATCH_SIZE), e.g. 512 → 128. Re-run.

Transient vector-DB connection drops (EPIPE, socket errors, "Insert into qdrant did not work")

Network / keep-alive blips to the vector DB

Retried automatically in current versions. If it persists, check vector-DB health/capacity.

Dimension mismatch on upsert

newEmbeddingDimension ≠ the model's real output dimension

Set newEmbeddingDimension to the model's actual dimension (e.g. 3072 for text-embedding-3-large).

A maintenance job for this company is still holding the lock

A job is active or recently finished (one job per company at a time)

Wait for it to finish, or cancelMaintenanceJob, then retry.

Job stays PENDING

No worker picked it up

Ensure the ingestion / maintenance worker is running.


Configuration reference

Setting

Default

Purpose

MAINTENANCE_OPS_CHUNK_BATCH_SIZE

512

Number of chunks per embedding request. Lower it if batch × avg-chunk-tokens approaches the provider's per-request token cap (≈300k).

Per-company embedding model registration

The new model must be registered/available for the tenant before switching.

Note on multi-endpoint embedding distribution: in environments where embedding requests are distributed across multiple endpoints, confirm that per-company model overrides are honored for the target company before running — otherwise the re-embed may use the default model. Check with Unique if your tenant uses multi-endpoint distribution.


Known limitations

  • One maintenance job per company at a time (enforced by a per-company lock).

  • A FAILED job is not auto-resumed — re-trigger it to restart (it re-embeds into a fresh collection; the partial collection from the failed run is garbage-collected).

  • Runtime scales with corpus size and embedding quota — large tenants take hours; plan accordingly and monitor.


This runbook documents the SWITCH_EMBEDDING_MODEL maintenance operation. Two sibling operations (RESHARD_COLLECTION, REBUILD_INDEX) exist for other vector-store maintenance and are out of scope here.

Last updated