Cuando tu pipeline de RAG está en producción, lo más común no es “cambiar el modelo”, sino algo más frecuente: el documento cambió (se editó, se reemplazó, se corrigió un párrafo) y necesitas actualizar los embeddings correspondientes en tu vector store. Este post describe un proceso práctico y seguro usando LangChain y PostgreSQL + pgvector.
⸻
Objetivo
Actualizar embeddings sin downtime, minimizando costo y evitando inconsistencias:
• Solo re-embeddear lo que cambió.
• Mantener IDs estables para hacer upsert.
• Borrar chunks obsoletos.
• Proteger la lectura (retrieval) mientras ocurre la actualización.
⸻
1) Principio clave: actualizar por chunk, no por documento
En producción, el documento suele partirse en chunks. Si solo cambió una sección, no tiene sentido recalcular todo.
Recomendación:
• Chunking determinista (misma regla de split).
• Para cada chunk, guarda un content_hash (hash del texto normalizado).
• Si el hash no cambia, no recalcules embedding.
⸻
2) IDs determinísticos (la base del upsert)
La actualización en pgvector normalmente no es “update por contenido”; es update por ID.
Ejemplo de ID estable:
• "{doc_id}:{chunk_index}" (simple)
• "{doc_id}:{chunk_hash_prefix}" (mejor si el chunking puede moverse)
Con IDs estables, puedes enviar el mismo ID y sobrescribir el vector + metadata (“upsert”).
⸻
3) LangChain + PGVector: consideraciones de integración
En el stack actual, la integración recomendada para PostgreSQL en LangChain es langchain-postgres, que requiere psycopg3 y pasar un connection object explícito. 
Además, LangChain advierte que cambios de esquema pueden implicar recrear tablas y reinsertar datos; por eso conviene decidir el diseño de metadata/IDs desde el inicio. 
⸻
4) Flujo de actualización (A) paso a paso
Paso 1 — Construir el “diff”
Para un doc_id:
1. Obtén el estado anterior (idealmente desde una tabla tuya): lista de chunk IDs + hashes.
2. Re-chunkea el documento actualizado.
3. Calcula:
• to_upsert: chunks nuevos o con hash distinto.
• to_delete: IDs que existían y ya no están.
En producción suele ser más confiable mantener un “inventario” de chunks por documento en tablas propias (Postgres normal), en vez de depender de “listar por metadata” desde el vector store.
Paso 2 — Upsert de chunks cambiados
Con PGVector, agrega documentos con ids=... para que el write sea determinista. La API expone métodos para agregar y usar IDs. 
`import psycopg
from langchain_postgres.vectorstores import PGVector
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings
conn = psycopg.connect("postgresql://user:pass@host:5432/db")
emb = OpenAIEmbeddings(model="text-embedding-3-small") # 1536 dims por defecto oai_citation:3‡OpenAI Platform
vs = PGVector(
embeddings=emb,
connection=conn,
collection_name="docs_v1",
use_jsonb=True,
)
docs = [
Document(
page_content=chunk_text,
metadata={
"doc_id": doc_id,
"chunk_index": i,
"content_hash": h,
"updated_at": updated_at_iso,
"embedding_model": "text-embedding-3-small",
},
)
for i, (chunk_text, h) in enumerate(chunks_to_upsert)
]
ids = [f"{doc_id}:{chunk_index}" for chunk_index, _ in enumerate(chunks_to_upsert)]
vs.add_documents(docs, ids=ids)`
Paso 3 — Borrar chunks obsoletos
Borra por ID (sync o async). LangChain documenta delete/adelete por IDs. 
`# sync
vs.delete(ids=obsolete_ids)
async (si estás en async)
await vs.adelete(ids=obsolete_ids)`
⸻
5) Lecturas consistentes durante el update (sin downtime)
Patrón recomendado: “doc_version” (2 fases)
Para evitar que el retrieval mezcle chunks viejos y nuevos:
1. Genera una doc_version nueva (p.ej. timestamp o número).
2. Upsertea chunks nuevos con metadata.doc_version = new_version.
3. Actualiza en una tabla de control docs_active_version(doc_id) = new_version.
4. Borra chunks de versiones anteriores (o márcalos como inactivos).
En tiempo de búsqueda, filtras por doc_id y doc_version activa. La búsqueda en LangChain soporta filtros en los métodos de similarity search. 
⸻
6) Dimensiones: evita sorpresas con pgvector
Si cambias de modelo, revisa dimensiones. OpenAI indica por defecto:
• text-embedding-3-small: 1536
• text-embedding-3-large: 3072 
Y pgvector especifica límites por tipo:
• vector: hasta 2000 dims
• halfvec: hasta 4000 dims 
Esto afecta la elección de modelo (o la necesidad de reducir dimensiones si tu proveedor lo permite).
⸻
7) Índices para rendimiento (HNSW / IVFFlat)
pgvector soporta índices ANN. Para cosine con HNSW: 
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
Y con parámetros típicos: 
CREATE INDEX ON items
USING hnsw (embedding vector_l2_ops)
WITH (m = 16, ef_construction = 64);
⸻
Checklist de producción
• Chunking determinista + content_hash.
• IDs determinísticos por chunk (upsert idempotente).
• “Diff” (upsert solo cambios, delete obsoletos).
• Control de consistencia de lectura (doc_version).
• Índice ANN (HNSW/IVFFlat) y monitoreo de latencia.
• Validación de dimensiones vs tipo (vector/halfvec). 
⸻
Si compartes tu patrón actual de chunking (tamaño/overlap) y volumen (chunks totales), puedo proponer:
1. un esquema mínimo de tablas para inventario de chunks/versiones, y
2. un job de actualización con transacciones y reintentos (idempotente) listo para correr en prod.
Top comments (0)