Contenido detallado
🔢 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.
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.
📚 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ó.
🔧 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.
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
Chunk — dividir el documento
Divide el texto en fragmentos autocontenidos (p. ej., 1 párrafo). Solo durante la ingestión.
Embed — convertir en vector
Pasa cada chunk por el modelo de embeddings; cada uno se convierte en un vector.
Store — guardar en la base de datos vectorial
Guarda el vector + el texto original en el vector store. Listo: documento «indexado».
Query — generar el embedding de la pregunta
Al hacer la pregunta, crea un embedding de su texto con el mismo modelo.
Retrieve — buscar los vecinos
Pídele a la base de datos los N chunks más cercanos al vector de la pregunta.
Generate — responder con contexto
Pega los fragmentos en el prompt y pídele al LLM local que responda solo con base en ellos.
⚡ 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.
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.
🗄️ 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.
🚀 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?
📌 Resumen del módulo
Siguiente:
Ruta 4 — Avanzado y agentes 24/7 · pasas de "ejecutar y servir modelos" a ponerlos a trabajar solos, todo el tiempo.