¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Un registro de auditoría que delata ediciones

Después de esta lección: Puedo llevar un registro que muestre si lo cambiaron.

Lección de 70 min7 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

Cómo hacer un registro que delate manipulaciones

Un registro de auditoría es un historial de quién cambió qué y cuándo. Un registro que cualquiera puede editar no es una prueba, porque nadie sabe si lo cambiaron.

Importa porque una decisión sobre un cambio de IA vale lo que vale su registro. Una cadena de hashes hace que cualquier edición del registro se note.

Al final de esta lección

Nota

Puedo llevar un registro que muestre si lo cambiaron.

Palabras clave

PalabraQué significaEjemplo
Solo añadirse añaden entradas nuevas y las viejas no se editan
Hashuna huella corta de un texto1a47e90b
Cadenaentradas que guardan cada una el hash de la anterior
Manipularcambiar un registro sin permiso
Anclael último hash, guardado en otro sitio

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un hash es la huella de un texto

Un hash convierte cualquier texto en un código corto. El mismo texto da siempre el mismo código. Cambia una letra y el código cambia por completo.

El hash de esta lección es pequeño y está hecho para enseñar. No es seguro. Un registro real usa un hash fuerte de una biblioteca, y la idea es la misma.

Ejemplo resuelto: tres huellas

TextoHash
(nada)811c9dc5
ae40c292c
abc1a47e90b

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una cadena enlaza cada entrada con la anterior

Cada entrada guarda su texto, el hash de la entrada anterior y su propio hash. Su propio hash se hace con el hash anterior, una barra y su texto.

Edita la entrada 2 y su propio hash ya no coincide con su texto. Calcula un hash nuevo para disimularlo y la entrada 3 sigue guardando el viejo, así que se rompe el eslabón siguiente.

Ejemplo resuelto: cinco entradas

EntradaTextoHash anteriorHash propio
0modelo fijado a chat-v3000000007e0a2f67
1canario al 5 por ciento7e0a2f67e5858133
2canario al 20 por cientoe585813314be4e02
3promovido a todos los usuarios14be4e021b816758
4modelo antiguo retirado1b816758454d87c4

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un error frecuente

Advertencia

Mucha gente cree que una cadena de hashes impide cambiar el registro. No lo impide. Solo muestra un cambio. Quien pueda escribir todo el archivo puede rehacer cada hash. Guarda el último hash, el ancla, donde no lleguen, y compáralo.

Escribe el ayudante del modelo, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Los tokens de una respuesta, o null si no trae ninguno.' 'function count(data, name) {' '  return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe el ayudante del modelo, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"estado \" + res.status);' '    const d = await res.json();' '    return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' '  } catch (error) {' '    console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' '    return { text: sample, inTokens: null, outTokens: null, recorded: true };' '  }' '}' 'module.exports = { ask };' >> model.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe el script de auditoría, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const START = \"00000000\";' 'function hash(text) {' '  let x = 2166136261;' '  for (let i = 0; i < text.length; i++) {' '    x = x ^ text.charCodeAt(i);' '    x = Math.imul(x, 16777619) >>> 0;' '  }' '  return (\"00000000\" + x.toString(16)).slice(-8);' '}' > audit.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe el script de auditoría, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function add(chain, text) {' '  const prev = chain.length > 0 ? chain[chain.length - 1].hash : START;' '  return chain.concat([{ text: text, prev: prev, hash: hash(prev + \"|\" + text) }]);' '}' 'function firstBroken(chain) {' '  let prev = START;' '  for (let i = 0; i < chain.length; i++) {' '    const e = chain[i];' '    if (e.prev === prev && e.hash === hash(e.prev + \"|\" + e.text)) prev = e.hash;' '    else return i;' '  }' '  return -1;' '}' >> audit.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe el script de auditoría, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const notes = [\"modelo fijado a chat-v3\", \"canario al 5 por ciento\", \"canario al 20 por ciento\",' '  \"promovido a todos los usuarios\", \"modelo antiguo retirado\"];' 'let chain = [];' 'notes.forEach(function (t) { chain = add(chain, t); });' 'const anchor = chain[4].hash;' 'const forged = chain.map(function (e) { return { text: e.text, prev: e.prev, hash: e.hash }; });' 'forged[2].text = \"canario al 90 por ciento\";' 'let again = [];' 'forged.forEach(function (e) { again = add(again, e.text); });' >> audit.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe el script de auditoría, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  console.log(\"hash de abc: \" + hash(\"abc\"));' '  console.log(\"primer eslabón roto en el registro real: \" + firstBroken(chain));' '  console.log(\"primer eslabón roto tras una edición: \" + firstBroken(forged));' '  console.log(\"el registro reconstruido es coherente: \" + (firstBroken(again) === -1));' '  console.log(\"el registro reconstruido coincide con el ancla: \" + (again[4].hash === anchor));' '  const q = \"En una frase corta, ¿por qué se nota una entrada editada en una cadena de hashes?\";' '  const sample = \"Cada entrada guarda la huella de la anterior, así que un cambio rompe todos los eslabones siguientes.\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 60);' '  console.log(\"el modelo dijo: \" + reply.text);' '}' 'main();' >> audit.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta el registro de auditoría

Comando
docker exec -w /home/node/project agent node audit.js

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Lo que imprime el Run

LíneaQué muestra
hash de abc: 1a47e90bLa misma huella siempre
primer eslabón roto en el registro real: -1Ningún eslabón roto
primer eslabón roto tras una edición: 2La edición se halla en la entrada 2
el registro reconstruido es coherente: trueUna reconstrucción completa pasa la comprobación de eslabones
el registro reconstruido coincide con el ancla: falseEl ancla sigue mostrando el cambio
el modelo dijo: ...Una frase del laboratorio. Una muestra grabada si ningún modelo responde

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.