# 📚 Knowledge Base — klaude-proxy
## 🤔 ¿Qué hago? ¿Cómo lo hago? ¿Y para qué lo hago?
**¿Qué hago?**
Indexo los ficheros de un proyecto (documentación y código fuente) en una colección Qdrant separada llamada `klaude_knowledge`. Cuando el usuario hace una pregunta sobre el proyecto, inyecto los fragmentos más relevantes como contexto RAG en el system prompt y llamo al LLM externo para generar la respuesta con conocimiento del proyecto.
**¿Cómo lo hago?**
El dashboard ofrece un botón **📚 Knowledge** que abre un modal. Mediante la File System Access API del navegador, el usuario selecciona una carpeta local. El JavaScript lee los ficheros con las extensiones configuradas, los divide en *chunks* semánticos y los envía al endpoint `POST /knowledge/ingest`. El proxy embebe cada chunk con `nomic-ai/nomic-embed-text-v1.5` y lo almacena en Qdrant con su ruta de fichero y posición.
En cada petición a `/v1/messages`, después de buscar en el caché semántico y antes de llamar al LLM externo, el proxy busca en `klaude_knowledge` mediante búsqueda híbrida (BM25 + dense, RRF fusion). Si hay resultados sobre el umbral (0.75 por defecto), inyecta los chunks en el system prompt como bloque de contexto RAG y llama al LLM. La respuesta lleva header `X-Cache: KNOWLEDGE-RAG`.
**¿Para qué lo hago?**
Para enriquecer las respuestas del LLM con conocimiento específico del proyecto. Cuando Claude Code está abierto dentro de un proyecto y el usuario pregunta sobre la arquitectura, APIs internas o configuración, el proxy incluye automáticamente los fragmentos de documentación más relevantes en el contexto — sin que el usuario tenga que copiarlos manualmente.
---
## 🗺️ Flujo de datos
```mermaid
flowchart TD
U[Usuario] -->|Selecciona carpeta| B[Dashboard JS\nFile System Access API]
B -->|Lee ficheros .md .py .sh .yml .yaml .json| C[Chunking JS]
C -->|POST /knowledge/ingest\nbatch 20 chunks| D[Endpoint FastAPI]
D -->|get_embedding - nomic ONNX| E[Qdrant\nklaude_knowledge]
subgraph Petición runtime
R[Request /v1/messages] --> S1[Semantic Cache Search]
S1 -->|score ≥0.98 directo| HIT[X-Cache: HIT]
S1 -->|score ≥0.87 RAG| CRAG[Inyectar cache\nen system prompt]
CRAG --> LLM_C[Anthropic API\nX-Cache: CACHE-RAG]
S1 -->|MISS| S2[Knowledge Search\nhíbrida BM25+dense]
S2 -->|chunks ≥0.75| KRAG[Inyectar knowledge\nen system prompt]
KRAG --> LLM_K[Anthropic API\nX-Cache: KNOWLEDGE-RAG]
S2 -->|MISS| LLM[Anthropic API\nX-Cache: MISS]
end
E --> S2
```
---
## ⚙️ Configuración
| Variable de entorno | Default | Descripción |
| --- | --- | --- |
| `KNOWLEDGE_SIMILARITY_THRESHOLD` | `0.75` | Umbral de similitud coseno para KB hits |
> El umbral 0.75 es deliberadamente más permisivo que el caché semántico (0.87) para maximizar el recall en documentación técnica donde el rephrase es mayor.
---
## 📂 Extensiones indexadas
| Extensión | Estrategia de chunking |
| --- | --- |
| `.md` | Por secciones `##` (preserva contexto semántico), sub-chunks de 800 chars |
| `.py` | Bloques de 500 chars con 100 overlap, split en doble newline |
| `.sh` | Bloques de 500 chars con 100 overlap |
| `.yml` / `.yaml` | Bloques de 500 chars con 100 overlap |
| `.json` | Bloques de 500 chars con 100 overlap |
### 🚫 Directorios excluidos automáticamente (`KB_SKIP_DIRS`)
`.git`, `node_modules`, `__pycache__`, `.venv`, `venv`, `dist`, `build`, `.claude`, `target`, `.mypy_cache`, `.pytest_cache`, **`tests`**, **`test`**, **`__tests__`**, **`spec`**, **`specs`**
> ⚠️ **Por qué se excluye `tests/`**: Los scripts de test funcional (`test_cache_semantic*.sh`) contienen preguntas hardcodeadas como strings de `call_proxy()` y payloads JSON de API. Si se indexaran, la colección acumularía "solicitudes del agente" en lugar de documentación real. Al indexarlas, el KB intercepta las peticiones reales vía `KNOWLEDGE HIT` antes de que lleguen a Anthropic, impidiendo que las respuestas se guarden en `klaude_cache`. (Diagnóstico: [GH-78](https://github.com/Ka0s-Klaus/klaude-code-local/issues/78))
### 🚫 Ficheros excluidos automáticamente (`KB_SKIP_FILES`)
`CLAUDE.md`
> ⚠️ El fichero `CLAUDE.md` contiene configuración del agente (instrucciones de sesión, contexto del proyecto para Claude Code). No es documentación del proyecto y su contenido queda obsoleto rápidamente — indexarlo contaminaría el KB con metadatos de agente en lugar de conocimiento técnico útil.
---
## 🛡️ Filtros anti-contaminación
El sistema tiene **dos capas de defensa** para evitar que contenido no útil contamine `klaude_knowledge`:
```mermaid
flowchart LR
F[Fichero del proyecto] --> L1{Capa 1: JS\nKB_SKIP_DIRS\nKB_SKIP_FILES}
L1 -->|excluido| SKIP1[❌ No se envía al proxy]
L1 -->|permitido| L2{Capa 2: Python\nis_llm_response}
L2 -->|chunk sospechoso| SKIP2[❌ skipped en ingest]
L2 -->|chunk legítimo| STORE[✅ Indexado en Qdrant]
```
| Capa | Dónde | Qué filtra |
| --- | --- | --- |
| 🥇 **KB_SKIP_DIRS** | JS dashboard | Directorios de test, build, entornos virtuales |
| 🥇 **KB_SKIP_FILES** | JS dashboard | Ficheros de configuración del agente (`CLAUDE.md`) |
| 🥈 **`is_llm_response()`** | Python backend | Meta-respuestas LLM, frases de compactación, payloads de API hardcodeados en scripts de test |
La capa JS es la defensa primaria (más eficiente — evita el RTT al proxy). La capa Python es una segunda línea que captura contenido que pudiera colarse por ficheros con extensión legítima (p.ej. un `.py` que genere payloads de test).
---
## 🔌 Endpoints API
### `POST /knowledge/ingest`
Recibe chunks pre-procesados del dashboard y los embebe + almacena en Qdrant.
**Body:**
```json
{
"chunks": [
{"content": "texto del fragmento", "file_path": "docs/arquitectura.md", "chunk_index": 0},
{"content": "...", "file_path": "proxy/main.py", "chunk_index": 1}
]
}
```
**Response:**
```json
{"stored": 42, "skipped": 3, "failed": 0, "total": 45}
```
> `skipped` cuenta los chunks rechazados por `is_llm_response()` — chunks que el backend detecta como meta-respuestas del LLM o payloads de scripts de test.
### `GET /knowledge/stats`
```json
{"collection": "klaude_knowledge", "points_count": 312, "status": "Green"}
```
### `DELETE /knowledge/flush`
Requiere header `x-api-key`. Borra y recrea la colección `klaude_knowledge`.
```bash
curl -X DELETE -H "x-api-key: test" http://192.168.1.50:8080/knowledge/flush
```
---
## 📊 Dashboard
El botón **📚 Knowledge** en el header abre un modal con:
| Elemento | Descripción |
| --- | --- |
| 📁 Seleccionar carpeta | `<input webkitdirectory>` — Firefox, Chrome, Edge, Safari |
| 🗑️ Borrar KB | Llama a DELETE /knowledge/flush |
| Progress bar | Progreso de upload en batches de 20 chunks |
| Status text | Feedback en tiempo real del estado de indexación |
La sidebar muestra el contador de **chunks indexados** (actualización cada 5s) y el contador **KB HIT** de la sesión. Los logs live muestran `KNOWLEDGE HIT` en color **púrpura** (`#a78bfa`).
---
## 🔒 Seguridad
- El endpoint `/knowledge/ingest` no requiere autenticación (solo añade datos, no los expone).
- El endpoint `/knowledge/flush` requiere `x-api-key` (operación destructiva).
- El proxy no expone el contenido indexado a través de ningún endpoint de listado — solo búsqueda por similitud.
- Los ficheros se leen localmente en el navegador del usuario — **no se envían a ningún servicio externo**.
---
## 📁 Ficheros relacionados
| Fichero | Rol |
| --- | --- |
| [`proxy/knowledge.py`](../proxy/knowledge.py) | Módulo: colección Qdrant, search, store, format_response, stats, flush |
| [`proxy/main.py`](../proxy/main.py) | Endpoints REST + integración en /v1/messages + dashboard HTML |
| [`proxy/config.py`](../proxy/config.py) | `knowledge_similarity_threshold` |
| [`proxy/embeddings.py`](../proxy/embeddings.py) | `get_embedding()` — compartido con el caché semántico |
| [`proxy/cache.py`](../proxy/cache.py) | `get_client()` — cliente Qdrant compartido |
🔗 Ver también: [`cache.md`](cache.md) — caché semántico (colección `klaude_cache`)