// Proyecto · RAG Agéntico · Qdrant · LangGraph
Repo Expert responde preguntas sobre cualquier repositorio GitHub con citas inline que apuntan al documento, fragmento y fuente exacta. Tres fuentes de conocimiento heterogéneas, agente con lógica de fallback y dos instancias configurables desde config. Evaluado con métricas reales, no prometidas: 100% routing y 100% de relevancia hit@6 en la instancia que puedes probar en el chat.
// Evaluación del sistema
El problema
Fuentes heterogéneas sin integrar. Un repo tiene documentación, código fuente e issues activos. Un sistema RAG que indexa solo una de ellas responde peor que un developer con grep bien entrenado.
Sin routing de fuentes. Preguntar "¿dónde está el bug del issue #42?" no es lo mismo que "¿qué hace la función parse_config?". Sin routing inteligente, el sistema mezcla fuentes irrelevantes con las pertinentes.
Respuestas sin trazabilidad. Un LLM que responde sin citar fuentes da la misma confianza que uno que alucina. Si no puedes verificar la respuesta, no sirve para trabajo real.
Instancias hardcodeadas. La mayoría de sistemas RAG son one-repo. Cambiar de repositorio implica reescribir código. Un sistema configurable desde config escala sin rozamiento.
PASO 01
Docs, código fuente e issues (instancia pública) o Career KB (instancia portfolio) se indexan en Qdrant Cloud por separado. El markdown se parte por secciones — con el encabezado repetido en cada trozo — y nunca por encima de la ventana del modelo de embeddings, que trunca en silencio.
PASO 02
El agente LangGraph decide qué fuentes consultar. Los huecos se reparten entre colecciones según lo bien que encaja cada una con la pregunta, no a partes iguales: el RRF clásico ordena dentro de cada colección y acababa dando una cuota fija a cada una.
PASO 03
El LLM (gpt-5-mini) genera la respuesta citando cada afirmación con el fragmento, documento y posición exacta de origen. Las respuestas son verificables, no solo plausibles.
PASO 04
Si el retrieval no devuelve resultados suficientes, el agente reintenta con una búsqueda más amplia antes de admitir que no sabe. Un juez LLM independiente verifica la respuesta contra las fuentes antes de devolverla.
Qdrant para vector search, LangGraph para la lógica agéntica, FastAPI como backend. Desplegado en Azure Container Apps con escalado a cero: coste ~$0–1/mes.
Tecnologías
El agente no solo recupera y genera — decide, reintenta y reconoce sus límites. Lógica de autoevaluación que distingue "no hay suficiente información" de "no sé contestar".
Docs, código e issues indexados por separado con estrategias propias. RRF combina los rankings en inferencia sin perder especificidad de cada fuente.
Instancia portfolio — la que responde en el chat — evaluada en 2026-09 sobre gpt-5-mini: 100% de acierto en routing, 100% de relevancia hit@6, y 100% de respuestas que un juez LLM independiente da por totalmente fundamentadas. Los fallos restantes son de recuperación en entradas largas, no invenciones: el agente responde “no lo sé” antes que rellenar huecos.
Una instancia pública (repo open-source + issues GitHub) y una portfolio (Career KB). El mismo código, dos contextos completamente diferentes, sin tocar lógica del agente.
Lo que se rompió — y cómo lo arreglé
Cinco fallos reales en producción, con el diagnóstico que los aisló y el arreglo que los cerró. Puedes comprobar el resultado tú mismo en el chat.
El modelo de embeddings era solo inglés.
Síntoma: "¿Qué proyectos ha construido Jorge?" devolvía plantillas de prompts de otros repos.
Diagnóstico: mismo índice, misma fusión, cambiando solo el idioma de la pregunta — en inglés devolvía 6/6 fragmentos de carrera correctos; en español, 6/6 irrelevantes. El corpus está en inglés y el chat se usa en español.
Arreglo: cambio a multilingual-e5-small, multilingüe y de la misma dimensión (384), con los prefijos query:/passage: que ese modelo espera. Solo hubo que recalcular los vectores, no migrar el esquema. Recuperación de carrera: 0.6 → 1.0.
La fusión era una cuota disfrazada de ranking.
Síntoma: toda pregunta devolvía exactamente 2 fragmentos de cada colección.
Diagnóstico: RRF puntúa por el puesto dentro de cada colección, así que todos los primeros empataban y la mezcla era un round-robin. Una pregunta sobre la carrera gastaba cuatro de seis huecos en código de repositorios.
Arreglo: reparto proporcional de huecos según lo bien que encaja cada colección, medido contra el rango de puntuaciones de esa consulta — necesario porque las similitudes del nuevo modelo se agrupan en una banda estrecha (~0.78–0.88) donde los cocientes en bruto no dicen nada. Una colección sin nada relevante ahora no ocupa ningún hueco.
El 55% de los fragmentos de carrera se truncaban sin avisar.
Síntoma: datos que existían en el documento no aparecían nunca.
Diagnóstico: 12 de 22 secciones superaban la ventana del modelo (la mayor, ~4×), y el truncado es silencioso: se indexaba solo hasta el corte.
Arreglo: las secciones largas se parten por frases — nunca a mitad de una — repitiendo el encabezado en cada trozo para que el embedding siga en tema y la cita conserve contexto. 22 → 56 fragmentos, el mayor de 4.952 a 899 caracteres, ninguno por encima del límite.
El 40% del corpus era ruido interno.
Síntoma: una pregunta sobre proyectos de ML respondía citando una oferta de empleo ficticia.
Diagnóstico: 1.003 fragmentos de ficheros de tareas y 161 de plantillas de prompts de otros repos, que competían con la documentación real.
Arreglo: excluidos del índice. El patrón de exclusión además no funcionaba en la raíz del repositorio — fnmatch deja que * cruce /, así que **/tasks/** exigía una barra antes de tasks — y se corrigió en el comparador, con tests. Docs: 2.885 → 1.691 fragmentos.
Y una trampa de medición, que casi me hace "arreglar" lo que no estaba roto.
Síntoma: tras ampliar el contexto del generador, la fidelidad cayó de 0.7 a 0.1.
Diagnóstico: el juez recupera su propia evidencia y seguía haciéndolo con 5 fragmentos recortados a 700 caracteres, mientras el generador ya usaba 12 de 2.400. Marcaba como no fundamentadas respuestas correctas porque el texto que las sostenía estaba fuera de su vista.
Arreglo: el juez ve ahora la misma amplitud que el generador. Documentado como arreglo de medición y no de calidad, para que nadie lea esa subida como una mejora del sistema.
¿Prefieres comprobarlo tú mismo?
Habla con mis repos →Relevante si...
Menos relevante si...
Proyectos relacionados
Multi-asistente con un índice por asistente, búsqueda híbrida y fallback honesto. 56 tests, construida en 7 días.
Ver caso completo → Claude Code · MCP · ASTLos seis puntos de extensión de Claude Code aplicados a un monorepo real, con suite de validación.
Ver caso completo →Contacto
30 minutos. Sin compromiso. Si veo que puedo ayudarte, te lo digo. Si no, también.
Agendar videollamada →