¿Cuántas veces has tenido que bucear en decenas de páginas de documentación técnica para encontrar una respuesta que sabes que existe? Entre Read the Docs, wikis internas, READMEs dispersos y manuales de API, la información de tu proyecto está ahí, pero encontrarla rápido es otro deporte.
La buena noticia es que hoy puedes construir un asistente inteligente que entienda tu documentación técnica y responda preguntas en lenguaje natural. Sin depender de APIs caras de OpenAI, sin enviar datos sensibles a la nube, y con resultados sorprendentemente precisos.
Bienvenido al mundo de RAG (Retrieval-Augmented Generation).
¿Qué es RAG y por qué debería importarte?
RAG (Generación Aumentada por Recuperación) es un patrón arquitectónico que combina dos componentes:
- Un sistema de recuperación que busca fragmentos relevantes en tu base de conocimiento.
- Un modelo generativo que construye una respuesta coherente usando esos fragmentos como contexto.
A diferencia de un LLM desnudo que alucina respuestas basadas en su entrenamiento general, un sistema RAG solo responde con información que realmente está en tu documentación. Esto elimina el mayor problema de los modelos de lenguaje: las alucinaciones en dominios específicos.
Para un equipo de desarrollo, esto se traduce en:
- Onboarding acelerado: los nuevos miembros preguntan en lenguaje natural y obtienen respuestas contextualizadas.
- Soporte técnico interno: un canal de Slack o Discord donde el bot responde con la documentación oficial.
- Documentación viva: tu asistente siempre refleja la última versión de tu código y guías.
- Privacidad total: todo corre on-premise o en tu VPC. Ningún dato sale de tu infraestructura.
Arquitectura de un sistema RAG para documentación técnica
La implementación práctica de RAG sigue este flujo:
Documentación (Markdown, HTML, PDF)
↓
[Chunking] → Fragmentos con solapamiento
↓
[Embeddings] → Vectores numéricos (384-1536 dimensiones)
↓
[Vector Store] → Base de datos vectorial (ChromaDB, Qdrant, FAISS)
↓
Pregunta del usuario
↓
[Query Embedding] → Transformar pregunta a vector
↓
[Similarity Search] → Top-K fragmentos más cercanos
↓
[Prompt + Contexto] → Fragmentos recuperados + instrucción
↓
[LLM] → Respuesta generada
Cada etapa tiene decisiones de diseño que afectan directamente la calidad del resultado. Veamos cómo implementarlas en Python.
Implementación paso a paso
1. Extracción y chunking de documentos
El primer paso es extraer texto de tu documentación y dividirlo en fragmentos manejables. Un error común es usar fragmentos demasiado grandes (pierde precisión) o demasiado pequeños (pierde contexto).
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=["\n## ", "\n### ", "\n", " ", ""],
)
docs = text_splitter.split_documents(raw_documents)
Regla práctica: 512 caracteres por fragmento con 64 de solapamiento funciona bien para documentación técnica en español. Ajusta según la estructura de tus documentos.
2. Generación de embeddings
Los embeddings convierten texto en vectores numéricos que capturan significado semántico. Para un setup 100% local sin depender de APIs externas:
from sentence_transformers import SentenceTransformer
# Modelo multilingüe ideal para documentación técnica en español
embedding_model = SentenceTransformer("intfloat/multilingual-e5-small")
def embed_text(text: str) -> list[float]:
return embedding_model.encode(f"passage: {text}").tolist()
El modelo multilingual-e5-small (384 dimensiones) ofrece un excelente equilibrio entre calidad y velocidad. Su versión large (1024 dimensiones) da mejor precisión pero requiere más RAM.
3. Almacenamiento vectorial
Para documentación de tamaño mediano (<100 MB), ChromaDB es la opción más simple:
import chromadb
client = chromadb.PersistentClient(path="./vector_store")
collection = client.get_or_create_collection(
name="docs-tecnicas",
metadata={"hnsw:space": "cosine"}
)
# Insertar fragmentos
collection.add(
ids=[f"chunk_{i}" for i in range(len(chunks))],
embeddings=[embed_text(c) for c in chunks],
documents=chunks,
metadatas=[{"source": doc.source, "section": doc.section} for doc in docs]
)
4. Recuperación y generación
El corazón de RAG está en conectar la búsqueda con la generación:
def answer_question(query: str, llm, k: int = 4) -> str:
# 1. Embedear la pregunta
query_vector = embed_text(f"query: {query}")
# 2. Buscar los K fragmentos más relevantes
results = collection.query(
query_embeddings=[query_vector],
n_results=k
)
# 3. Construir el contexto
context = "\n\n---\n\n".join(results["documents"][0])
# 4. Prompt con instrucción explícita
prompt = f"""Eres un asistente técnico especializado en la documentación del proyecto.
Responde SOLO con la información proporcionada en el contexto.
Si la respuesta no está en el contexto, di que no la sabes.
Contexto:
{context}
Pregunta: {query}
Respuesta:"""
# 5. Generar respuesta con el LLM local
return llm.generate(prompt)
Eligiendo el LLM adecuado
Para un asistente de documentación técnica, la prioridad es precisión factual, no creatividad. Estas son las mejores opciones hoy:
| Modelo | Parámetros | RAM | Precisión en hechos |
|---|---|---|---|
| Llama 3.2 (3B) | 3B | 8 GB | Buena — ideal para respuestas cortas |
| Qwen 2.5 (7B) | 7B | 16 GB | Excelente — muy buena comprensión técnica |
| DeepSeek Coder V2 | 16B | 24 GB | Superior — ideal para código y APIs |
| Mistral 7B | 7B | 16 GB | Excelente — buen balance general |
# Ejemplo con Ollama (el más sencillo de configurar)
ollama pull qwen2.5:7b
ollama run qwen2.5:7b
Evaluación de calidad: ¿cómo saber si funciona bien?
No implementes RAG sin medir. Las tres métricas clave son:
- Hit Rate: ¿El fragmento correcto aparece en el Top-5 recuperado? Debería estar sobre 85%.
- MRR (Mean Reciprocal Rank): ¿En qué posición aparece? Ideal >0.7.
- Faithfulness: ¿La respuesta del LLM se limita al contexto recuperado? Mide cuánto alucina.
# Evaluación rápida con tu propio set de preguntas
test_set = [
("¿Cómo configuro la autenticación JWT?", "doc/auth.md"),
("¿Cuál es el rate limit de la API?", "doc/api/limits.md"),
]
hits = 0
for question, expected_doc in test_set:
results = collection.query(query_embeddings=[embed_text(question)], n_results=5)
sources = [m["source"] for m in results["metadatas"][0]]
if expected_doc in sources:
hits += 1
print(f"Hit Rate: {hits/len(test_set):.0%}")
Caso real: asistente de documentación para un proyecto Django
En DojoFullStack implementamos este mismo patrón para la documentación interna de uno de nuestros proyectos Django (200+ páginas de documentación técnica entre modelos, vistas, signals, middlewares y despliegue).
Resultados después de 3 meses de uso:
- Consultas resueltas en 12 segundos promedio (vs 3-5 minutos buscando manualmente).
- 94% de hit rate en preguntas sobre configuración y deployment.
- 0 fugas de datos — todo corre en una VPS de 16 GB RAM con Ollama + ChromaDB.
- Onboarding de nuevos devs reducido de 2 semanas a 4 días.
El secreto estuvo en el chunking: usar la estructura natural de los documentos (separar por ## y ###) en vez de fragmentar por número fijo de caracteres mejoró el hit rate un 18%.
Lleva esto a tu proyecto
Construir un asistente RAG para tu documentación técnica es uno de esos proyectos que parecen complejos pero que en realidad puedes tener funcionando en un par de tardes. No necesitas GPUs costosas ni suscripciones a APIs cloud — con una VPS modesta y modelos open-source obtienes resultados profesionales.
El código completo de este tutorial (con extracción de documentación desde repos, chunking inteligente, y un frontend tipo chat en Streamlit) está disponible para los miembros de la comunidad DojoFullStack.
Sigue explorando estos temas en el blog de DojoFullStack, donde profundizamos en arquitecturas de agentes, embeddings multilingües y optimización de sistemas RAG para producción. El siguiente paso natural es conectar este asistente con una herramienta como LangChain o LlamaIndex para que además de leer documentación, pueda ejecutar comandos, hacer deploys o consultar tu base de datos.
La documentación técnica de tu proyecto es uno de tus activos más valiosos. Dale el motor de búsqueda inteligente que se merece.