RAG sobre Kafka: arquitectura técnica de referencia para datalakes en streaming, con embeddings frescos y vector stores siempre al día
TL;DR
La pieza que más bloquea proyectos GenAI empresariales en 2026 no es el modelo, ni siquiera los guardrails: es la ingestión de datos para RAG. Las empresas tienen información valiosa en bases de datos OLTP, en logs operacionales, en sistemas SaaS, y todo eso está silenciosamente cambiando cada segundo. Los RAG batch que se reindexan cada noche llegan tarde —la respuesta del modelo está respaldada en un snapshot de hace 18 horas— y dan paso a alucinaciones operacionales aunque el retriever sea perfecto. La respuesta dominante en producción en 2026 es montar la pieza RAG sobre Kafka como source-of-truth: log inmutable, throughput masivo, schema evolution gestionada, y un ecosistema de stream processing maduro (Flink, Kafka Streams, RisingWave) que permite transformar y embedder eventos a medida que ocurren, llevándolos en milisegundos a vector stores (Milvus, Qdrant, Weaviate, pgvector). El patrón canónico: origen → CDC con Debezium → topics Kafka → Flink SQL con embedding UDF → sink connector a vector store → serving con vLLM o equivalente. Las novedades 2026 que cambian el juego: Confluent Tableflow convierte topics Kafka en tablas Iceberg/Delta automáticamente (lectura desde Snowflake/Databricks/Trino sin ETL, 30-50% menos TCO); Flink SQL nativo trae openai_embedding() y vector search integrado con Cosmos DB y Amazon S3 Vectors; el MCP server oficial de Confluent permite a agentes IA consultar Kafka/Flink/Tableflow en lenguaje natural. Este post desarrolla la arquitectura end-to-end con manifests, código Flink SQL y números concretos.
Este es el segundo post de la serie MLOps específico para LLMs. El primero (Panorama 2026) estableció el marco. Aquí bajamos a la pieza más operacional del stack: cómo se conecta un sistema empresarial real a un agente LLM manteniendo el RAG fresco sin caer en complejidad explosiva.
La analogía: Kafka como el “single source of truth”
Quien lleva tiempo en sistemas distribuidos ha visto el patrón una y otra vez: un log inmutable, append-only, replicado, ordenado en el tiempo se ha vuelto la primitiva canónica para reconstruir sistemas complejos. Los DBAs lo conocen como write-ahead log (PostgreSQL WAL, MySQL binlog). Los desarrolladores de sistemas de eventos lo conocen como event sourcing. Los arquitectos de datos lo conocen como Kappa architecture. Kafka es la implementación masiva, distribuida y madura de esa primitiva: un log que vive en disco, particionado para escalar, replicado para durabilidad, retenido por tiempo o tamaño, legible desde cualquier punto histórico.
Cuando se piensa en RAG, esto es exactamente lo que se necesita. Un sistema RAG bien diseñado tiene dos preguntas críticas: ¿cómo se mantiene fresco el índice? y ¿cómo se reconstruye el índice cuando algo se rompe? Las dos las contesta Kafka de manera natural: fresco porque cada cambio en el origen se publica como evento al log y el pipeline lo procesa en milisegundos; reconstruible porque el log entero está ahí: borras el vector store, dispones del topic Kafka desde el offset 0 y vuelves a construir el índice tal como estaba.
Hay además una segunda capa de analogía. Kafka, para una arquitectura GenAI moderna, juega el papel del WAL del sistema entero. Igual que el WAL de Postgres es el evangelio del estado de la base de datos —si pierdes la DB pero conservas el WAL, puedes reconstruirla—, el log de Kafka es el evangelio del estado del conjunto del negocio: pedidos, usuarios, transacciones, documentos. Conectar tu agente IA a Kafka es conectarlo al pulso real del sistema, no a snapshots obsoletos.
El problema del RAG estático
Antes de presentar la arquitectura, vale la pena fijar qué problema concreto estamos resolviendo. El antipattern que tropieza a la mayoría de proyectos GenAI:
- Equipo construye RAG sobre un dataset estático: vuelca documentos de Confluence, PDFs de productos, snapshots de base de datos.
- Lo embedea con un cron nocturno que regenera el índice cada 24 horas.
- Lanza el producto.
- Día 2: usuario pregunta sobre un cambio que ocurrió hace dos horas. El RAG no lo tiene; el modelo responde sobre la versión vieja.
- Equipo añade lógica frágil: “si la query menciona una fecha reciente, escalar a un agente humano”.
- Día 30: el dataset se ha movido tanto que media RAG está desactualizado. El equipo decide refactor y migrar a streaming.
Es la historia repetida de tantos proyectos que el ecosistema ha aprendido la lección: streaming desde el día 1, aunque el volumen sea bajo. La complejidad operacional de un pipeline streaming bien diseñado es constante; la complejidad de migrar de batch a streaming en proyecto vivo es enorme.
Del Lambda al Kappa al Streaming RAG
Tres arquitecturas en orden histórico:
Lambda (clásica de big data 2014): dos pipelines paralelos, uno batch para precisión y uno streaming para freshness. La consulta combina ambos. Funciona pero exige mantener dos pipelines.
Kappa (Jay Kreps 2014, mainstream desde 2020): solo un pipeline streaming. El batch es un caso particular del streaming (reprocesar desde el principio). Simplifica mucho.
Streaming RAG (emergente 2025-2026): variante específica de Kappa donde el output del pipeline son embeddings indexados en un vector store que el LLM consulta en runtime. El log Kafka es la fuente de verdad, el vector store es un proyección consultable.
La conversión mental: piensa en el vector store como la vista materializada del log Kafka. Si la vista se corrompe, la reconstruyes desde el log. Si quieres una vista nueva (otro embedding model, otro chunking strategy), creas otro consumer del log y construyes una segunda vista en paralelo.
La arquitectura de referencia
Vamos al diagrama. Voy a presentar la arquitectura canónica que se ha estabilizado en 2026, mostrando dónde encaja cada componente:
[OLTP DB (Postgres)] [Otros origenes]
│ │
│ WAL via logical decoding │
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ Debezium / Kafka Connect (Sources) │
└──────────────────────────────────────────────────────────┘
│
▼ produce eventos
┌──────────────────────────────────────────────────────────┐
│ Kafka cluster │
│ ┌───────────────────────────────────────────────────┐ │
│ │ topic: orders.raw (3 particiones, RF=3) │ │
│ │ topic: users.raw (3 particiones, RF=3) │ │
│ │ topic: documents.raw (6 particiones, RF=3) │ │
│ └───────────────────────────────────────────────────┘ │
│ + Schema Registry (Avro/Protobuf) │
└──────────────────────────────────────────────────────────┘
│
▼ consume y transforma
┌──────────────────────────────────────────────────────────┐
│ Flink SQL streaming jobs │
│ - chunking text │
│ - llamadas a embedding model (UDF) │
│ - enriquecimiento con metadata │
│ - sink a topic curado: documents.embedded │
└──────────────────────────────────────────────────────────┘
│
┌───────────┼────────────────────┐
▼ ▼ ▼
[Vector store] [Tableflow] [Iceberg/Delta]
Milvus/Qdrant auto-convert para analytics
/pgvector/ topics →
Weaviate tables
│
▼ consultado en runtime
┌──────────────────────────────────────────────────────────┐
│ LLM serving (vLLM / SGLang) + Retriever │
│ - recibe query del agente │
│ - busca top-K en vector store │
│ - construye prompt + contexto │
│ - genera respuesta con citas │
└──────────────────────────────────────────────────────────┘
Las cinco capas que ves —fuente, ingestión (CDC), transporte (Kafka), procesamiento (Flink), almacenamiento (vector + tablas)— son las que estructuran cualquier RAG sobre datalake serio en 2026. Vamos a cada una.
Capa 1 — Fuentes: tu OLTP como punto de partida
La fuente típica es una base de datos OLTP (Postgres, MySQL, SQL Server). Es donde vive el estado vivo del negocio. La técnica para extraer cambios en tiempo real es Change Data Capture (CDC): leer el log de transacciones de la base de datos (PostgreSQL WAL, MySQL binlog) y convertir cada commit en un evento Kafka.
El estándar OSS es Debezium. Soporta Postgres, MySQL, SQL Server, MongoDB, Oracle, Cassandra y otros. Despliegue típico como cluster Kafka Connect con conectores Debezium.
Ejemplo de configuración Debezium para PostgreSQL:
{
"name": "postgres-orders-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"tasks.max": "1",
"database.hostname": "postgres.prod.internal",
"database.port": "5432",
"database.user": "debezium",
"database.password": "${secret:postgres-creds}",
"database.dbname": "ecommerce",
"database.server.name": "ecommerce-prod",
"table.include.list": "public.orders,public.users,public.products",
"publication.autocreate.mode": "filtered",
"slot.name": "debezium_slot",
"plugin.name": "pgoutput",
"topic.prefix": "ecommerce",
"key.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "http://schema-registry:8081",
"value.converter.schema.registry.url": "http://schema-registry:8081"
}
}
Esto produce, por cada commit en la base de datos, un evento Avro al topic correspondiente (ecommerce.public.orders, ecommerce.public.users, etc.) con el cambio: tipo (INSERT/UPDATE/DELETE), valores antes y después, timestamp del commit, posición en el WAL.
Alternativa más simple para 2026: RisingWave puede leerse el WAL de Postgres directamente, sin Debezium ni Kafka Connect intermedio. Cuando el caso es solo CDC sin más fuentes, es operacionalmente más simple. Para arquitecturas con múltiples fuentes (CDC + APIs + scrapers + logs), Debezium sigue siendo la pieza estándar.
Capa 2 — Kafka como transporte y persistencia
El cluster Kafka es donde aterrizan todos los eventos. Decisiones operativas clave:
Topics: raw vs curated
Convención que se ha establecido en 2026:
*.raw: el evento crudo tal como llegó. CDC sin transformar, log de aplicación sin parsear.*.cleaned: tras dedup, validación de schema, normalización de tipos.*.enriched: tras añadir metadatos (geolocalización, identificadores cruzados, etc.).*.embedded: el evento con su vector embedding ya calculado.
Multi-stage topics permite debug por capa y reprocesamiento parcial: si cambias el embedding model, descartar *.embedded y reconstruir desde *.enriched cuesta horas; reconstruir desde *.raw cuesta días.
Schema Registry
Sin schema registry, los topics se rompen silenciosamente cuando alguien cambia el schema en origen. Confluent Schema Registry o el OSS Apicurio son las opciones dominantes.
Formatos comunes:
- Avro: schema versionado, evolution rules estrictas. El default histórico.
- Protobuf: compatible con stacks gRPC, buena performance.
- JSON Schema: textual, debuggable a ojo, menos eficiente.
Para RAG sobre Kafka recomendamos Avro por defecto. Schema evolution es importante porque las tablas origen cambian con el tiempo, y un esquema sin versión rompe consumidores aguas abajo.
Particiones, replicación y retención
Decisiones operativas para topics de RAG:
- Particiones: típicamente 3-12. Más particiones = más paralelismo en consumer Flink, pero más overhead. La regla del pulgar: particiones = pico esperado de eventos/s ÷ 1000.
- Replication factor: 3 mínimo en producción. La replicación protege contra fallo de broker; con RAG el coste de perder un topic puede ser semanas de re-embedding.
- Retención: para topics que alimentan RAG, retención larga o compactada por key. Si el documento
doc-42cambia 100 veces, compactación solo guarda el último estado por key, dejando un log más pequeño y reconstruible. Para datos que no se actualizan (logs históricos), retención por tiempo (90 días, 1 año).
Replicación cross-cluster
Para deployments multi-región o multi-cloud, MirrorMaker 2 o Cluster Linking (Confluent) replican topics entre clusters Kafka. El RAG puede consultar el cluster local sin tener que cruzar región.
Capa 3 — Flink como procesador streaming
Apache Flink es la pieza dominante de stream processing en 2026. Apache 2.0, distribución mature, ecosistema amplio. La alternativa principal es Kafka Streams (más simple, Java-only); RisingWave es la opción emergente para casos SQL puros.
Lo que Flink añade a Kafka:
- Stateful streaming: agregaciones temporales, joins entre streams, sesiones.
- Exactly-once semantics: con checkpoint coordination.
- Watermarks: handling correcto de eventos out-of-order.
- UDFs en Python/Java: incluyendo llamadas a modelos LLM.
Flink SQL: la pieza más operacional
Flink SQL es la pieza más usable de Flink para data engineers que no son streaming experts. Veamos un ejemplo realista de pipeline RAG:
-- 1. Definir la fuente: topic Kafka con eventos CDC de documentos
CREATE TABLE documents_raw (
doc_id STRING,
title STRING,
body STRING,
category STRING,
updated_at TIMESTAMP_LTZ(3),
PRIMARY KEY (doc_id) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'topic' = 'ecommerce.public.documents',
'properties.bootstrap.servers' = 'kafka:9092',
'key.format' = 'avro-confluent',
'value.format' = 'avro-confluent',
'value.fields-include' = 'EXCEPT_KEY'
);
-- 2. Definir el sink: vector store via Kafka topic intermedio
CREATE TABLE documents_embedded (
doc_id STRING,
chunk_id INT,
title STRING,
chunk_text STRING,
category STRING,
embedding ARRAY<FLOAT>,
embedded_at TIMESTAMP_LTZ(3),
PRIMARY KEY (doc_id, chunk_id) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'topic' = 'rag.documents.embedded',
'properties.bootstrap.servers' = 'kafka:9092',
'key.format' = 'json',
'value.format' = 'json'
);
-- 3. UDF para chunking (definida en Python o Java)
-- CREATE TEMPORARY FUNCTION chunk_text AS 'com.example.ChunkingUDF';
-- 4. Pipeline: chunkear, embedder, escribir al sink
INSERT INTO documents_embedded
SELECT
doc_id,
chunk_idx AS chunk_id,
title,
chunk AS chunk_text,
category,
OPENAI_EMBEDDING(chunk,
'text-embedding-3-small') AS embedding,
CURRENT_TIMESTAMP AS embedded_at
FROM documents_raw
CROSS JOIN UNNEST(chunk_text(body, 512, 64))
WITH ORDINALITY AS t(chunk, chunk_idx);
Lo que pasa aquí, línea a línea:
- La tabla
documents_rawlee el topic CDC en modo upsert-kafka (cada nuevo evento por la misma key reemplaza el anterior). Esto refleja correctamente la semántica “esta es la última versión del doc 42”. - La tabla
documents_embeddedserá el topic intermedio donde Flink escribe los chunks embedded. - La UDF
chunk_text(definida en Python o Java) divide cada doc en chunks de 512 tokens con overlap de 64. - La consulta
INSERT INTOse ejecuta continuamente: cada evento nuevo endocuments_rawse chunkea, cada chunk se embedea conOPENAI_EMBEDDING(función built-in de Flink SQL en Confluent Cloud 2026), y se escribe al topic embedded.
OPENAI_EMBEDDING puede sustituirse por una función custom que llame a un modelo self-hosted (vLLM con un encoder), a SentenceTransformers, o a un servicio managed. La sintaxis es la misma; cambias el provider.
Watermarks y late events
Para casos donde un evento puede llegar tarde (eg el WAL de Postgres se retrasa porque hubo un network blip), Flink permite definir watermarks:
CREATE TABLE documents_raw (
doc_id STRING,
title STRING,
body STRING,
updated_at TIMESTAMP_LTZ(3),
WATERMARK FOR updated_at AS updated_at - INTERVAL '5' MINUTE
) WITH (...)
Esto le dice a Flink “asume que ningún evento llega más de 5 minutos tarde respecto al timestamp del evento”. Para joins y agregaciones temporales, Flink usa el watermark para decidir cuándo “cerrar” una ventana.
Capa 4 — Sinks a vector stores
El último paso es indexar los embeddings en un vector store. Tres patrones en 2026:
Patrón A — Kafka Connect sink directo
Cada vector store tiene su connector oficial:
- Milvus: sink connector oficial de Zilliz. Soporta named/unnamed dense/sparse vectors.
- Qdrant: sink connector oficial. Soporta dense, sparse, multi-vector.
- pgvector: no tiene connector dedicado, pero se usa el JDBC Sink Connector con SQL custom.
- Weaviate: connector community.
- LanceDB: connector community.
Ejemplo de configuración Milvus sink:
{
"name": "milvus-rag-embeddings-sink",
"config": {
"connector.class": "com.milvus.io.kafka.MilvusSinkConnector",
"tasks.max": "3",
"topics": "rag.documents.embedded",
"milvus.host": "milvus.prod.internal",
"milvus.port": "19530",
"milvus.collection.name": "documents",
"milvus.collection.dim": "1536",
"milvus.collection.partition": "default",
"key.converter": "org.apache.kafka.connect.storage.StringConverter",
"value.converter": "org.apache.kafka.connect.json.JsonConverter",
"value.converter.schemas.enable": false
}
}
Tres tasks en paralelo (tasks.max: 3) consumen el topic embedded y escriben a la colección Milvus. La latencia desde “evento en Kafka” hasta “vector indexable en Milvus” es típicamente <5 segundos.
Patrón B — pgvector con CDC pipe directo
Para equipos que ya viven en PostgreSQL, pgvector es la opción de menor fricción. Patrón: el mismo cluster Postgres origen tiene una segunda DB para embeddings con extensión pgvector activada; el pipeline Flink escribe directamente vía JDBC.
-- En el cluster Postgres con pgvector activado
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE document_embeddings (
doc_id TEXT,
chunk_id INT,
chunk_text TEXT,
category TEXT,
embedding vector(1536),
embedded_at TIMESTAMP,
PRIMARY KEY (doc_id, chunk_id)
);
CREATE INDEX ON document_embeddings
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
Ventajas: tu mismo DBA opera todo, transactionality cross-tables, joins con metadatos relacionales triviales. Limitación: a >10M vectores, el rendimiento de pgvector empieza a ceder respecto a sistemas dedicados.
Patrón C — Confluent Tableflow → Iceberg + vector search Flink SQL
Esta es la novedad 2026 que cambia la mecánica. Confluent Tableflow materializa automáticamente topics Kafka como tablas Apache Iceberg o Delta Lake. Características:
- Sin pipeline ETL: no escribes Flink/Spark jobs para mover Kafka a tabla. Lo hace Tableflow.
- Schema evolution automática: cambios en el schema del topic se reflejan en la tabla.
- Catálogo unificado: la tabla aparece en Glue, Unity Catalog, Snowflake, Databricks. Cualquier motor analítico la consulta sin copiar datos.
- CDC nativo: maneja inserts, updates, deletes correctamente.
- 30-50% menos TCO según las cifras que Confluent publica vs pipelines tradicionales.
Y desde 2026, Tableflow + Flink SQL ofrecen vector search nativo integrado con Cosmos DB y Amazon S3 Vectors. La consulta RAG se puede hacer directamente en Flink SQL:
SELECT doc_id, chunk_text, category
FROM documents_embedded
WHERE VECTOR_SEARCH(embedding,
OPENAI_EMBEDDING('query del usuario', 'text-embedding-3-small'),
top_k => 10) > 0.7
ORDER BY VECTOR_SEARCH_SCORE DESC;
Esto unifica capas que antes eran separadas (vector store + analytics). Para muchos casos, elimina la necesidad de mantener un vector store dedicado.
El MCP server oficial de Confluent
Una pieza añadida en 2026 que merece mención: Confluent ha publicado un MCP server oficial que expone Kafka, Flink y Tableflow como tools accesibles a agentes IA vía MCP. Cualquier MCP client (Claude Desktop, Cursor, agentes propios) puede:
- Listar topics, leer mensajes recientes, publicar a topics.
- Ejecutar queries Flink SQL en lenguaje natural (“dame las órdenes de las últimas 24 horas con valor > 1000€”).
- Consultar tablas Tableflow Iceberg.
- Gestionar conectores Kafka Connect.
Esto cierra el círculo: tu agente IA, además de leer datos del datalake vía RAG (con vector search), puede escribir datos al log (vía MCP) y disparar transformaciones (vía Flink SQL en natural language). Es el punto de fusión más profundo entre LLM ops y data ops del año.
Conexión con la serie anterior: este MCP server emite traces con las OpenTelemetry GenAI MCP semantic conventions que cubrimos en el post de MCP observability. Los spans aparecen en Langfuse, Phoenix o tu OTel backend con la cardinalidad correcta. Cero código de instrumentación.
Vector stores: comparativa 2026
Las cinco opciones dominantes:
| Vector store | Licencia | Operación | Cuándo encaja |
|---|---|---|---|
| pgvector | Postgres ext, OSS | Tu DBA | <10M vectores, equipo Postgres-heavy |
| Qdrant | Apache 2.0 | Self-host o managed | Mid-scale, foco performance |
| Milvus | Apache 2.0 | Self-host o Zilliz Cloud | Large-scale, foco escalabilidad |
| Weaviate | BSD-3 | Self-host o managed | Hybrid search nativo, semantic rich |
| LanceDB | Apache 2.0 | Embedded o serverless | Small-medium, simplicidad |
La selección depende de:
- Escala: pgvector se queda corto >10M vectores. Milvus y Qdrant escalan a billones.
- Hybrid search: Weaviate trae lexical + vector nativo. Otros lo soportan pero menos integrado.
- Operación: pgvector si ya tienes Postgres operado. Qdrant si quieres simplicidad. Milvus si necesitas máxima escala.
- Cloud managed: Zilliz Cloud para Milvus, Qdrant Cloud para Qdrant, Pinecone si quieres SaaS puro (sin OSS detrás).
Freshness vs accuracy: el trade-off operativo
Una decisión crítica que cualquier sistema RAG sobre Kafka debe responder: ¿cuándo se considera que un nuevo documento está “live” en el índice?
Tres opciones:
Streaming síncrono: el evento llega a Kafka, Flink lo embedea, el sink lo escribe al vector store, y solo entonces se considera live. Latencia típica: 1-5 segundos. La mejor freshness. Pero si el embedding model falla o el vector store es lento, los eventos se acumulan en el topic.
Streaming asíncrono con baseline: el evento se considera live inmediatamente; un proceso de fondo lo embedea cuando puede. Mientras tanto, queries que pidan ese documento no lo encuentran. Latencia típica: 5-60 segundos. Aceptable para la mayoría de aplicaciones.
Batch micro: se procesa en mini-batches cada 1-5 minutos. Menos eficiente que streaming continuo pero más estable bajo carga variable. Latencia: 1-5 minutos.
La decisión depende del SLA del producto. Para chatbots de soporte al cliente, 5-60 segundos es aceptable. Para sistemas que reaccionan a eventos críticos (precios financieros, alarmas), streaming síncrono es necesario.
Schema evolution y reembedding
Cuando el embedding model cambia (cambias de text-embedding-3-small a text-embedding-3-large, o pasas de OpenAI a Cohere), los vectores existentes en el índice son incompatibles: dimensiones distintas, espacios semánticos distintos. La distancia entre un vector viejo y uno nuevo no significa nada.
Patrón estándar para handle de esto: dual-index durante la migración.
- T0: índice activo es V1 (embedding model A).
- T1: empieza pipeline paralelo que escribe a un índice V2 (embedding model B), consumiendo el topic desde offset 0 (reprocesar todo el log).
- T2: V2 ha caught-up al presente.
- T3: cambias el retriever para que use V2.
- T4: una semana después, descartas V1.
El log de Kafka hace este patrón factible porque es inmutable y reproducible. Sin el log, este patrón se vuelve un proyecto de migración de datos de semanas.
Trampas operativas
Topics sin retención adecuada
Configurar topics con retención de 7 días pensando “ya tengo el vector store” lleva a perder la capacidad de reconstruir si el vector store falla. Retención larga (90+ días) o compactada por key para topics que alimentan RAG.
CDC pesado en cargas pico
Debezium leyendo el WAL en horas pico puede impactar performance de la base de datos origen. Replica de lectura dedicada para Debezium, no la primaria de producción. O usar logical replication específica solo para las tablas necesarias.
Embedding cost run-away
OPENAI_EMBEDDING en cada evento de un topic con millones de mensajes/día son miles de USD/mes. Estrategias: filtrar antes de embedder (solo embedder lo que aporta valor); deduplicar por hash de contenido; usar embedding models open-source self-hosted (BGE, E5, GTE) cuando el coste cloud sea prohibitivo.
Reembedding lento por throughput limitado
Recalcular 10M embeddings con OpenAI API a 3000 req/min tarda 55 horas. Si esperas a un incidente para reembeder, son dos días sin servicio. Embedding throughput es un capacity planning explícito; reservar capacity o tener un job offline pre-arrancable.
Schema breaks aguas abajo
Un cambio en el schema del topic raw rompe Flink jobs aguas abajo. Schema Registry con compatibility BACKWARD obligatoria; nunca ALLOW_ALL. Y test schema evolution en CI.
Vector store sin backup
Tu vector store tiene 50M vectores. Es la única copia (los topics expiraron). Un fallo lo borra. Vector stores deben ser backed up igual que cualquier persistencia primaria. Para Milvus/Qdrant: snapshots periódicos. Para pgvector: el propio pg_dump.
Multi-region sin replicación cross-cluster
Tu RAG sirve a usuarios en US y EU. El vector store está en US-east. Latencia desde EU = 100ms+ por query. MirrorMaker o Cluster Linking para replicar topics y vector stores en ambas regiones.
Lo que no hemos cubierto
- Hybrid search en producción: combinar BM25/lexical + vector + reranker. Tema de su propio post.
- Multimodal RAG: indexar imágenes, audio, vídeo además de texto. Embeddings multimodales (CLIP, Imagebind), arquitectura específica.
- GraphRAG: usar conocimiento estructurado (knowledge graphs) además de vector retrieval. Microsoft GraphRAG, LlamaIndex KnowledgeGraphQueryEngine.
- RAG con ACL multi-tenant: filtrar por permisos en runtime. Patrón con metadatos en el vector store + filtros server-side.
- Query rewriting con LLM: usar un primer LLM para expandir la query antes del retrieval (HyDE, multi-query, step-back prompting).
Referencias
Kafka y stream processing:
- Apache Kafka y Debezium.
- Confluent Schema Registry y Apicurio Registry.
- Apache Flink y Flink SQL docs.
- RisingWave — alternativa SQL streaming con embedding built-in.
Vector store connectors:
- Milvus Sink Connector (Zilliz, GitHub).
- Connect Apache Kafka with Milvus (docs).
- Qdrant Kafka Sink (GitHub).
- Vector Database Benchmarks 2026.
- Streaming to Vector Databases (Streamkap).
Tableflow y arquitectura 2026:
- Tableflow — Confluent.
- Tableflow GA: Real-Time Kafka to Iceberg (Confluent Blog).
- Tableflow + Databricks Unity Catalog (Confluent Blog).
- Better-Governed Data Lake Architectures with Tableflow (Confluent Blog).
- Top Trends for Data Streaming with Kafka and Flink in 2026 (Kai Waehner).
RAG streaming:
- RAG Architecture in 2026: How to Keep Retrieval Actually Fresh (RisingWave).
- Streaming CDC Events to Vector Databases (Streamkap).
- Apache Kafka + Vector Database + LLM = Real-Time GenAI (Kai Waehner).
- From Static to Dynamic: A Streaming RAG Approach (arxiv 2508.05662).
- How to generate vector embeddings for RAG with Flink SQL (Confluent Developer).
- Event-Driven Architectures for AI Pipelines (dasroot).
Cross-references:
- Post anterior: MLOps específico para LLMs en 2026: el panorama.
- Serie post-tracing: Evals, Guardrails, MCP observability, eBPF + drift.
- Serie eBPF: eBPF de cero a Cilium, Tetragon, Hubble, AgentSight.