Add log review workflow for RAG evaluation

This commit is contained in:
Paco POR-CORREO 2026-04-06 19:54:36 +02:00
parent 98faecb967
commit 8ce0d7223b
8 changed files with 255 additions and 2 deletions

View file

@ -424,6 +424,8 @@ Ejemplo:
curl -sS "https://rag.por-correo.com/logs/recent"
```
Tambien admite actualizacion posterior del estado de revision mediante `PATCH /logs/:id`.
---
### 9. `POST /logs/manual`
@ -452,6 +454,26 @@ Payload base:
}
```
---
### 10. `PATCH /logs/:id`
Permite actualizar el seguimiento de un log ya creado.
Ejemplo:
```bash
curl -sS -X PATCH "https://rag.por-correo.com/logs/<id>" \
-H "Content-Type: application/json" \
-d '{
"reviewStatus": "resolved",
"severity": "high",
"reviewedBy": "Paco POR-CORREO",
"resolutionNote": "Se ajusto el bootstrap para scopes aislados.",
"fixReference": "6557aea"
}'
```
Respuesta esperada resumida:
```json

129
RAG/docs/LOGS_EVALUACION.md Normal file
View file

@ -0,0 +1,129 @@
# Logs de evaluacion del RAG
**Proyecto:** Workspace de tools IA para empresas
**Modulo:** RAG
**Ultima actualizacion:** 2026-04-06
**Ultima modificacion por:** Agente tools IA para potenciar servicios empresariales
**Estado:** Implementado en codigo, pendiente de redeploy
---
## Donde se guardan
Los logs de evaluacion se guardan en `Qdrant`.
Coleccion usada:
```text
rag_eval_logs
```
Se eligio `Qdrant` porque:
- ya forma parte de la infraestructura activa del RAG
- ofrece persistencia sin depender del filesystem efimero del contenedor
- permite busqueda semantica posterior sobre las consultas o incidencias registradas
---
## Estructura del log
Cada log guarda, como minimo:
- `id`
- `trigger`: `automatic` o `manual`
- `operation`: `retrieve`, `answer` o `chat`
- `reason`
- `query`
- `mode`
- `intent`
- `model`
- `note`
- `createdAt`
- `scope`
- `usedBootstrapContext`
- `usedAdditionalRetrieve`
- `responseSummary`
- `retrievedItemsCount`
- `chunkIds`
- `documentIds`
Y ahora tambien campos de seguimiento:
- `reviewStatus`: `pending`, `in_progress`, `resolved`, `ignored`
- `severity`: `low`, `medium`, `high`
- `reviewedAt`
- `reviewedBy`
- `resolutionNote`
- `fixReference`
- `supersedesLogId`
---
## Tipos de log
### 1. Automatico
Se genera cuando el sistema detecta que:
- el contexto recuperado es insuficiente
- la respuesta deja claro que no hay suficiente contexto
Objetivo:
- detectar huecos en ingesta, chunking, embeddings, retrieval o prompting
### 2. Manual
Se genera cuando el usuario lo pide explicitamente desde el playground o por API.
En este caso la nota siempre deja constancia de que fue:
```text
Log solicitado por el usuario.
```
Y si el usuario añade una nota, se concatena a ese texto base.
---
## Seguimiento del log
El sistema ya permite marcar cada log como:
- pendiente
- en progreso
- resuelto
- ignorado
Y asociarle:
- quien lo reviso
- nota de resolucion
- referencia al fix o commit
Esto permite saber:
- que logs ya se han considerado
- cuales siguen pendientes
- cuales quedaron resueltos por una mejora concreta
- cuales se decidio ignorar conscientemente
---
## Endpoints relacionados
- `GET /logs/recent`
- `POST /logs/manual`
- `PATCH /logs/:id`
---
## Uso previsto
Estos logs estan pensados para que el usuario, este agente u otros agentes puedan revisarlos juntos y decidir mejoras del RAG de forma mas fiable.
No sustituyen una evaluacion formal completa, pero si dan una base practica para:
- detectar limitaciones reales
- priorizar correcciones
- vincular mejoras con incidencias concretas

View file

@ -151,6 +151,13 @@ El playground ya soporta dos vias de logging:
Los logs quedan guardados en `Qdrant`, por lo que no dependen del filesystem efimero del contenedor.
Aunque de momento el playground no tiene una pestaña dedicada solo a logs, si puede:
- registrar logs manuales
- mostrar logs recientes
Y estos logs ya quedan preparados para ser revisados despues por el usuario junto con este agente u otros agentes.
---
## Idea de uso

View file

@ -353,5 +353,27 @@ export function createApp() {
}
});
app.patch("/logs/:id", async (req, res) => {
try {
const entry = await evaluationLogs.update(String(req.params.id), {
reviewStatus: req.body.reviewStatus,
severity: req.body.severity,
reviewedBy: req.body.reviewedBy ? String(req.body.reviewedBy) : undefined,
resolutionNote: req.body.resolutionNote ? String(req.body.resolutionNote) : undefined,
fixReference: req.body.fixReference ? String(req.body.fixReference) : undefined,
supersedesLogId: req.body.supersedesLogId ? String(req.body.supersedesLogId) : undefined
});
if (!entry) {
res.status(404).json({ ok: false, error: "Log not found" });
return;
}
res.json(entry);
} catch (error) {
res.status(500).json({ ok: false, error: error instanceof Error ? error.message : "Unknown log update error" });
}
});
return app;
}

View file

@ -40,7 +40,9 @@ export class EvaluationLogService {
responseSummary: input.responseSummary,
retrievedItemsCount: input.retrievedItems?.length ?? 0,
chunkIds: (input.retrievedItems ?? []).map((item) => item.chunkId),
documentIds: [...new Set((input.retrievedItems ?? []).map((item) => item.documentId))]
documentIds: [...new Set((input.retrievedItems ?? []).map((item) => item.documentId))],
reviewStatus: "pending",
severity: input.trigger === "automatic" ? "medium" : "low"
};
await this.client.upsert(env.qdrantLogsCollection, {
@ -75,6 +77,52 @@ export class EvaluationLogService {
.slice(0, Math.min(limit, 100));
}
async update(id: string, updates: Partial<Pick<EvaluationLogEntry, "reviewStatus" | "severity" | "reviewedBy" | "resolutionNote" | "fixReference" | "supersedesLogId">>): Promise<EvaluationLogEntry | null> {
const collections = await this.client.getCollections();
const exists = collections.collections.some((collection) => collection.name === env.qdrantLogsCollection);
if (!exists) {
return null;
}
const points = await this.client.retrieve(env.qdrantLogsCollection, {
ids: [id],
with_payload: true
});
const point = points[0];
if (!point?.payload) {
return null;
}
const current = point.payload as unknown as EvaluationLogEntry;
const next: EvaluationLogEntry = {
...current,
reviewStatus: updates.reviewStatus ?? current.reviewStatus,
severity: updates.severity ?? current.severity,
reviewedBy: updates.reviewedBy ?? current.reviewedBy,
resolutionNote: updates.resolutionNote ?? current.resolutionNote,
fixReference: updates.fixReference ?? current.fixReference,
supersedesLogId: updates.supersedesLogId ?? current.supersedesLogId,
reviewedAt: updates.reviewStatus || updates.reviewedBy || updates.resolutionNote || updates.fixReference
? new Date().toISOString()
: current.reviewedAt
};
const [vector] = await this.embeddingProvider.embed([next.query || next.reason]);
await this.client.upsert(env.qdrantLogsCollection, {
wait: true,
points: [{
id,
vector,
payload: next as unknown as Record<string, unknown>
}]
});
return next;
}
private async ensureCollection(vectorSize: number): Promise<void> {
if (this.collectionReady) {
return;

View file

@ -140,4 +140,11 @@ export interface EvaluationLogEntry {
retrievedItemsCount: number;
chunkIds: string[];
documentIds: string[];
reviewStatus: "pending" | "in_progress" | "resolved" | "ignored";
severity: "low" | "medium" | "high";
reviewedAt?: string;
reviewedBy?: string;
resolutionNote?: string;
fixReference?: string;
supersedesLogId?: string;
}

View file

@ -67,6 +67,7 @@ Este archivo registra agentes y sesiones de trabajo de este workspace.
- Evolucion del playground a una mecanica mas completa con pestañas `Ingesta / Bootstrap / Chat`, indicador visual de contexto activo y endpoint `/chat` con bootstrap reutilizable y consultas adicionales al RAG durante la conversacion.
- Ampliacion de la ingesta y del playground para soportar upload directo de archivos y `sourceId` personalizado, permitiendo aislar documentos ajenos al RAG en scopes separados.
- Implementacion de logs de evaluacion persistentes en `Qdrant`, con disparo automatico por contexto insuficiente y registro manual con nota desde el playground.
- Ampliacion de los logs de evaluacion para permitir seguimiento real (`pending`, `resolved`, `ignored`, etc.) y documentacion de su arquitectura en `RAG/docs/LOGS_EVALUACION.md`.
- Reorganizacion de RAG como modulo raiz independiente con documentacion propia en `RAG/docs/`.
- Ajuste del indice documental global para reflejar la separacion entre documentacion global y documentacion por tool.
- Creacion de `docs/TASK.md` para descomponer lineas de trabajo amplias en puntos de analisis y acuerdos.

View file

@ -306,6 +306,23 @@ Documentar la tecnologia, ubicacion y utilidad del playground interno del RAG pa
---
### `RAG/docs/LOGS_EVALUACION.md`
**Ubicacion:** `RAG/docs/LOGS_EVALUACION.md`
**Proposito:**
Documentar como se guardan, siguen y revisan los logs de evaluacion del RAG.
**Cuando leerlo:**
- al revisar logs generados por el playground o por la API
- al planificar mejoras del RAG a partir de incidencias registradas
**Cuando actualizarlo:**
- cuando cambie el esquema o el flujo de revision de logs
- cuando se amplie el sistema de evaluacion del RAG
---
### `sesion_actual_opencode.md`
**Ubicacion:** `docs/sesion_actual_opencode.md`
@ -332,4 +349,4 @@ Instruccion universal para detectar la sesion activa de OpenCode del workspace a
## Estadistica global
**Total de documentos indexados:** 17
**Total de documentos indexados:** 18