Preparación de índices antes de actualizar a 2.5.1#
Este procedimiento se aplica al actualizar de 2.5.0 a 2.5.1. Para otra compilación con un esquema de base de datos particionado equivalente, utilícelo solo después de que el administrador y el equipo de soporte confirmen que corresponde. Si la versión actual es anterior a 2.5.0, por ejemplo 2.4.10, no ejecute los comandos SQL siguientes sin acordar antes la ruta de actualización.
Para esta actualización, prepare los índices antes de ejecutar update.sh o las migraciones manuales de la versión 2.5.1. El script de actualización inicia las migraciones, pero no realiza esta preparación. La migración comprueba que los índices estén listos y no terminará sin ellos.
Esta tarea corresponde a un administrador de bases de datos. La creación de índices en una base grande puede llevar tiempo; haga una copia de seguridad y programe una ventana de actualización. Necesita psql conectado a la misma base de datos que usa Sherpa AI Server y un rol con permisos para crear índices en las tablas. Cree los archivos SQL en el equipo donde ejecutará psql: el archivo de instalación del cliente no los incluye.
1. Compruebe la conexión a la base de datos#
Sustituya DB_HOST, DB_OWNER y SHERPA_DB por los parámetros reales de su base de datos. Si ejecuta psql dentro del contenedor de la base de datos, utilice los parámetros disponibles allí. Confirme la base de datos y el usuario antes de continuar:
export PGHOST='DB_HOST'
export PGPORT='5432'
export PGUSER='DB_OWNER'
export PGDATABASE='SHERPA_DB'
psql -X -v ON_ERROR_STOP=1 -c 'SELECT current_database(), current_user;'
En una instalación estándar desde el archivo del cliente, puede usar psql dentro del contenedor PostgreSQL. Desde el directorio que contiene docker-compose.yml y .env, compruebe la conexión sin instalar psql en el equipo anfitrión:
docker compose exec -T aiserver-pg sh -c 'psql -X -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT current_database(), current_user;"'
Compruebe que public.embeddings es una tabla particionada con una columna account_id. Utilice el comando correspondiente a su método de conexión:
psql -X -v ON_ERROR_STOP=1 -Atc "SELECT EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace JOIN pg_attribute a ON a.attrelid = c.oid WHERE n.nspname = 'public' AND c.relname = 'embeddings' AND c.relkind = 'p' AND a.attname = 'account_id' AND a.attnum > 0 AND NOT a.attisdropped) AS schema_ready;"
En una instalación estándar del cliente, utilice el contenedor de la base de datos:
printf '%s\n' "SELECT EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace JOIN pg_attribute a ON a.attrelid = c.oid WHERE n.nspname = 'public' AND c.relname = 'embeddings' AND c.relkind = 'p' AND a.attname = 'account_id' AND a.attnum > 0 AND NOT a.attisdropped) AS schema_ready;" | docker compose exec -T aiserver-pg sh -c 'psql -X -v ON_ERROR_STOP=1 -At -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f -'
El resultado esperado es t. Si obtiene f, un error o ningún resultado, no ejecute los archivos SQL siguientes ni update.sh. Acuerde primero los siguientes pasos con el equipo de soporte.
2. Guarde el SQL para la búsqueda vectorial#
Copie todo el contenido del siguiente bloque en el archivo prepare_dimension_hnsw.sql sin modificarlo:
-- Run with psql -X -v ON_ERROR_STOP=1 -f prepare_dimension_hnsw.sql before
-- Phinx migration 20260930160000 on a database that already has vectors.
-- Each command runs outside a transaction so PostgreSQL can build online.
-- The migration verifies these indexes before its short metadata cutover.
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON public.%I USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=384, m=8, efconstruction=64, efsearch=800) '
|| 'WHERE cardinality(embedding_vector) = 384',
child.relname || '_dim_384_hnsw_idx', child.relname
)
FROM pg_inherits inheritance
JOIN pg_class child ON child.oid = inheritance.inhrelid
WHERE inheritance.inhparent = 'public.embeddings'::regclass
ORDER BY child.relname
\gexec
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON orchestrator.samples USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=384, m=8, efconstruction=64, efsearch=64) '
|| 'WHERE cardinality(embedding_vector) = 384',
'samples_dim_384_hnsw_idx'
)
\gexec
-- Prepare the standard 384↔1024 model switch before the migration. The
-- 1024 predicates are empty on 384-only accounts but building them online
-- here prevents the first 1024 request from building HNSW on live data.
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON public.%I USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=1024, m=8, efconstruction=64, efsearch=800) '
|| 'WHERE cardinality(embedding_vector) = 1024',
child.relname || '_dim_1024_hnsw_idx', child.relname
)
FROM pg_inherits inheritance
JOIN pg_class child ON child.oid = inheritance.inhrelid
WHERE inheritance.inhparent = 'public.embeddings'::regclass
ORDER BY child.relname
\gexec
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON orchestrator.samples USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=1024, m=8, efconstruction=64, efsearch=64) '
|| 'WHERE cardinality(embedding_vector) = 1024',
'samples_dim_1024_hnsw_idx'
)
\gexec
-- Older installations might already contain other dimensions where the old
-- index was absent. Preserve them without building a blocking index in Phinx.
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON public.%I USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=%s, m=8, efconstruction=64, efsearch=800) '
|| 'WHERE cardinality(embedding_vector) = %s',
format('embeddings_account_%s_dim_%s_hnsw_idx', account_id, dimension),
format('embeddings_account_%s', account_id),
dimension, dimension
)
FROM (
SELECT account_id, cardinality(embedding_vector) AS dimension
FROM public.embeddings
WHERE cardinality(embedding_vector) NOT IN (384, 1024)
GROUP BY account_id, cardinality(embedding_vector)
) dimensions
ORDER BY account_id, dimension
\gexec
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON orchestrator.samples USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=%s, m=8, efconstruction=64, efsearch=64) '
|| 'WHERE cardinality(embedding_vector) = %s',
format('samples_dim_%s_hnsw_idx', dimension), dimension, dimension
)
FROM (
SELECT cardinality(embedding_vector) AS dimension
FROM orchestrator.samples
WHERE embedding_vector IS NOT NULL
AND cardinality(embedding_vector) NOT IN (384, 1024)
GROUP BY cardinality(embedding_vector)
) dimensions
ORDER BY dimension
\gexec
3. Guarde el SQL para comprobar fragmentos duplicados#
Copie todo el contenido del siguiente bloque en el archivo prepare_documents_dedup_index.sql sin modificarlo:
-- Run with psql -X -v ON_ERROR_STOP=1 -f before Phinx migration
-- 20260930161000 on an existing database with documents. psql must not wrap
-- this command in a transaction: the index is built without blocking writes.
CREATE INDEX CONCURRENTLY IF NOT EXISTS documents_file_text_md5_active_idx
ON public.documents (file_id, md5(text_chunk))
WHERE NOT is_deleted;
Compruebe los archivos guardados con estas sumas SHA-256 de los bloques anteriores (incluido el salto de línea final):
sha256sum prepare_dimension_hnsw.sql prepare_documents_dedup_index.sql
fe7e626f0bfb5d8d6b8a8147ee128f4ae6b830ca7753c44648ab812229c17521 prepare_dimension_hnsw.sql
dfde574e8a213c32b2e2cb16f713701d29d1991de905f1e16d414e9b0dbe9401 prepare_documents_dedup_index.sql
Si las sumas no coinciden, corrija los archivos antes de ejecutarlos.
4. Ejecute ambos archivos antes de actualizar#
Ejecute estos comandos uno por uno en el equipo que contiene los archivos. Cada comando debe terminar con el código 0. No utilice psql -1 ni BEGIN: CREATE INDEX CONCURRENTLY debe ejecutarse fuera de una transacción común.
psql -X -v ON_ERROR_STOP=1 -f prepare_dimension_hnsw.sql
psql -X -v ON_ERROR_STOP=1 -f prepare_documents_dedup_index.sql
Si psql no está disponible en el equipo anfitrión, utilice estos comandos en su lugar desde el directorio con docker-compose.yml. Los archivos SQL permanecen en el equipo anfitrión y se transmiten al contenedor por la entrada estándar:
docker compose exec -T aiserver-pg sh -c 'psql -X -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f -' < prepare_dimension_hnsw.sql
docker compose exec -T aiserver-pg sh -c 'psql -X -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f -' < prepare_documents_dedup_index.sql
5. Compruebe el resultado#
Después de ejecutar correctamente ambos archivos, esta consulta no debe devolver ninguna fila con índices no válidos:
psql -X -v ON_ERROR_STOP=1 -c "SELECT n.nspname AS schema_name, c.relname AS invalid_index FROM pg_index i JOIN pg_class c ON c.oid = i.indexrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE NOT i.indisvalid AND (c.relname ~ '_dim_[0-9]+_hnsw_idx$' OR c.relname = 'documents_file_text_md5_active_idx');"
Si usa el contenedor PostgreSQL, realice la misma comprobación de esta manera:
printf "%s\n" "SELECT n.nspname AS schema_name, c.relname AS invalid_index FROM pg_index i JOIN pg_class c ON c.oid = i.indexrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE NOT i.indisvalid AND (c.relname ~ '_dim_[0-9]+_hnsw_idx$' OR c.relname = 'documents_file_text_md5_active_idx');" | docker compose exec -T aiserver-pg sh -c 'psql -X -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f -'
Si un comando falla o la consulta muestra un índice no válido, deténgase y resuelva el problema con el administrador de bases de datos antes de actualizar. Cuando las comprobaciones sean satisfactorias, ejecute update.sh o su procedimiento de migración manual. No hace falta volver a indexar los documentos solo por esta preparación: los índices se crean sobre las tablas existentes e incluyen los registros que ya estén presentes.
6. Dimensiones adicionales después de la actualización #
Utilice esta sección después de las migraciones de la versión 2.5.1 si el nuevo modelo produce vectores con una dimensión distinta de 384 o 1024. Prepare un índice para cada cuenta afectada antes de guardar el nuevo modelo en los ajustes de indexación. Obtenga el account_id numérico en la base de datos de Sherpa AI Server y la dimensión real del vector (dimension, de 32 a 2000). Ejecute la preparación por separado para cada cuenta. Las dimensiones estándar 384 y 1024 se prepararon con los pasos anteriores antes de actualizar.
Copie todo el contenido del siguiente bloque en el archivo prepare_additional_dimension_hnsw.sql sin modificarlo:
-- Prepare one account and the shared samples table for an additional embedding
-- dimension after migration 20260930160000. Run with psql outside a transaction:
-- psql -X -v ON_ERROR_STOP=1 -v account_id=123 -v dimension=768 \
-- -f backend/migrations/prepare_additional_dimension_hnsw.sql
-- CREATE INDEX CONCURRENTLY keeps ordinary inserts and updates available.
\set AUTOCOMMIT on
SELECT CASE WHEN EXISTS (
SELECT 1 FROM orchestrator.accounts WHERE id = :'account_id'::integer
) AND :'dimension'::integer BETWEEN 32 AND 2000
THEN 'true' ELSE 'false' END AS valid_parameters
\gset
\if :valid_parameters
\else
\echo 'Expected an existing account_id and a dimension between 32 and 2000'
SELECT 1 / 0;
\endif
-- Before the tuning migration, keep the index compatible with its migration
-- precheck. Afterwards create future dimensions with the tuned search depth.
SELECT CASE WHEN EXISTS (
SELECT 1 FROM orchestrator.phinxlog WHERE version = 20260930163000
) THEN 32000 ELSE 800 END AS hnsw_efsearch
\gset
-- The session lock serializes simultaneous preparation of the shared samples
-- index. It is released automatically if psql exits on an error.
SELECT pg_advisory_lock(2291, -1);
SELECT public.ensure_embeddings_account_partition(:'account_id'::integer);
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON public.%I USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=%s, m=8, efconstruction=64, efsearch=%s) '
|| 'WHERE cardinality(embedding_vector) = %s',
format('embeddings_account_%s_dim_%s_hnsw_idx', :'account_id'::integer, :'dimension'::integer),
format('embeddings_account_%s', :'account_id'::integer),
:'dimension'::integer,
:'hnsw_efsearch'::integer,
:'dimension'::integer
)
\gexec
SELECT format(
'CREATE INDEX CONCURRENTLY IF NOT EXISTS %I ON orchestrator.samples USING hnsw '
|| '(embedding_vector public.ann_cos_ops) '
|| 'WITH (dims=%s, m=8, efconstruction=64, efsearch=64) '
|| 'WHERE cardinality(embedding_vector) = %s',
format('samples_dim_%s_hnsw_idx', :'dimension'::integer),
:'dimension'::integer,
:'dimension'::integer
)
\gexec
SELECT public.ensure_embeddings_dimension_index(:'account_id'::integer, :'dimension'::integer);
SELECT orchestrator.ensure_samples_dimension_index(:'dimension'::integer);
SELECT pg_advisory_unlock(2291, -1);
Suma SHA-256 del archivo, incluido el salto de línea final:
sha256sum prepare_additional_dimension_hnsw.sql
94e18e038e02371a0859d60ca8f8e6345d0d5091e04f1659a8beecba034c4702 prepare_additional_dimension_hnsw.sql
Sustituya ACCOUNT_ID y DIMENSION por los números reales. Ejecute este comando fuera de una transacción común con los mismos parámetros de conexión de psql:
psql -X -v ON_ERROR_STOP=1 -v account_id=ACCOUNT_ID -v dimension=DIMENSION -f prepare_additional_dimension_hnsw.sql
Para la instalación estándar del cliente, ejecute lo siguiente desde el directorio que contiene docker-compose.yml para utilizar el contenedor de la base de datos:
docker compose exec -T aiserver-pg sh -c 'psql -X -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v account_id=ACCOUNT_ID -v dimension=DIMENSION -f -' < prepare_additional_dimension_hnsw.sql
El comando debe terminar con código 0: el propio script comprueba que la cuenta existe, que la dimensión es válida y que ambos índices están listos. Si falla, no guarde el nuevo modelo de indexación hasta resolver la causa. Tras completar la preparación, vuelva al procedimiento para cambiar el modelo de indexación.