Dataflow: Base de Conocimiento Local
Dataflow: Base de Conocimiento Local
Sección titulada «Dataflow: Base de Conocimiento Local»Construye una base de conocimiento en tu propia máquina — crea el vector store, luego divide en chunks e
ingiere documentos en él. Este es el complemento de creación de datos del
tutorial RAG: aquí levantas y llenas una KB local; allí
recuperas de ella y generas respuestas. Ambos usan el módulo wippy/embeddings respaldado por
un vector store SQLite local.
Lo que construirás
Sección titulada «Lo que construirás»- Una app local cuya base de datos contiene un vector store de 512 dimensiones.
- La migración que crea la tabla
embeddings_512al arrancar. - Una función de ingesta que divide markdown en chunks y escribe los embeddings en el store.
Requisitos previos
Sección titulada «Requisitos previos»- Un proyecto Wippy (clona app-template, o
wippy init). - Un proveedor LLM con un modelo de embeddings configurado (por ejemplo,
text-embedding-3-small) — consulta Framework LLM. El vector store se crea localmente sin él, pero la ingesta (que llama allm.embed) necesita un proveedor configurado.
Instala las dependencias:
wippy add wippy/embeddingswippy add wippy/migrationwippy add wippy/bootloaderwippy add wippy/llmwippy installCrear el store
Sección titulada «Crear el store»La KB vive en una base de datos SQLite local. wippy/embeddings incluye una migración que
crea la tabla vectorial; el bootloader la ejecuta al arrancar. Conecta las piezas:
version: "1.0"namespace: app
entries: - name: db kind: db.sql.sqlite file: ./data/app.db lifecycle: auto_start: true
- name: processes kind: process.host host: max_processes: 1000 workers: 8
- name: embeddings kind: ns.dependency component: wippy/embeddings parameters: - name: target_db value: app:db
- name: migration kind: ns.dependency component: wippy/migration parameters: - name: app_db value: app:db
- name: bootloader kind: ns.dependency component: wippy/bootloader parameters: - name: application_host value: app:processes - name: app_db value: app:db - name: env_storage value: app.env:storeEl bootloader necesita un store de entorno; añade el estándar en su propio namespace:
version: "1.0"namespace: app.env
entries: - name: file kind: env.storage.file auto_create: true file_path: .env lifecycle: auto_start: true
- name: os kind: env.storage.os lifecycle: auto_start: true
- name: store kind: env.storage.router lifecycle: auto_start: true storages: - app.env:file - app.env:osCrea el directorio de datos e inicia la app:
mkdir -p datawippy runAl arrancar, la migración se ejecuta y el store aparece en data/app.db:
$ sqlite3 data/app.db ".tables"_migrations embeddings_512 embeddings_512_chunksembeddings_512_info embeddings_512_rowids embeddings_512_vector_chunks00...embeddings_512 es una tabla virtual vec0 de SQLite; las tablas sombra embeddings_512_*
contienen sus chunks, row ids y metadatos. (En PostgreSQL la misma migración usa
pgvector en su lugar.)
Ingerir documentos
Sección titulada «Ingerir documentos»La ingesta consta de dos pasos: dividir el texto en chunks con el módulo text, luego escribirlos
con embeddings.add_batch, que embebe y persiste cada chunk.
-- src/ingest.lualocal text = require("text")local embeddings = require("embeddings")
local function ingest(doc_id, title, markdown) local splitter, err = text.splitter.markdown({ chunk_size = 800, chunk_overlap = 100, heading_hierarchy = true, code_blocks = true, }) if err then return nil, err end
local chunks, split_err = splitter:split_text(markdown) if split_err then return nil, split_err end
local batch = {} for i, chunk in ipairs(chunks) do table.insert(batch, { content = chunk, content_type = "doc_chunk", origin_id = doc_id, context_id = tostring(i), meta = { title = title, chunk = i }, }) end
return embeddings.add_batch(batch)end
return { ingest = ingest }Registra la función:
- name: ingest kind: function.lua source: file://ingest.lua method: ingest modules: - text imports: embeddings: wippy.embeddings:embeddingsPuntos clave:
origin_idagrupa todos los chunks de un documento fuente — elimina y reingiere por documento conembedding_repo.delete_by_origin(doc_id).content_typete permite mantener corpus distintos (doc_chunk,faq,code_snippet) en un solo store y filtrar en tiempo de consulta.add_batchdivide automáticamente cuando el batch supera el límite de 8000 tokens por solicitud.
Verificar el contenido
Sección titulada «Verificar el contenido»Una vez ingeridos los documentos, confirma que las filas se guardaron y ejecuta una búsqueda por similitud:
local embeddings = require("embeddings")
local results, err = embeddings.search("how do I configure TLS?", { content_type = "doc_chunk", limit = 5,})-- results[i].content, .similarity, .meta, .origin_id, .context_idA partir de ahí, el tutorial RAG muestra cómo alimentar estos resultados a un LLM para obtener respuestas fundamentadas.
Notas operativas
Sección titulada «Notas operativas»- Tamaño de chunk: 500–1000 tokens es un buen valor por defecto. Usa
chunk_overlap(~10–20 % del tamaño del chunk) para que las frases no se corten a través de los límites. - Dimensiones:
text-embedding-3-smalla 512 dimensiones es rentable y coincide con la tablaembeddings_512. Vectores más grandes significan mayor almacenamiento y búsqueda más lenta. - Local vs. compartido: SQLite (
vec0) mantiene toda la KB en un solo archivo local — ideal para desarrollo y apps de un solo nodo. Apuntatarget_dba undb.sql.postgresconpgvectorpara un store compartido y de producción; el código de ingesta no cambia.
Siguientes Pasos
Sección titulada «Siguientes Pasos»- RAG — recupera de este store y genera respuestas fundamentadas
- Framework LLM —
llm.embed, modelos de embeddings, proveedores - Módulo Text — splitters y tokenización