<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Felipe Camargo Polo</title>
    <description>The latest articles on DEV Community by David Felipe Camargo Polo (@davidcamargo).</description>
    <link>https://dev.to/davidcamargo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1003116%2Fc04c225c-7fab-4625-951a-f5a3257fc1e5.png</url>
      <title>DEV Community: David Felipe Camargo Polo</title>
      <link>https://dev.to/davidcamargo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davidcamargo"/>
    <language>en</language>
    <item>
      <title>Actualización de embeddings en producción con LangChain + pgvector</title>
      <dc:creator>David Felipe Camargo Polo</dc:creator>
      <pubDate>Thu, 05 Feb 2026 18:43:53 +0000</pubDate>
      <link>https://dev.to/davidcamargo/actualizacion-de-embeddings-en-produccion-con-langchain-pgvector-3ed5</link>
      <guid>https://dev.to/davidcamargo/actualizacion-de-embeddings-en-produccion-con-langchain-pgvector-3ed5</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;Objetivo&lt;/p&gt;

&lt;p&gt;Actualizar embeddings sin downtime, minimizando costo y evitando inconsistencias:&lt;br&gt;
    • Solo re-embeddear lo que cambió.&lt;br&gt;
    • Mantener IDs estables para hacer upsert.&lt;br&gt;
    • Borrar chunks obsoletos.&lt;br&gt;
    • Proteger la lectura (retrieval) mientras ocurre la actualización.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;1) Principio clave: actualizar por chunk, no por documento&lt;/p&gt;

&lt;p&gt;En producción, el documento suele partirse en chunks. Si solo cambió una sección, no tiene sentido recalcular todo.&lt;/p&gt;

&lt;p&gt;Recomendación:&lt;br&gt;
    • Chunking determinista (misma regla de split).&lt;br&gt;
    • Para cada chunk, guarda un content_hash (hash del texto normalizado).&lt;br&gt;
    • Si el hash no cambia, no recalcules embedding.&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;2) IDs determinísticos (la base del upsert)&lt;/p&gt;

&lt;p&gt;La actualización en pgvector normalmente no es “update por contenido”; es update por ID.&lt;/p&gt;

&lt;p&gt;Ejemplo de ID estable:&lt;br&gt;
    • "{doc_id}:{chunk_index}" (simple)&lt;br&gt;
    • "{doc_id}:{chunk_hash_prefix}" (mejor si el chunking puede moverse)&lt;/p&gt;

&lt;p&gt;Con IDs estables, puedes enviar el mismo ID y sobrescribir el vector + metadata (“upsert”).&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;3) LangChain + PGVector: consideraciones de integración&lt;/p&gt;

&lt;p&gt;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.  ￼&lt;/p&gt;

&lt;p&gt;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.  ￼&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;4) Flujo de actualización (A) paso a paso&lt;/p&gt;

&lt;p&gt;Paso 1 — Construir el “diff”&lt;/p&gt;

&lt;p&gt;Para un doc_id:&lt;br&gt;
    1.  Obtén el estado anterior (idealmente desde una tabla tuya): lista de chunk IDs + hashes.&lt;br&gt;
    2.  Re-chunkea el documento actualizado.&lt;br&gt;
    3.  Calcula:&lt;br&gt;
    • to_upsert: chunks nuevos o con hash distinto.&lt;br&gt;
    • to_delete: IDs que existían y ya no están.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Paso 2 — Upsert de chunks cambiados&lt;/p&gt;

&lt;p&gt;Con PGVector, agrega documentos con ids=... para que el write sea determinista. La API expone métodos para agregar y usar IDs.  ￼&lt;/p&gt;

&lt;p&gt;`import psycopg&lt;br&gt;
from langchain_postgres.vectorstores import PGVector&lt;br&gt;
from langchain_core.documents import Document&lt;br&gt;
from langchain_openai import OpenAIEmbeddings&lt;/p&gt;

&lt;p&gt;conn = psycopg.connect("postgresql://user:pass@host:5432/db")&lt;/p&gt;

&lt;p&gt;emb = OpenAIEmbeddings(model="text-embedding-3-small")  # 1536 dims por defecto  &lt;a href="https://platform.openai.com/docs/guides/embeddings?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;oai_citation:3‡OpenAI Platform&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;vs = PGVector(&lt;br&gt;
    embeddings=emb,&lt;br&gt;
    connection=conn,&lt;br&gt;
    collection_name="docs_v1",&lt;br&gt;
    use_jsonb=True,&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;docs = [&lt;br&gt;
    Document(&lt;br&gt;
        page_content=chunk_text,&lt;br&gt;
        metadata={&lt;br&gt;
            "doc_id": doc_id,&lt;br&gt;
            "chunk_index": i,&lt;br&gt;
            "content_hash": h,&lt;br&gt;
            "updated_at": updated_at_iso,&lt;br&gt;
            "embedding_model": "text-embedding-3-small",&lt;br&gt;
        },&lt;br&gt;
    )&lt;br&gt;
    for i, (chunk_text, h) in enumerate(chunks_to_upsert)&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;ids = [f"{doc_id}:{chunk_index}" for chunk_index, _ in enumerate(chunks_to_upsert)]&lt;/p&gt;

&lt;p&gt;vs.add_documents(docs, ids=ids)`&lt;/p&gt;

&lt;p&gt;Paso 3 — Borrar chunks obsoletos&lt;/p&gt;

&lt;p&gt;Borra por ID (sync o async). LangChain documenta delete/adelete por IDs.  ￼&lt;/p&gt;

&lt;p&gt;`# sync&lt;br&gt;
vs.delete(ids=obsolete_ids)&lt;/p&gt;

&lt;h1&gt;
  
  
  async (si estás en async)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  await vs.adelete(ids=obsolete_ids)`
&lt;/h1&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;5) Lecturas consistentes durante el update (sin downtime)&lt;/p&gt;

&lt;p&gt;Patrón recomendado: “doc_version” (2 fases)&lt;/p&gt;

&lt;p&gt;Para evitar que el retrieval mezcle chunks viejos y nuevos:&lt;br&gt;
    1.  Genera una doc_version nueva (p.ej. timestamp o número).&lt;br&gt;
    2.  Upsertea chunks nuevos con metadata.doc_version = new_version.&lt;br&gt;
    3.  Actualiza en una tabla de control docs_active_version(doc_id) = new_version.&lt;br&gt;
    4.  Borra chunks de versiones anteriores (o márcalos como inactivos).&lt;/p&gt;

&lt;p&gt;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.  ￼&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;6) Dimensiones: evita sorpresas con pgvector&lt;/p&gt;

&lt;p&gt;Si cambias de modelo, revisa dimensiones. OpenAI indica por defecto:&lt;br&gt;
    • text-embedding-3-small: 1536&lt;br&gt;
    • text-embedding-3-large: 3072  ￼&lt;/p&gt;

&lt;p&gt;Y pgvector especifica límites por tipo:&lt;br&gt;
    • vector: hasta 2000 dims&lt;br&gt;
    • halfvec: hasta 4000 dims  ￼&lt;/p&gt;

&lt;p&gt;Esto afecta la elección de modelo (o la necesidad de reducir dimensiones si tu proveedor lo permite).&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;7) Índices para rendimiento (HNSW / IVFFlat)&lt;/p&gt;

&lt;p&gt;pgvector soporta índices ANN. Para cosine con HNSW:  ￼&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Y con parámetros típicos:  ￼&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CREATE INDEX ON items&lt;br&gt;
USING hnsw (embedding vector_l2_ops)&lt;br&gt;
WITH (m = 16, ef_construction = 64);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;Checklist de producción&lt;br&gt;
    • Chunking determinista + content_hash.&lt;br&gt;
    • IDs determinísticos por chunk (upsert idempotente).&lt;br&gt;
    • “Diff” (upsert solo cambios, delete obsoletos).&lt;br&gt;
    • Control de consistencia de lectura (doc_version).&lt;br&gt;
    • Índice ANN (HNSW/IVFFlat) y monitoreo de latencia.&lt;br&gt;
    • Validación de dimensiones vs tipo (vector/halfvec).  ￼&lt;/p&gt;

&lt;p&gt;⸻&lt;/p&gt;

&lt;p&gt;Si compartes tu patrón actual de chunking (tamaño/overlap) y volumen (chunks totales), puedo proponer:&lt;br&gt;
    1.  un esquema mínimo de tablas para inventario de chunks/versiones, y&lt;br&gt;
    2.  un job de actualización con transacciones y reintentos (idempotente) listo para correr en prod.&lt;/p&gt;

</description>
      <category>langchain</category>
    </item>
  </channel>
</rss>
