PTENES
Saltar al contenido
MÓDULO 3.6

🧠 Embeddings, memoria y RAG básico

Tu modelo local no conoce los tus documentos, pero puede llegar a conocerlos. En este módulo aprendes el trío que le da memoria a la IA local: embeddings (convertir texto en significado), base de datos vectorial (guardar y buscar) y RAG (responder con base en tus archivos). Al final, un RAG completo que funciona en pocas líneas, 100% local.

6
Temas
~40
Minutos
Intermedio
Nivel
Práctico
Tipo

Contenido detallado

1

🔢 Qué son los embeddings

La computadora no entiende palabras: entiende números. Embedding es el truco que convierte texto en números de una forma que preserva el significado: cada frase se convierte en una lista larga de números (un vector), y los textos con significados parecidos quedan cerca unos de otros en un espacio imaginario. "Gato" y "felino" quedan cerca; "gato" y "camión" quedan lejos.

🆕 ¿Eres nuevo por aquí?

"Embedding" (o "vector") es una lista de números, como [0.12, -0.03, 0.88, ...], generalmente con 384 a 1024 posiciones, que representa el significado de un texto. Un "modelo de embeddings" es una IA pequeña, entrenada solo para producir estos vectores (no genera texto, solo «mide el sentido»). "Similitud" = qué tan cerca están dos vectores; cerca = significado parecido.

🔑 La idea central

Los embeddings convierten «buscar texto» en un problema de geometría:

  • •Cada fragmento de texto se convierte en un punto en un espacio de muchas dimensiones.
  • •Los significados parecidos quedan siguientes; los significados distantes quedan lejos.
  • •Buscar «lo que coincide con la pregunta» se convierte en encontrar los puntos más cercanos — no importa si coinciden las palabras exactas.
vecinos de la pregunta gato felino gatito pregunta: "mi mascota" auto moto vehículo ≈ transporte (lejos) pizza banana fruta

Cada palabra se convierte en un punto. La pregunta (en cian) cae cerca del grupo de "animales" y lejos de "transporte" y "comida", así que la búsqueda devuelve gato, felino, minino, aunque la pregunta no use ninguna de esas palabras. Esto es búsqueda por significado, no por coincidencia exacta de palabras.

Texto
se convierte en vector
Sentido
se convierte en distancia
Cercano
= parecido
Buscar
= encontrar vecinos
2

📚 Por qué RAG

El modelo local sabe mucho sobre el mundo — pero no sabe nada de tu mundo: tus PDF, tus notas, la documentación interna de la empresa. Volver a entrenar el modelo con eso es caro y lento. RAG lo resuelve por fuera: al momento de la pregunta, búsqueda los fragmentos adecuados de tus documentos y pega en el prompt, como contexto. El modelo responde leyendo eso, sin volver a entrenarse.

🆕 ¿Eres nuevo por aquí?

RAG = Retrieval-Augmented Generation (generación aumentada por recuperación). En palabras simples: "antes de responder, ve a buscar los fragmentos relevantes y léelos". El modelo sigue siendo el mismo: solo enriqueces el prompt con el material adecuado, en el momento adecuado.

✗ Solo el modelo (sin RAG)

  • ✗No conoce tus documentos privados
  • ✗"Alucina" cuando no sabe: inventa con confianza
  • ✗Conocimiento congelado en la fecha del entrenamiento
  • ✗No hay forma de citar la fuente

✓ Modelo + RAG

  • ✓Responde sobre TUS archivos
  • ✓Menos alucinaciones: responde basándose en el fragmento recuperado
  • ✓Se actualiza al instante: cambiaste el documento, cambió la respuesta
  • ✓Puedes mostrar de dónde vino la respuesta

🆚 RAG no es fine-tuning

Fine-tuning cambia los pesos del modelo (enseña una nueva forma de responder) — caro, lento y difícil de actualizar. RAG no toca el modelo: solo decide qué mostrar en el prompt. Para "responder sobre mis datos", RAG casi siempre es el camino más sencillo, económico y fácil de actualizar.

💡 Consejo práctico

RAG es lo que proporciona memoria de largo plazo para un agente local (Trilha 4): guardas conversaciones, notas y datos como vectores, y el agente «recuerda» recuperando lo que importa, en vez de intentar meterlo todo en la ventana de contexto. Memoria = RAG sobre lo que ya pasó.

Buscar
fragmentos correctos
Aumentar
el prompt
Generar
la respuesta
Sin
volver a entrenar
3

🔧 Anatomía de un RAG

Todo RAG tiene dos mitades: la ingesta (se hace una vez, cuando agregas documentos) y la consulta (se hace con cada pregunta). Entiende el esquema y el resto se convierte en un detalle de código.

🆕 ¿Eres nuevo por aquí?

"Chunk" (fragmento) es una parte del documento: un párrafo, unos 200–500 tokens. Divides el texto en chunks porque (1) el modelo de embeddings tiene un límite de tamaño y (2) quieres recuperar solo el fragmento relevante, no el archivo completo. Un buen chunk es autocontenido (tiene sentido por sí solo) y no es ni demasiado grande ni demasiado pequeño.

INGESTA (una vez) CONSULTA (con cada pregunta) se rompe incrusta guarda incrusta búsqueda de vecinos fragmentos 📄 Documento.txt / .pdf ✂️ Fragmentos(chunks) 🔢 Embeddingsvectores 🗄️ Vector Store búsqueda por similitud ❓ Preguntadel usuario 🔢 Vector de la preguntamismo modelo 🤖 LLM local arma la respuesta ✅ Respuestacon fuentes

Lee en dos líneas: arriba (violeta), la ingesta — documento → fragmentos → vectores → base de datos. Debajo (cian), la consulta — pregunta → vector → busca los vecinos en la base de datos → el LLM local recibe los fragmentos y escribe la respuesta. Se usa el mismo modelo de embeddings en ambos lados.

Los 6 pasos, en orden

1

Chunk — dividir el documento

Divide el texto en fragmentos autocontenidos (p. ej., 1 párrafo). Solo durante la ingestión.

2

Embed — convertir en vector

Pasa cada chunk por el modelo de embeddings; cada uno se convierte en un vector.

3

Store — guardar en la base de datos vectorial

Guarda el vector + el texto original en el vector store. Listo: documento «indexado».

4

Query — generar el embedding de la pregunta

Al hacer la pregunta, crea un embedding de su texto con el mismo modelo.

5

Retrieve — buscar los vecinos

Pídele a la base de datos los N chunks más cercanos al vector de la pregunta.

6

Generate — responder con contexto

Pega los fragmentos en el prompt y pídele al LLM local que responda solo con base en ellos.

Ingesta
chunk·embed·store
Consulta
query·retrieve·gen
El mismo
modelo de embeddings
LLM
solo al final
4

⚡ Ejecutar embeddings localmente

Los modelos de embeddings son pequeños — algunos tienen unos pocos cientos de MB y funcionan sin problemas incluso en hardware económico, sin GPU. El nomic-embed-text es un excelente estándar para empezar: liviano, bueno en portugués y ya empaquetado en Ollama. Vamos a descargarlo y generar el primer vector.

⌨️ Ejemplo práctico: tu primer embedding

Objetivo: descargar un modelo de embeddings y transformar una frase en un vector. Pégalo en la terminal y luego en Python.

# 1) descarga un modelo de embeddings pequeño (~270 MB)
ollama pull nomic-embed-text
# 2a) genera el vector de un texto mediante la API REST de Ollama
curl http://localhost:11434/api/embeddings -d '{
  "model": "nomic-embed-text",
  "prompt": "La IA local se ejecuta en tu máquina"
}'
# 2b) o desde Python, mediante el SDK de OpenAI apuntado al entorno local (módulo 3.5)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
v = client.embeddings.create(model="nomic-embed-text",
                     input="La IA local se ejecuta en tu máquina")
print(len(v.data[0].embedding))  # ej.: 768 → el tamaño del vector

Cómo verificar: o curl devuelve un JSON con una lista larga de números — ese es el vector. En Python, len(...) imprime la dimensión (ej.: 768). Si imprimió un número, tus embeddings se están ejecutando 100% localmente. Guarda ese número: todos los vectores del mismo modelo tienen el mismo tamaño.

💡 Consejo práctico

Usa el mismo modelo de embeddings para indexar los documentos y para generar embeddings de la pregunta. Mezclar modelos (o cambiar de modelo a mitad del proceso) rompe la búsqueda, porque los vectores dejan de vivir en el mismo espacio. ¿Cambiaste de modelo de embeddings? Vuelve a indexar todo.

Pequeño
funciona incluso sin GPU
nomic
buen estándar
768
dimensiones fijas
El mismo
modelo en ambos lados
5

🗄️ Base de datos vectorial simple

Podrías guardar los vectores en una lista y compararlos uno por uno: funciona con 10 chunks, pero se vuelve lento con 100 mil. Un base de datos vectorial existe para eso: almacena los vectores y responde rápido a la pregunta «¿cuáles son los N más parecidos a este?»

🆕 ¿Eres nuevo por aquí?

"Vector store" / "base de datos vectorial" es una base de datos especializada en la búsqueda por similitud entre vectores. Algunas opciones locales comunes: Chroma (más fácil para empezar, guarda en disco con 2 líneas), FAISS (biblioteca de Meta, rapidísima, sin servidor) y sqlite-vec (una extensión que agrega búsqueda vectorial a SQLite, que ya conoces). Todo funciona localmente, sin nube.

🟣 Chroma

Más sencillo para empezar. Persiste en disco por sí solo, API amigable.

Usa cuando: quiere montar un RAG en minutos.

🟣 FAISS

Biblioteca de búsqueda de Meta. Muy rápida, excelente a escala.

Usa cuando: son muchos vectores y la velocidad importa.

🟣 sqlite-vec

Búsqueda vectorial dentro de un archivo SQLite. Sin servidor.

Usa cuando: ya usa SQLite y quiere un solo archivo.

⌨️ Configuración mínima con Chroma

Objetivo: guardar un vector y buscar el más parecido — el corazón del RAG, de forma aislada.

# pip install chromadb
import chromadb

db  = chromadb.PersistentClient(path="./meu_rag")   # guarda em disco
col = db.get_or_create_collection("docs")

# guardar: id + texto + o vetor que veio do nomic-embed-text
col.add(ids=["1"],
        documents=["A senha do wifi do escritório é roxo-1234"],
        embeddings=[[0.12, -0.03, 0.88]])            # <vetor real do passo 4>

# buscar: os 3 mais parecidos com o vetor da pergunta
hits = col.query(query_embeddings=[[0.10, -0.01, 0.90]], n_results=3)
print(hits["documents"])   # → o(s) trecho(s) recuperado(s)

Cómo verificar: ejecútalo dos veces. El PersistentClient guarda en ./meu_rag, así que en la segunda ejecución el documento ya está ahí, sin tener que volver a añadirlo. El query debe devolver el texto que guardaste.

💡 Consejo práctico

Guarda siempre el texto original junto al vector (el campo documents). El vector solo sirve para encontrar; lo que pegas en el prompt es el texto. Una base de datos vectorial sin el texto almacenado es una búsqueda que encuentra el punto correcto, pero no puede mostrarte el contenido.

Guarda
vector + texto
Búsqueda
los N vecinos
Local
en disco, sin nube
Rápido
incluso con muchos
6

🚀 Un RAG mínimo de extremo a extremo

Es hora de unirlo todo. Este script hace el RAG completo: lee un .txt, se divide en chunks, genera embeddings, los guarda y luego responde una pregunta usando solo lo que está en el documento — todo en tu Ollama. Los fragmentos marcados con <...> son lo que intercambias.

⌨️ Ejemplo práctico: RAG completo en ~25 líneas

Objetivo: responder una pregunta cuya respuesta solo existe en tu archivo. Guárdalo como rag.py y ejecuta.

# pip install chromadb openai   (Ollama já rodando)
from openai import OpenAI
import chromadb

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
EMB = "nomic-embed-text"
LLM = "qwen2.5:7b"                       # <troque pelo seu modelo>

def embed(txt):
    return client.embeddings.create(model=EMB, input=txt).data[0].embedding

# 1) INGESTÃO: lê o .txt, quebra em chunks, embeda e guarda
texto  = open("notas.txt").read()        # <seu arquivo .txt>
chunks = [c for c in texto.split("\n\n") if c.strip()]   # 1 parágrafo = 1 chunk
col = chromadb.Client().create_collection("notas")
col.add(ids=[str(i) for i in range(len(chunks))],
        documents=chunks,
        embeddings=[embed(c) for c in chunks])

# 2) CONSULTA: embeda a pergunta, busca os vizinhos, monta o prompt
pergunta = "Qual a senha do wifi do escritorio?"   # <sua pergunta>
viz = col.query(query_embeddings=[embed(pergunta)], n_results=3)["documents"][0]
contexto = "\n".join(viz)

resp = client.chat.completions.create(model=LLM, messages=[
  {"role": "system", "content": "Responda SO com base no contexto. Se nao houver, diga 'nao sei'."},
  {"role": "user",   "content": f"Contexto:\n{contexto}\n\nPergunta: {pergunta}"}
])
print(resp.choices[0].message.content)

Cómo verificar: crea un notas.txt con un hecho que el modelo no habría forma de saberlo — ej.: la línea La contraseña del wifi de la oficina es roxo-1234. Ejecútalo y haz la pregunta anterior: si la respuesta incluye roxo-1234, el RAG funcionó; el modelo solo acertó porque el fragmento venía de tu documento. Ahora pregunta algo que no está en el archivo: debe responder no sé en lugar de inventar.

✓ RAG que funciona

  • ✓Chunks autocontenidos y del tamaño adecuado
  • ✓El mismo modelo de embeddings durante la ingesta y la consulta
  • ✓System prompt indica usar SOLO el contexto
  • ✓Muestra la fuente de cada respuesta

✗ RAG que falla

  • ✗Chunk gigante (documento entero) o demasiado fragmentado
  • ✗Cambiaste el modelo de embeddings y no volviste a indexar
  • ✗Sin la regla de «no inventar»: el modelo alucina
  • ✗Mete demasiado texto en el prompt y excede el contexto

Autoevaluación (opcional): ¿qué hace RAG en la práctica?

Ingiere
.txt → vectores
Búsqueda
vecinos de la pregunta
Responde
solo con el contexto
100%
local

📌 Resumen del módulo

✓
Embeddings = significado en números — los textos parecidos se convierten en puntos cercanos; buscar es encontrar vecinos.
✓
RAG proporciona memoria sin reentrenar — busca los fragmentos adecuados y los pega en el prompt; responde sobre TUS datos.
✓
Anatomía: ingestión + consulta — chunk·embed·store, después query·retrieve·generate, con el mismo modelo de embeddings.
✓
Todo local y en pocas líneas — nomic-embed-text + Chroma + Ollama = un RAG completo, sin nube.

Siguiente:

Ruta 4 — Avanzado y agentes 24/7 · pasas de "ejecutar y servir modelos" a ponerlos a trabajar solos, todo el tiempo.