4dim / Notas

Barrer datos personales en un chat colombiano: el punto de los miles

Un barrido pensado en inglés borra «1.500.000» creyendo que es un teléfono. Las ocho reglas que usamos, en su orden, con el NIT, el algoritmo de Luhn, las siete cifras que son teléfono, y tres trampas de JavaScript con texto en español.

Por qué barrer si ya hay fichas

Si un asistente de IA va a leer las conversaciones de tus clientes, hay un momento en que ese texto sale de tu servidor. Esta nota cuenta qué se borra antes de ese viaje, y por qué unas reglas escritas fuera no sirven aquí.

Los contactos y las personas del equipo viajan al modelo como fichas, etiquetas cortas del tipo «C-7», y eso se puede deshacer al volver. Pero lo que el cliente escribió es texto libre: «mándamelo al 310 555 1234, mi cédula es 1.020.345.678». Ahí no hay entidad a la que referirse. Hay que borrar.

Y borrar de verdad. El barrido es irreversible a propósito. Nada de eso se necesita de regreso, y un mapa de vuelta sería una segunda copia de justo lo que estamos protegiendo.

La filosofía cabe en una frase del código: ante la duda, se borra. Tapar de más deja al asistente diciendo «el cliente pidió que le mandaran a «TEL»», que se entiende. Tapar de menos manda el teléfono de una persona a un tercero, y eso no se deshace.

El punto de los miles

La primera decisión que un barrido pensado en inglés toma mal es esta: en Colombia el punto separa los miles. «1.500.000» es un millón y medio de pesos, no un teléfono. Si el punto se admite como separador de dígitos de teléfono, el barrido borra el precio de cada cotización. Y un asistente que no puede hablar de precios en una conversación de ventas no sirve. Así que el punto queda fuera de la regla de teléfonos.

Lo mismo pasa con el NIT, el número con el que se identifica una empresa. «900.123.456-7» y «1.500.000» tienen los mismos grupos y los mismos puntos. Solo los distinguen el guion y el dígito de verificación del final.

Por eso la regla del NIT exige el guion y ese dígito como parte obligatoria del patrón. Un NIT escrito sin ellos lo captura otra regla, la de documentos anunciados, que mira la palabra que va delante en vez de la forma del número: «cédula», «CC», «NIT», «pasaporte», «RUT», «DNI», seguidas de un número de al menos cinco dígitos.

Un barrido de datos personales para Colombia tiene que saber cuánto vale un millón y medio de pesos. Si no, protege la privacidad borrando el negocio.

La tarjeta antes que el teléfono, y con Luhn

Una regla de «doce a dieciocho dígitos» marca como tarjeta cualquier número de guía o referencia de pago. Por eso se le añade el algoritmo de Luhn, la cuenta de control que llevan las tarjetas. Para colarse, un número tiene además que cuadrar esa suma, y eso no pasa por azar.

Y la regla de tarjeta corre antes que la de teléfono. Si el teléfono fuera primero, se comería los diez primeros dígitos de una tarjeta y dejaría los seis últimos sueltos. La regla de tarjeta ya no vería nada que cuadrar. En un barrido por reglas, el orden es parte de la regla.

El orden de las reglas

Las reglas se aplican en orden, de la más específica a la más ancha.

OrdenReglaPor qué aquí
1Tarjeta (con Luhn)Antes de que el teléfono le coma dígitos
2CorreoTiene forma propia; no compite con nadie
3EnlaceSe conserva el dominio y se borra lo que va después: ahí viven los pedidos y los tokens
4Documento anunciadoLa palabra delante desambigua el número
5NIT con dígitoEl guion lo separa de un precio
6Nombres de contactosEntre documentos y teléfonos, para que un nombre no parta un número
7Teléfono con «+»Forma inequívoca
8Teléfono suelto (7 o más cifras)El último, «el que tiene la boca más grande»

La última regla toma una decisión estadística, y la toma a la vista de todos. Siete cifras seguidas, en un chat de ventas colombiano, son un teléfono muchas más veces de las que son otra cosa. Se tapan aunque en algún caso fueran un precio. Seis cifras, como «450000», pasan intactas.

Tres trampas de JavaScript

El barrido está hecho de expresiones regulares: patrones que describen la forma de lo que se busca. JavaScript, el lenguaje en que se escriben aquí, tiene tres trampas.

  • \b no conoce las tildes. Sirve para marcar dónde empieza y acaba una palabra, pero está definido sobre letras sin acento. Así que «Andrés» termina, para \b, en un borde que no existe. Se cambia por una comprobación de lo que hay alrededor, con clases Unicode: que ni antes ni después haya letra ni dígito, (?<![\p{L}\p{N}]) y su pareja. Es lo que \b quiere decir de verdad.
  • Un tope superior hace que el patrón no capture nada. Un patrón de teléfono «de 7 a 15 dígitos» no casa con una tira de 16. El patrón mira hacia delante, ve otro dígito y no hay retroceso que lo salve, así que la tira sale intacta. Dieciséis cifras que no cuadraban con Luhn se escapaban enteras por eso. La regla de teléfono no tiene máximo.
  • Un patrón con /g guarda memoria. Esa marca pide buscar todas las apariciones, y al preguntarle con .test() arrastra su lastIndex entre llamadas. La segunda pregunta empieza a media cadena, y contesta que no hay nada justo donde sí lo hay. Las copias que usan las pruebas van sin esa marca.

Lo que se pierde a sabiendas

Un contacto llamado «Ana Ruiz» tapa «Ana Ruiz» y tapa «Ruiz», pero no tapa «Ana» suelta. Tres letras casan dentro de demasiadas palabras y el texto acabaría ilegible. Se pierde alguna mención de un nombre corto. A cambio, el mensaje sigue siendo un mensaje. Sí se generan variantes sin tildes, porque un cliente escribe «Maria» y la base dice «María».

Y una cosa que casi nadie hace en la pantalla de un asistente: quitar el bloque de «razonamiento en voz alta» que algunos modelos devuelven dentro de la propia respuesta, entre etiquetas <think>. Si se pinta, el cliente ve la cocina. Y ve cómo razona un sistema al que le acabamos de tapar los nombres. Se borra antes de mostrar, incluido el caso de la etiqueta sin cerrar, cuando la respuesta se cortó a mitad de pensamiento.

Qué hacer entonces

  • Escribir el barrido para el país: separador de miles, formato de NIT, palabras que anuncian un documento.
  • Ordenar las reglas de la más específica a la más ancha, y dejar escrito por qué cada una va donde va.
  • Hacerlo un módulo aparte, sin base de datos, y probarlo con casos reales: es la última línea antes de que un dato salga a un tercero.
  • Preferir el falso positivo. Lo que se tapa de más se entiende; lo que se tapa de menos no se deshace.
  • Revisar las tres trampas de JavaScript antes de confiar en un patrón con texto en español.

Esta nota sale de nuestro trabajo en Agentes conversacionales.

← todas las notas