Ir al contenido

Auditoría y Privacidad del Asesor de IA

🔐 Auditoría y Privacidad del Asesor de IA

Sección titulada «🔐 Auditoría y Privacidad del Asesor de IA»

El asesor usa dos almacenes con propiedades opuestas y un modelo de privacidad diseñado para que el único tráfico saliente sea la propia llamada al modelo.


🗄️ Persistencia: Memoria vs. Rastro de Auditoría

Sección titulada «🗄️ Persistencia: Memoria vs. Rastro de Auditoría»
ai-advisor.db (memoria conversacional) ai_advisor_runs (rastro de auditoría)
Motor libsql mediante @mastra/libsql, creado por createAdvisorMemory Ledger SQLite (criptofolio_ledger.db), tabla STRICT
Contenido Conversación completa, incluida cada cifra enunciada o devuelta por una herramienta Solo procedencia, nunca contenido
Ciclo de vida Desechable; borrar el archivo solo elimina conversaciones Duradero; no le afecta un borrado
Escrito por Memory de Mastra SqliteAdvisorRunLogAdapter mediante IAdvisorRunLogPort

La migración 009_ai_advisor_runs.sql creó ai_advisor_runs; 010_nullable_advisor_execution_profile.sql hizo execution_profile anulable. Consulta el Ledger Transaccional SQLite para las convenciones de esquema circundantes.

CREATE TABLE ai_advisor_runs (
id TEXT PRIMARY KEY,
thread_id TEXT NOT NULL,
started_at TEXT NOT NULL,
finished_at TEXT,
outcome TEXT NOT NULL CHECK (outcome IN ('completed','refused','failed','aborted')),
provider_id TEXT,
model_id TEXT,
tools_called TEXT NOT NULL DEFAULT '[]',
input_tokens INTEGER,
output_tokens INTEGER,
failure_code TEXT,
execution_profile TEXT CHECK (execution_profile IS NULL OR execution_profile IN ('local','metered','mixed')),
steps_used INTEGER,
max_steps INTEGER
) STRICT;
Columna Significado
id El id de la ejecución; la escritura es un upsert sobre él, así que una finalización y una limpieza por cancelación nunca pueden escribir doble.
thread_id El hilo de la conversación.
started_at, finished_at Marcas de tiempo ISO; finished_at es NULL en una ejecución abortada.
outcome completed, refused, failed o aborted.
provider_id, model_id La entrada que respondió, o la última intentada; NULL cuando no se probó ninguna entrada.
tools_called Array JSON de nombres de herramientas, ampliado cuando llega un resultado o error de herramienta.
input_tokens, output_tokens Uso informado en finish; NULL en ejecuciones abortadas.
failure_code Solo en ejecuciones failed; permanece como ALL_PROVIDERS_FAILED y no registra el tipo de causa.
execution_profile local, metered, mixed o NULL.
steps_used, max_steps Pasos consumidos (contados desde los chunks step-finish) frente al techo; NULL cuando se desconocen.

Reglas que se derivan del código:

  • El reason de una ejecución refused nunca se escribe, deliberadamente.
  • Un fallo antes de probar ninguna entrada de la cadena (NO_MODEL_AVAILABLE, VAULT_LOCKED) es un recibo pre-run: las columnas de perfil y pasos son NULL, no se adivinan.
  • Una ejecución abortada se escribe desde el finally del caso de uso solo con el id de ejecución, el id de hilo, la hora de inicio y una lista de herramientas vacía, así que no tiene proveedor, modelo, perfil, tokens ni contadores de pasos.
  • stepsUsed y maxSteps de un recibo profiled son obligatorios junto con executionProfile; el tipo hace irrepresentable “perfil sin pasos”.

[!WARNING] La migración 010 reconstruye la tabla con DROP TABLE IF EXISTS ai_advisor_runs (SQLite no puede eliminar NOT NULL con ALTER) y declara una suposición de pizarra limpia: el proyecto no tenía despliegue en producción y la tabla no contenía filas que valiera la pena conservar. Aplicarla a una base de datos con filas de auditoría reales las descarta.

  • Ubicación: ai-advisor.db en la raíz de datos, sobrescribible con ADVISOR_DB_PATH (un valor relativo se ancla a la raíz de datos, se respeta :memory:). CRIPTOFOLIO_DATA_DIR mueve toda la raíz de datos.
  • Recuerdo: cada turno reproduce los últimos lastMessages mensajes del hilo (resource: 'local'). La recuperación semántica, la memoria de trabajo y la memoria observacional están desactivadas, así que no hay embedder ni almacén vectorial. Los pares llamada/resultado de herramienta de turnos anteriores se filtran (ToolCallFilter con preserveModelOutput: true): el modelo conserva su propia prosa anterior pero debe volver a consultar una herramienta en lugar de fiarse de una cifra obsoleta.
  • Borrado: detén el backend y elimina ai-advisor.db junto con cualquier archivo -wal, -shm o -journal a su lado. El archivo se recrea en la siguiente ejecución. No se borra nada más.
  • Ciclo de vida: el archivo se abre de forma perezosa en la primera petición al asesor, nunca al importar.

Propiedad Mecanismo
Solo lectura Sin herramienta de escritura; un test estructural rechaza insert/update/delete en los archivos de herramientas.
Sin secretos en la ruta del modelo Las claves se descifran por petición, se mantienen solo en la cadena resuelta y se redactan de los mensajes de error registrados.
Entrada del modelo acotada Esquemas de herramienta estrictos; ningún id de cuenta ni identificador interno es nunca una entrada del modelo; regex de símbolos.
Salida del modelo no confiable Markdown con HTML crudo, imágenes y enlaces fuera de la app desactivados.
El contenido permanece local en reposo Contenido conversacional solo en ai-advisor.db; la tabla de auditoría no contiene ninguno.
Sin telemetría No se registra ninguna instancia de Mastra ni exportador; @mastra/observability no está instalado (noTelemetry.spec.ts).
Local vs medido no lo etiqueta el usuario Derivado del proveedor y el id de modelo (ver Proveedores y Modelos).
Detalle de errores La red transporta un cause.kind cerrado, nunca texto del proveedor, URLs, cabeceras, claves ni cuerpos.
Sin superficie genérica de agentes Exactamente seis rutas; ningún adaptador de servidor de Mastra montado.

[!WARNING] Cuando el modelo que responde es de nube, la pregunta y las cifras devueltas por las herramientas se envían a ese proveedor. El panel de chat muestra una insignia cloud precisamente por eso. Usa una cadena totalmente local si los datos no deben salir de la máquina. Trata ai-advisor.db con la misma sensibilidad que el ledger.

El único tráfico saliente es la propia llamada al modelo: a un proveedor alojado para entradas de nube, o a OLLAMA_BASE_URL para ollama. Las claves de proveedor viven en la Bóveda Cifrada Local, nunca en variables de entorno.