Laboratorio · en producción
Esta web es mi demo
El asistente de soytrillos.com no es un chatbot genérico: es un sistema RAG completo. Un pipeline de datos en Python prepara y valida mis contenidos, genera embeddings y alimenta a un agente que busca, usa herramientas y cita sus fuentes. El mismo conocimiento está disponible para cualquier IA a través de un servidor MCP.
Arquitectura
Contenido
Markdown y MDX: perfil, servicios y blog
Pipeline Python
Extrae, limpia, fragmenta, valida y genera embeddings
Índice vectorial
30 fragmentos · 384 dimensiones
Agente RAG
Búsqueda híbrida + herramientas + Gemma 4
01 · Datos
Pipeline de ingesta en Python
Antes de que la IA vea un solo dato, el pipeline lo valida. Si encuentra un error de calidad (títulos duplicados, documentos vacíos o un email que no debería publicarse), se detiene y no genera el índice. Se ejecuta automáticamente en GitHub Actions cada vez que publico contenido, y solo recalcula los embeddings de los fragmentos que cambiaron.
- ›Extract → Markdown y MDX con frontmatter
- ›Clean → texto plano sin sintaxis ni JSX
- ›Chunk → por secciones, con solapamiento
- ›Quality → duplicados, longitudes, datos sensibles
- ›Embed → multilingual-e5-small, por lotes y con reintentos
- ›Load → índice JSON + reporte de calidad
- documentos procesados
- 7
- fragmentos indexados
- 30
- duplicados eliminados
- 0
- errores de calidad
- 0
Reporte real de la última ejecución · intfloat/multilingual-e5-small · 19 de octubre de 2026
def window(text: str, max_chars: int, overlap: int) -> list[str]:
"""Corta textos largos en ventanas, intentando terminar en un párrafo o frase."""
if len(text) <= max_chars:
return [text]
if overlap >= max_chars:
raise ValueError("overlap debe ser menor que max_chars")
parts, start = [], 0
while start < len(text):
end = min(start + max_chars, len(text))
if end < len(text):
cut = max(text.rfind("\n\n", start, end), text.rfind(". ", start, end))
if cut > start + max_chars // 2:
end = cut + 1
parts.append(text[start:end].strip())
if end >= len(text):
break
start = max(end - overlap, start + 1)
return [p for p in parts if p]def load_previous_vectors(index_path: Path, model: str) -> dict[str, list[float]]:
"""Carga incremental: reutiliza embeddings de fragmentos cuyo texto no cambió."""
if not index_path.exists():
return {}
try:
previous = json.loads(index_path.read_text(encoding="utf-8"))
except json.JSONDecodeError:
log.warning("Índice anterior ilegible: se regenerará completo")
return {}
if previous.get("model") != model:
log.info("Cambió el modelo de embeddings: se regenerará completo")
return {}
return {c["hash"]: c["embedding"] for c in previous.get("chunks", []) if c.get("embedding")}02 · RAG y agente
Búsqueda híbrida y herramientas
Embeddings + BM25
La búsqueda vectorial entiende el significado; BM25 no pierde términos exactos como «ETL». Reciprocal Rank Fusion combina ambos rankings.
Agente con tool calling
Gemma 4 decide si necesita más contexto, listar artículos o dar el contacto. El servidor ejecuta la herramienta y le devuelve el resultado.
Respuestas verificables
Cada respuesta muestra sus fuentes y los pasos que siguió. Si la información no está en mis datos, lo dice en lugar de inventar.
Pruébalo: haz una pregunta y despliega «Cómo respondí» bajo la respuesta.
03 · MCP
Conecta tu IA a mi servidor MCP
El Model Context Protocol permite que asistentes como Claude o Cursor usen herramientas externas de forma estándar. Mi servidor es público y de solo lectura: tu IA puede buscar en mis contenidos, leer mis artículos y obtener mis datos de contacto.
buscar_conocimientobúsqueda híbrida con scoreslistar_articulosartículos del blog por categoríaleer_articulotexto completo de un artículocontactoemail, agenda y redes
Endpoint: https://soytrillos.com/api/mcp
Claude Code
claude mcp add --transport http soytrillos https://soytrillos.com/api/mcpClaude Desktop, Cursor y otros clientes (mcp.json)
{
"mcpServers": {
"soytrillos": { "url": "https://soytrillos.com/api/mcp" }
}
}¿Quieres algo así con los datos de tu empresa?
Asistentes internos, búsqueda sobre tus documentos o un servidor MCP para tus sistemas. Empezamos por ordenar los datos.