Add log review workflow for RAG evaluation
This commit is contained in:
parent
98faecb967
commit
8ce0d7223b
8 changed files with 255 additions and 2 deletions
|
|
@ -424,6 +424,8 @@ Ejemplo:
|
||||||
curl -sS "https://rag.por-correo.com/logs/recent"
|
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`
|
### 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:
|
Respuesta esperada resumida:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
129
RAG/docs/LOGS_EVALUACION.md
Normal file
129
RAG/docs/LOGS_EVALUACION.md
Normal 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
|
||||||
|
|
@ -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.
|
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
|
## Idea de uso
|
||||||
|
|
|
||||||
|
|
@ -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;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ export class EvaluationLogService {
|
||||||
responseSummary: input.responseSummary,
|
responseSummary: input.responseSummary,
|
||||||
retrievedItemsCount: input.retrievedItems?.length ?? 0,
|
retrievedItemsCount: input.retrievedItems?.length ?? 0,
|
||||||
chunkIds: (input.retrievedItems ?? []).map((item) => item.chunkId),
|
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, {
|
await this.client.upsert(env.qdrantLogsCollection, {
|
||||||
|
|
@ -75,6 +77,52 @@ export class EvaluationLogService {
|
||||||
.slice(0, Math.min(limit, 100));
|
.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> {
|
private async ensureCollection(vectorSize: number): Promise<void> {
|
||||||
if (this.collectionReady) {
|
if (this.collectionReady) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -140,4 +140,11 @@ export interface EvaluationLogEntry {
|
||||||
retrievedItemsCount: number;
|
retrievedItemsCount: number;
|
||||||
chunkIds: string[];
|
chunkIds: string[];
|
||||||
documentIds: string[];
|
documentIds: string[];
|
||||||
|
reviewStatus: "pending" | "in_progress" | "resolved" | "ignored";
|
||||||
|
severity: "low" | "medium" | "high";
|
||||||
|
reviewedAt?: string;
|
||||||
|
reviewedBy?: string;
|
||||||
|
resolutionNote?: string;
|
||||||
|
fixReference?: string;
|
||||||
|
supersedesLogId?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
- 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.
|
- 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.
|
- 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/`.
|
- 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.
|
- 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.
|
- Creacion de `docs/TASK.md` para descomponer lineas de trabajo amplias en puntos de analisis y acuerdos.
|
||||||
|
|
|
||||||
|
|
@ -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`
|
### `sesion_actual_opencode.md`
|
||||||
|
|
||||||
**Ubicacion:** `docs/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
|
## Estadistica global
|
||||||
|
|
||||||
**Total de documentos indexados:** 17
|
**Total de documentos indexados:** 18
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue