# Runbook: Validación Diferenciada de Archivos en Klaus
**Objetivo:** Implementar políticas diferenciadas para indexación de archivos (9K+ archivos, 9.5GB)
**Componentes:** `proxy/file_validator.py`, `scripts/ingest_all_with_validation.py`
**Documentación:** `docs/MATRIZ_POLITICAS_INDEXACION.md`
---
## I. DESCRIPCIÓN GENERAL
Klaus ahora categoriza automáticamente archivos en **18 categorías semánticas** y aplica políticas diferenciadas de:
- **Chunking:** Tamaño, estrategia (AST, heading, semantic sections)
- **Embedding:** Prefijo semántico, modelo
- **Filtrado:** Qué indexar/saltar con razones explícitas
Todo se valida en **UNA PASADA** (single-pass) sin re-lectura de archivos.
---
## II. INSTALACIÓN Y SETUP
### Paso 1: Copiar archivos
Los siguientes archivos ya están en el repositorio:
```
proxy/file_validator.py # Detector de categoría + políticas
scripts/ingest_all_with_validation.py # Ingestor con validación
docs/MATRIZ_POLITICAS_INDEXACION.md # Documentación completa
docs/RUNBOOK_VALIDACION_INDEXACION.md # Este archivo
```
### Paso 2: Verificar importación
En `proxy/knowledge.py`, el ingestor ya importa `is_llm_response()`. Validar que exista:
```python
# en knowledge.py, línea ~161
def is_llm_response(content: str) -> bool:
"""Rechaza chunks que parecen respuestas LLM, no código/docs."""
...
```
### Paso 3: Test rápido (sin ingestión)
```bash
cd /Users/asantacana/proyectos/klaus-proxy-global
# Test del validador en modo reporte (no ingestión)
python scripts/ingest_all_with_validation.py --report-only
# Salida esperada:
# - Reporte en ingest_validation_report.json
# - Logs mostrando categorización de archivos
```
---
## III. FLUJO DE VALIDACIÓN (UNA PASADA)
### Fase 1: Descubrimiento de archivos
```
discover_files(root_path)
├─ Itera sobre directorios
├─ Salta: .git, node_modules, __pycache__, .venv, dist, build, tests, etc.
└─ Retorna: lista ordenada de archivos
```
### Fase 2: Categorización (extension + heurísticos)
Para **cada archivo**:
```python
category = detect_file_category(file_path, content_sample)
# Retorna: FileCategory enum (18 opciones)
```
Orden de decisión:
1. **Extensión** (rápido, .py → CODE_PYTHON)
2. **Directorio** (vendored → skip, tests → test fixtures)
3. **Nombre archivo** (.log, .sample → special handling)
4. **Contenido** (JSON structure, YAML colons, etc.) — fallback
### Fase 3: Aplicación de política
```python
should_index, reason = should_index_file(
file_path,
category,
content, # Contenido completo para LLM response check
max_size_mb=50
)
# Retorna: (bool, reason_string)
```
Decisiones por categoría:
| Categoría | Indexar | Razón |
|---|---|---|
| CODE_* | ✓ SÍ | High semantic value |
| DOCUMENTATION | ✓ SÍ | Context critical |
| CONFIG_STRUCTURED | ✓ SÍ (filtrado) | Schema patterns, pero skip .sample |
| CONFIG_UNSTRUCTURED | ❌ NO | Noise |
| MARKUP | ❌ NO | Tag noise |
| STYLES | ❌ NO | Low semantics |
| DATA_TABULAR | ❌ NO | Bloat (>1MB) |
| BINARY_* | ❌ NO | Non-textual |
| LOGS | ❌ NO | Volatile |
| TEST_FIXTURES | ❌ NO | Mock data noise |
| VENDORED | ❌ NO | External code |
| BUILD_ARTIFACTS | ❌ NO | Compiled binaries |
### Fase 4: Resolución de política de chunking
```python
policy = ChunkingPolicy.for_category(category)
# Retorna: {strategy, chunk_size, overlap, extract_symbols, ...}
```
Ejemplos:
```python
# CODE_PYTHON
policy = ChunkingPolicy(
strategy='ast', # AST parsing
chunk_size=500, # Max per def
overlap=0, # No overlap (boundaries)
extract_symbols=True, # Get func/class names
)
# DOCUMENTATION
policy = ChunkingPolicy(
strategy='heading', # Split by ## headings
chunk_size=800, # Per subsection
overlap=120, # Preserve context
extract_symbols=True, # Get heading as symbol
)
# CONFIG_STRUCTURED
policy = ChunkingPolicy(
strategy='semantic_section', # Split by top-level keys
chunk_size=1000,
overlap=0,
extract_symbols=True, # Get key path (db.connection.host)
)
```
---
## IV. USO: TRES MODOS
### Modo 1: Validación Solamente (Reporte)
**Genera reporte SIN ingestión:**
```bash
python scripts/ingest_all_with_validation.py --report-only
```
**Salida:**
```
File Validation Stats
Total: 1234
Indexed: 1100 (89.1%)
Skipped: 134 (10.9%)
By Category:
code_python 234
documentation 89
config_structured 234
test_fixtures 15
vendored 100
binary_images 50
...
Skip Reasons (top 10):
category_vendored_skip 100
category_test_fixtures_skip 50
config_sample_skip 20
category_data_tabular_skip 15
...
```
**Archivo generado:** `ingest_validation_report.json`
```json
{
"validation": {
"total_files": 1234,
"indexed": 1100,
"skipped": 134,
"indexation_rate": 0.891,
"by_category": {...},
"by_skip_reason": {...}
}
}
```
### Modo 2: Ingestión Proxy Solamente (Debug)
**Ingesta solo `proxy/` para testing rápido:**
```bash
python scripts/ingest_all_with_validation.py --proxy-only
```
**Flujo:**
1. Descubre ~50-100 archivos en proxy/
2. Valida cada uno con políticas
3. Ingestiona archivos aprobados
4. Reporta: chunks_stored, chunks_skipped, chunks_failed
### Modo 3: Ingestión Full (Producción)
**Opción A: Flush + Re-ingestión (limpia el KB primero)**
```bash
python scripts/ingest_all_with_validation.py --flush
```
**Flujo:**
1. Elimina todos los puntos en Qdrant
2. Descubre ~9K archivos
3. Valida cada uno
4. Ingestiona en paralelo (rate-limited, 0.2s entre archivos)
**Opción B: Ingestión incremental (agrega a lo existente)**
```bash
python scripts/ingest_all_with_validation.py
```
---
## V. INTERPRETACIÓN DE RESULTADOS
### Reporte: Total vs. Indexation Rate
```
Total: 1234 files
Indexed: 1100 (89.1%)
Skipped: 134 (10.9%)
```
**Análisis:**
- **89% es bueno** para proyectos reales (contienen binarios, tests, etc.)
- **Si < 75%:** Revisar `by_skip_reason` (posible over-filtering)
- **Si > 95%:** Posible bajo-filtering (incluir noise)
### Skip Reasons: Red Flags
**Normal/Esperado:**
- `category_vendored_skip` — node_modules, vendor/
- `category_test_fixtures_skip` — /tests/fixtures/
- `category_binary_images_skip` — .png, .jpg
- `category_generated_logs_skip` — .log
**Inesperado (revisar):**
- `category_config_structured_skip` — Muchos (revisar si hay .sample templates)
- `llm_response_contamination` — Muy alto (revisar si docs generadas indexadas)
- `config_sample_skip` — Múltiples (normal si hay template configs)
### By Category: Distribución esperada
Para un proyecto típico (9K files):
```
code_python ~219 (2%) — Small but critical
code_typescript ~54 (0.6%) — High value
code_javascript ~54 (0.6%)
code_shell ~75 (0.8%)
code_other ~60 (0.6%)
documentation ~742 (8%) — Important
config_structured ~2854 (30%) — Large, filtered
config_unstructured ~50 (0.5%) — Skipped
markup ~52 (0.5%) — Skipped
styles ~54+ (0.6%) — Skipped
test_fixtures ~300 (3%) — Skipped
vendored ~3500 (37%) — Skipped
binary_images ~130 (1.4%) — Skipped
generated_logs ~100+ (1%) — Skipped
build_artifacts ~50+ (0.5%) — Skipped
---
TOTAL INDEXED: ~4000 (43%)
TOTAL SKIPPED: ~5300 (57%)
```
**Validar:**
- Code + docs + config_structured = indexed files (should be ~4K)
- Everything else = skipped (should be ~5K)
---
## VI. CUSTOMIZACIÓN DE POLÍTICAS
### Cambiar umbral de tamaño máximo
**Actual:** 50 MB por archivo
```python
# En scripts/ingest_all_with_validation.py, función validate_file():
should_index, reason = should_index_file(
rel_path,
category,
content,
max_size_mb=50 # ← CAMBIAR AQUÍ
)
```
Recomendaciones:
- **< 5MB:** Estricto (solo contenido crítico)
- **5-50MB:** Balanceado (actual)
- **> 50MB:** Permisivo (pero cuidado con bloat)
### Cambiar categoría de skip a index
Ejemplo: Si quieres indexar CSS para búsqueda de estilos:
```python
# En proxy/file_validator.py, función should_index_file():
if category == FileCategory.STYLES:
# Cambiar de:
# return False, "category_styles_skip"
# A:
return True, "category_styles_index"
# Y ajustar política de chunking:
FileCategory.STYLES: ChunkingPolicy(
strategy='rule_group',
chunk_size=200, # Conservador (pocos caracteres por regla)
overlap=30,
min_chunk_len=50, # Requiere reglas no-vacías
extract_symbols=True, # Extraer selectores CSS
preserve_structure=True,
)
```
### Agregar nueva categoría
Ejemplo: Indexar Dockerfiles
```python
# 1. En proxy/file_validator.py:
class FileCategory(enum.Enum):
# Agregar:
CODE_DOCKERFILE = "code_dockerfile"
# 2. En detect_file_category():
if ext == 'dockerfile' or 'dockerfile' in path_lower:
return FileCategory.CODE_DOCKERFILE
# 3. En should_index_file():
if category == FileCategory.CODE_DOCKERFILE:
return True, "category_code_dockerfile_index"
# 4. En ChunkingPolicy.for_category():
FileCategory.CODE_DOCKERFILE: ChunkingPolicy(
strategy='fixed',
chunk_size=400,
overlap=50,
min_chunk_len=40,
extract_symbols=True, # Extract FROM, RUN commands
preserve_structure=False,
),
```
---
## VII. DIAGNÓSTICO Y TROUBLESHOOTING
### Problema: Demasiados archivos skipped
**Síntomas:**
```
Indexed: 500 (5%)
Skipped: 9500 (95%)
```
**Causas probables:**
1. Demasiados binarios (normal si hay imágenes, videos)
2. Muchos archivos de configuración template (.sample, .example)
3. Directorio vendored no excluido correctamente
**Solución:**
```bash
# Ver qué está siendo skipped
python scripts/ingest_all_with_validation.py --report-only
# Analizar ingest_validation_report.json:
cat ingest_validation_report.json | jq '.validation.by_skip_reason' | sort -k2 -nr
```
### Problema: Chunks muy grandes o muy pequeños
**Síntomas:**
```
Warning: chunk 0 in file.py is 5000 chars (expected ~500)
Warning: chunk 1 in file.py is 20 chars (expected ~40 min)
```
**Análisis:**
1. Verificar archivo problemático:
```bash
ls -lh <archivo problemático>
```
2. Si muy grande: ajustar chunk_size para esa categoría
3. Si muy pequeño: aumentar min_chunk_len
### Problema: LLM contamination alta
**Síntomas:**
```
Quality issue: llm_response_contamination in docs/FAQ.md
Quality issue: llm_response_contamination in docs/EXAMPLES.md
```
**Causa:**
Documentación generada por LLM indexada (contiene patrones de respuesta LLM)
**Solución:**
1. Revisar archivos marcados en `quality_issues` del reporte
2. Editar archivos para remover patrones LLM (ej: "Aquí está...", "He utilizado...")
3. Re-ingestionar:
```bash
python scripts/ingest_all_with_validation.py --flush
```
### Problema: Búsqueda lenta después de ingestión
**Síntomas:**
- Latencia de búsqueda > 500ms (esperado: < 200ms)
- Muchos chunks sin relevancia
**Análisis:**
```python
# En proxy/knowledge.py, check query latency:
start = time.time()
results = await search(query_vector, query_text, top_k=5)
latency = (time.time() - start) * 1000
log.info(f"Search latency: {latency:.0f}ms")
```
**Soluciones:**
1. Verificar cantidad de puntos en KB:
```bash
curl http://192.168.1.127:8080/knowledge/stats
# Esperado: < 50K puntos para 9K archivos
```
2. Si > 100K puntos: hay duplicados o over-chunking
```bash
# Flush y re-ingest con políticas más estrictas
python scripts/ingest_all_with_validation.py --flush
```
3. Si latencia alta pero puntos normales: indexar es OK
```bash
# Problema es búsqueda, no ingestión (revisa Qdrant config)
```
---
## VIII. MÉTRICAS DE MONITOREO
Después de cada ingestión, validar estas métricas:
```python
# Script para generar dashboard (opcional)
import json
with open('ingest_validation_report.json') as f:
report = json.load(f)
metrics = {
'indexation_rate': report['validation']['indexation_rate'],
'total_files': report['validation']['total_files'],
'indexed_files': report['validation']['indexed'],
'skipped_files': report['validation']['skipped'],
'avg_chunks_per_file': report['ingest']['chunks_stored'] / max(1, report['validation']['indexed']),
'skip_ratio': report['validation']['skipped'] / report['validation']['total_files'],
}
print(f"Indexation Rate: {metrics['indexation_rate']:.1%}")
print(f"Avg Chunks/File: {metrics['avg_chunks_per_file']:.1f}")
print(f"Skip Ratio: {metrics['skip_ratio']:.1%}")
# Alertas
if metrics['indexation_rate'] < 0.75:
print("⚠️ WARNING: Low indexation rate")
if metrics['skip_ratio'] > 0.5:
print("⚠️ WARNING: More than 50% files skipped")
```
---
## IX. INTEGRACIÓN CON CI/CD
### GitHub Actions (ejemplo)
```yaml
name: KB Validation
on:
push:
branches: [main]
paths:
- 'proxy/**'
- 'docs/**'
- 'scripts/**'
- '.github/workflows/kb-validation.yml'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install deps
run: pip install httpx pydantic
- name: Run KB validation
run: python scripts/ingest_all_with_validation.py --report-only
- name: Upload report
uses: actions/upload-artifact@v3
with:
name: kb-validation-report
path: ingest_validation_report.json
```
### Manual trigger (para ingestión)
```bash
# En producción, después de code review + merge:
# 1. Validación solamente (sin riesgo)
python scripts/ingest_all_with_validation.py --report-only
# 2. Revisar reporte
cat ingest_validation_report.json | jq '.validation'
# 3. Si todo OK: ingestión completa
python scripts/ingest_all_with_validation.py --flush
```
---
## X. CASOS DE USO COMUNES
### Case 1: Agregar nuevo proyecto a Klaus
```bash
# Proyecto ubicado en: ~/src/nuevo-proyecto/
# 1. Symlink o copy
ln -s ~/src/nuevo-proyecto ./projects/nuevo-proyecto
# 2. Validar (sin ingestión)
python scripts/ingest_all_with_validation.py --report-only
# 3. Revisar report
# ¿Indexation rate > 75%? ✓
# ¿Skip reasons sensatos (binarios, tests, etc.)? ✓
# 4. Ingestionar
python scripts/ingest_all_with_validation.py
```
### Case 2: Remover ruido (re-clean KB)
```bash
# Síntomas: búsqueda devuelve resultados irrelevantes
# 1. Identificar categorías ruidosas
python scripts/ingest_all_with_validation.py --report-only
# 2. Ajustar should_index_file() para ser más estricto
# Ej: skip CONFIG_UNSTRUCTURED en lugar de indexar
# 3. Flush + re-ingest
python scripts/ingest_all_with_validation.py --flush
```
### Case 3: Aumentar cobertura (indexar más)
```bash
# Síntomas: búsquedas específicas no encuentran respuestas
# 1. Analizar qué categorías se están saltando
python scripts/ingest_all_with_validation.py --report-only
# ¿Muchos CONFIG_UNSTRUCTURED skipped? ¿MARKUP?
# 2. Cambiar should_index_file() para indexar esa categoría
# 3. Ajustar chunking policy si es necesario
# 4. Test incremental (sin flush)
python scripts/ingest_all_with_validation.py --proxy-only
```
---
## XI. REFERENCIA RÁPIDA
### Comandos más comunes
```bash
# Validación solamente (reporte, sin ingestión)
python scripts/ingest_all_with_validation.py --report-only
# Ingestión proxy/ solamente (debug)
python scripts/ingest_all_with_validation.py --proxy-only
# Ingestión full + flush anterior
python scripts/ingest_all_with_validation.py --flush
# Ingestión incremental (agrega a existente)
python scripts/ingest_all_with_validation.py
```
### Inspeccionar reporte
```bash
# Ver por categoría
jq '.validation.by_category | to_entries | sort_by(.value) | reverse' ingest_validation_report.json
# Ver por skip reason
jq '.validation.by_skip_reason | to_entries | sort_by(.value) | reverse' ingest_validation_report.json
# Ver indexation rate
jq '.validation.indexation_rate' ingest_validation_report.json
```
### Check KB health
```bash
# Total de puntos
curl -s http://192.168.1.127:8080/knowledge/stats | jq '.points_count'
# Por categoría (si está implementado)
curl -s http://192.168.1.127:8080/knowledge/stats/by_category | jq '.'
```
---
## XII. CHANGELOG
### Version 1.0 (2026-07-28)
- ✓ Single-pass file categorization (18 categories)
- ✓ Differentiated chunking policies per category
- ✓ Automatic filtering (binaries, logs, test fixtures, vendored)
- ✓ LLM response contamination detection
- ✓ Validation statistics and reporting
- ✓ Integration with existing ingest_all.py
### Planned for v1.1
- [ ] Per-category embedding model selection (code-aware vs general)
- [ ] Dynamic threshold adjustment based on category
- [ ] Dashboard for KB health metrics
- [ ] Automated re-ingest on quality issues
---
## SOPORTE
Si encuentras problemas:
1. **Revisar documentación principal:** `docs/MATRIZ_POLITICAS_INDEXACION.md`
2. **Inspeccionar reporte:** `ingest_validation_report.json`
3. **Ejecutar con debug:**
```python
# En proxy/file_validator.py, línea ~100:
log.setLevel(logging.DEBUG)
```
4. **Contactar:** Team Klaus/Indexing