Resumen
- Muestra cómo se puede ampliar HCL Informix para aplicaciones modernas de inteligencia artificial.
- Explica la gestión directa de datos vectoriales en Informix.
- Permite crear flujos de trabajo de IA sin necesidad de bases de datos adicionales.
- Permite aprovechar los datos existentes de Informix para la IA generativa.
Capítulos
Bueno, pues vale. Herzlich willkommen al « seminario en línea » sobre HCL Informix y Vector Blade. Me llamo Steffen Kleber, soy investigador jefe en Action, llevo ya más de nueve años en la empresa y soy responsable de nuestros sistemas de bases de datos, la estrategia y las iniciativas de inteligencia artificial en los sistemas.
De lo que queremos hablar hoy es, por un lado, como es lógico, de Vector Blade, la extensión para HCL Informix que permite consultar datos vectoriales y trabajar con ellos, pero también, en cierta medida, del panorama general: ¿cómo encaja Informix en el panorama actual de la IA? Antes de entrar en detalles, me gustaría hablar de las tendencias y situar todo esto en su contexto. Por un lado, es evidente que la IA, en general, está cambiando nuestra forma de entender lo que realmente podemos hacer con los datos.
Es decir, tradicionalmente se ha trabajado con datos estructurados. Se utilizaban tablas y esquemas fijos, y todo ello se gestionaba mediante bases de datos relacionales. Sin embargo, la inteligencia artificial permite ahora el procesamiento de datos no estructurados, es decir, textos, imágenes, vídeos y audio, incluso en combinación.
Además, los datos estructurados y no estructurados pueden procesarse conjuntamente. Todo ello es posible gracias al tipo de datos vectoriales y a la búsqueda por similitud. Es decir, ya no tenemos que limitarnos a trabajar únicamente con consultas relacionales (consultas SQL), sino que también podemos buscar datos en función de su significado, en lugar de limitarse únicamente a palabras clave exactas.
Y eso es precisamente lo que lo hace tan atractivo en el ámbito empresarial, ya que hay diversos estudios —algo que ya se puso de manifiesto en 1998 con el primer informe, pero que también han confirmado informes recientes de Gartner e IDC— que indican que, en realidad, el 80 % de los datos empresariales son no estructurados. Es decir, se trata de los correos electrónicos, las páginas de Confluence y la documentación en PDF que se encuentran dispersas por ahí y que contienen tanto valor que es importante sacarles partido y valor. Por otro lado, la IA cambia, por supuesto, la forma de gestionar los datos.
Por supuesto, antes de la era de la IA, trabajábamos con ello principalmente de forma manual. Es decir, gestionábamos flujos de trabajo, los creábamos, gestionábamos esquemas, limpiábamos los datos manualmente, buscábamos errores en los datos, administrábamos sistemas y creábamos estructuras de informes, todo ello de forma manual. El primer paso que ha aportado la IA es que ahora todo esto puede realizarse de forma interactiva.
Esto significa que, por supuesto, podemos utilizar el lenguaje natural para solicitar que se analicen nuestros datos. Pero también podemos formular instrucciones o preguntas en lenguaje natural sobre cómo debemos utilizar el sistema, qué errores podrían producirse y qué posibilidades de optimización existen. Y esto facilita enormemente el acceso a estos sistemas, incluso para personas que quizá tengan pocos o ningún conocimiento técnico.
Lo importante es que, por supuesto, esto todavía lo controla una persona. Es decir, tengo que plantear preguntas de forma activa en mi sistema. En principio, esto significa que ya tengo que identificar los problemas y solo tengo que preguntar cómo se puede resolver todo.
En la era de la IA, nos orientamos hacia los agentes autónomos. Es decir, estos se guían por objetivos. Por lo tanto, como ser humano, ya no tengo que saber cuál es realmente el objetivo o el camino para alcanzarlo, sino que puedo dejar que los agentes trabajen de forma autónoma.
Detectan problemas de forma autónoma, planifican y aplican soluciones para ellos, y eso es precisamente lo que permite un mantenimiento de los sistemas las 24 horas del día, consultas automatizadas; quizá los agentes también detecten estructuras o problemas en los datos que un ser humano nunca habría encontrado. Contamos precisamente con estas dos cosas. Por un lado, la IA permite procesar más datos, pero también nos permite abordar los datos de una forma totalmente diferente.
Entonces, naturalmente, surge la pregunta: ¿siguen siendo relevantes las bases de datos? Y ese es uno de los mensajes principales que queremos transmitir hoy. Las bases de datos relacionales siguen siendo muy importantes y siguen constituyendo la base fiable sobre la que se asientan la IA y los agentes.
No hay que olvidarlo. Las bases de datos son fiables por su propio diseño. Todos conocemos las propiedades ACID, y esto es especialmente importante en la era de los agentes, para que estos trabajen con datos coherentes y como única fuente de verdad.
Es decir, dispongo de un sistema que mantiene mis datos coherentes y de calidad, y que no están dispersos en distintas estructuras de carpetas y cosas por el estilo. A modo de anécdota: vi, por ejemplo, un vídeo de « seminario en línea » de Antropic en el que se planteaban cómo los agentes pueden trabajar juntos en archivos. Se trata de problemas que el mundo de las bases de datos ya ha resuelto desde hace décadas.
Concurrencia y aplicaciones multiusuario. Informix resulta especialmente adecuado para este tipo de aplicaciones, ya que es multimodal y ampliable. Esto significa que Informix puede almacenar distintos tipos de datos, no solo datos estructurados.
Con Vector Blade, de lo que vamos a hablar hoy, ampliamos el conjunto con vectores, lo que nos permite ampliar HCL Informix de tal forma que también esté preparado para aplicaciones de IA. Además, los agentes modifican ligeramente la carga de trabajo que enviamos a los sistemas. Los agentes realizan muchas más consultas repetitivas con un alto nivel de concurrencia, y ahí es donde dan sus frutos las décadas de optimización de consultas y la seguridad de los sistemas de bases de datos, que permiten que dichas consultas de los agentes se mantengan rápidas y seguras.
Además, las bases de datos siguen siendo muy relevantes hoy en día, y lo son especialmente para los agentes. Entre las conclusiones importantes que queremos compartir hoy con vosotros destaca que HCL Informix estará preparado para la IA. Así pues, podremos aplicar la IA allí donde ya se encuentran los datos.
Esto es posible gracias al tipo de datos vectorial nativo y a MCP. Además, por supuesto, analizaremos las interfaces de lenguaje que permiten «comunicarse» con los datos y veremos hacia dónde se dirige el futuro: herramientas de agente que, en definitiva, permiten trabajar de forma más sencilla con el sistema y contar con apoyo en la administración.
Bien, hablemos primero del Vector Blade. A modo de contexto, este patrón de los sistemas RAG (Retrieval Augmented Generation) es, en realidad, bastante conocido. No obstante, como breve introducción, me gustaría volver a abordarlo aquí.
¿Qué tenemos? Tenemos una gran cantidad de datos no estructurados. Pueden ser textos, imágenes o vídeos.
Desglosamos estos datos en fragmentos mediante un mecanismo de fragmentación y, a continuación, lo que ocurre es que los incrustamos en un espacio vectorial. Es decir, utilizamos un modelo de incrustación que, en principio, asigna estos fragmentos a un espacio de alta dimensión, y cada uno de estos fragmentos se convierte en un punto dentro de ese espacio. A continuación, almacenamos estos vectores, que describen precisamente esos puntos, en la base de datos —en nuestro caso, HCL Informix Vector Blade—, lo que nos permite, mediante una búsqueda por similitud, recuperar documentos y fragmentos relevantes, introducirlos en un modelo y, de este modo, obtener respuestas basadas en datos que, en realidad, no almacenamos.
También en los conocimientos propios de la empresa. Con HCL Informix Vector Blade, ahora disponemos de ello de forma nativa en Informix. Es decir, contamos con un componente integrado del servidor de base de datos.
Están justo al lado de los datos relacionales. Por lo tanto, no necesito ningún sistema adicional para gestionar mis representaciones y datos vectoriales, por lo que tampoco necesito ninguna conectividad adicional ni configuración de seguridad. Puedo consultar todo ello a través de la misma interfaz SQL a la que ya estoy acostumbrado.
Todo el sistema está aislado y es escalable. Hemos diseñado el Informix Vector Blade de tal forma que las rutinas que contiene se ejecutan en una clase VP independiente, denominada LVec. Esto significa que todas las operaciones y todos los subprocesos que se utilizan para el Vector Blade no afectan a los subprocesos existentes del sistema operativo de la base de datos.
Además, la fiabilidad, que sin duda necesitamos para un sistema de bases de datos de este tipo, no se ve afectada por ello. Funciona de forma aislada. Veamos brevemente cómo funciona.
Así pues, disponemos de documentos y datos de imágenes. Por ejemplo, ahora construiríamos el flujo de trabajo en la capa de aplicación que se encuentra por encima. Los documentos se dividen en fragmentos, se incorporan mediante un modelo de incrustación y se convierten en vectores, que a continuación se guardan además en el «Blade» de la base de datos.
Esta es una idea clave que debemos comprender. Informix y Vector Blade sirven para consultar y almacenar estos fragmentos o vectores. El resto se lleva a cabo en la capa de aplicación.
El resto suele depender de la aplicación concreta. Es decir: ¿cómo divido mis datos en bloques? ¿De qué datos dispongo realmente? Sin embargo, Informix, con Vector Blade, es capaz de gestionar la granularidad de los vectores.
Es decir, guardo mis vectores en la base de datos, en Vector Blade, y luego, cuando tengo una consulta —por poner un ejemplo: «¿Cómo fueron las cifras de ventas el año pasado?»—, A continuación, la pregunta se descompone potencialmente en fragmentos o se seleccionan palabras clave. Estas se procesan de nuevo mediante el mismo modelo de incrustación, de modo que obtengo un vector que describe mis preguntas y, a continuación, puedo realizar una búsqueda por similitud.
La búsqueda por similitud consiste en examinar mi espacio de alta dimensión y buscar los puntos de ese espacio que estén especialmente cerca de mi vector, el cual corresponde a mi pregunta. Así pues, puedo utilizar medidas de similitud en el espacio vectorial para ver qué fragmentos, qué vectores, se ajustan mejor a mi pregunta. A continuación, Vector Blade me devuelve esos fragmentos relevantes y puedo proporcionárselos como contexto a mi LLM para obtener así una respuesta a mi pregunta.
Y esta respuesta se refiere, por supuesto, al contexto que he obtenido del Vector Blade. Acabo de mencionar brevemente el aislamiento. Es algo relativamente importante.
Aquí vemos de nuevo todas las tareas de la base de datos que hay hasta ahora. Están separadas del Vector Blade. En el Blade puedo introducir vectores, consultar vectores e incluso modificarlos.
Es decir, también ofrecemos soporte para operaciones de modificación. ¿Cómo puedo empezar ahora mismo? Lo primero es instalar el blade en el directorio de Informix, dentro del directorio «Extend».
Traemos una imagen, un modelo de incrustación. Es decir, ya tenemos una función. Volveré sobre esto más adelante, cuando hable de las funciones importantes.
Contamos con una función que facilita la conversión de texto en representaciones vectoriales. Así pues, no es necesario que tengamos que desarrollar todo el sistema en la capa de aplicación. Ofrecemos un modelo de representaciones que simplemente permite empezar y probarlo fácilmente.
Y todo esto se basa en OpenAI; para utilizarlo, se puede configurar una clave de OpenAI a través de la variable de entorno, y esta se utiliza para llamar a un modelo de embedding. A continuación, puedo registrar el Blade en la base de datos; además, se incluyen un montón de ejemplos, por lo que puedo empezar a utilizarlo directamente. Me gustaría repasar las funciones más importantes que ya vienen predefinidas.
Lo que hay que entender ahora es que, en principio, el Vector Blade, al ejecutarse por separado, vuelve a almacenar las representaciones por separado. Existen las tablas que aquí se indican como «Lens Table Name» en el Vector Blade, y hay tablas en formato de tabla de Informix. Las representaciones se almacenan en formato de tabla de Informix, pero se pueden consultar a través de esta tabla «Lens».
Es decir, aquí puedo crear una tabla de vectores utilizando la función «L Vector Create Table». Lo que tengo que especificar es el número de dimensiones de mis vectores. Esto viene determinado simplemente por el modelo de incrustación que utilice.
Los modelos de incrustación tienen diferentes dimensiones de salida, normalmente 384, 768 o 1534. Estos son los modelos más habituales que existen. Cuanto mayor sea la dimensión, por supuesto, más precisas serán mis consultas y más datos diferentes podré representar, pero también mayor será el tiempo de ejecución de la incrustación y la recuperación.
He aquí un ejemplo: puedo crear una tabla de demostración sencilla con tres dimensiones. Del mismo modo, por supuesto, también puedo eliminar una tabla con un «Drop Table» indicando el nombre de la tabla. Y para introducir datos en ella, tengo, por un lado, la posibilidad de ejecutar lo que se denomina un «Sync Insert», indicando el nombre de la tabla, y así una tabla vectorial de este tipo siempre tiene el mismo esquema.
Hay un ID, hay una columna de vector y hay una columna de metadatos. En este ejemplo se ve bastante bien: el ID uno para este tuplo, luego el vector como cadena y los metadatos también pueden ser cualquier cadena. Por ejemplo, también podría crear un documento con formato JSON.
Del mismo modo, puedo volver a borrar esos tuplos mediante el ID, es decir, mediante un «Sync Delete». Y para que la recuperación de datos sea realmente rápida, existen las estructuras de índice. Las estructuras de índice son algo que siempre se ha utilizado en las bases de datos.
En principio, cambio la capacidad de actualización por la velocidad de recuperación o de consulta. Si tengo un índice, la consulta suele ser más rápida. Esto siempre ha sido así con los árboles B y demás.
Sin embargo, si añado nuevos tuplas, también tengo que actualizar el índice. Lo que puedo hacer aquí es indicar el nombre de la tabla, especificar el tipo de índice —sobre esto hablaré más adelante— y el número de particiones del índice. Se trata de un parámetro de optimización del rendimiento.
Y con esto, el índice se crea automáticamente en la columna vectorial. Y luego viene la función más importante: «L Vector Search». Esa es la que me permite implementar mi búsqueda.
Vuelvo a indicar el nombre de la tabla y el vector de búsqueda. Es decir, ese es el vector que obtengo cuando genero un vector a partir de mi consulta con un modelo de embedding; en la práctica, ese es el punto del espacio vectorial que estoy buscando. Y ahora Vector Blade realiza esta búsqueda de similitud en torno a ese punto: ¿cuáles son los puntos más cercanos?
Ahora puedo indicar aquí cuántos vecinos quiero tener. Esto también es un parámetro de ajuste y depende en cierta medida de la aplicación. Por lo general, se busca un contexto de entre tres y diez fragmentos, que luego se utilizarán como contexto para el modelo de lenguaje grande (LLM).
Esta es la función de incrustación a la que me acabo de referir, que permite calcular una incrustación de este tipo con relativa facilidad, siempre que disponga de una clave de OpenAI y la haya configurado en la variable de entorno. La llamada es muy sencilla: introduzco una cadena y la función devuelve un vector. Y lo mejor de todo es que también puedo combinar todo esto.
Así que puedo realizar una instrucción INSERT, especificar la tabla, indicar el ID y, en lugar del vector, puedo llamar a esta función de incrustación para calcular directamente la incrustación, sin tener que hacerlo manualmente. Hay otro grupo de funciones predefinidas. Existe una función de distancia, es decir, puedo calcular distancias manualmente.
En definitiva, eso es en lo que se basa la función de búsqueda. Puedo comparar vectores entre sí. Puedo realizar una búsqueda con un filtro adicional.
Eso es muy interesante. Es decir, puedo seguir realizando consultas sobre los metadatos de primitivas simples. Por ejemplo, si se incluye una palabra clave concreta o si se cumple una clave determinada.
Más adelante volveré a hablar de la importancia de la búsqueda que acabo de mencionar. Esto es especialmente importante en el ámbito empresarial. Normalmente realizo búsquedas bajo determinadas condiciones y no en toda mi base de datos.
Y luego está la operación de sincronización masiva, que me permite, a partir de mi tabla de Informix —ya sea que haya insertado vectores en ella o que la tabla ya exista—, sincronizar de una sola vez, en bloque, todos mis vectores con la tabla de vectores de Vector Blade. Hay una serie de tipos de índices compatibles. Si uno se fija… Si lee un poco sobre el «Vector Space», verá que son los tipos más habituales.
Existe un índice plano que, en realidad, realiza una búsqueda exacta. Es decir, con él consigo realmente una precisión del 100 %. Existe la cuantificación, que, en principio, comprime la representación vectorial.
Con esto suelo obtener resultados relativamente imprecisos, pero muy rápidamente. Y existe el HNSW, es decir, un índice de árbol, el «Hierarchical Navigable Small World Index». Al igual que un árbol típico, empieza con una granularidad gruesa, busca al primer vecino y, a partir de ese vecino, continúa y examina con una granularidad más fina.
Existen varios tipos de índices que se pueden evaluar. Como siempre, un índice supone, en principio, un equilibrio entre velocidad y precisión. Si necesito una precisión del 100 %, debería optar por el índice plano.
Sin embargo, si acepto que entre mis diez resultados haya también uno irrelevante, entonces también puedo utilizar un índice HNSW de este tipo.
Vale, pasemos a la demostración. He preparado dos cosas. Por un lado, he preparado todo lo necesario para que probemos todo esto de forma práctica en la consola.
Un momento. Vuelvo a compartir mi pantalla. Acercémonos un poco más.
Espero que se vea bien. Ahora volvemos a la base de datos y creamos una base de datos de prueba. Y lo que podemos hacer ahora es: podemos probar un poco con las funciones que ya están ahí.
Podemos probar una función de distancia entre dos vectores, 111 y... Y aquí, en principio, se puede especificar el tipo de función de distancia. Una distancia L2 es una simple distancia espacial. Es decir, calcula la diferencia de cada componente, eleva el resultado al cuadrado, suma todas las diferencias y, al final, extrae la raíz cuadrada; así me da una idea de cuán lejos están realmente los puntos en el espacio.
En este caso, 15. Y luego hay otras medidas de distancia, como, por ejemplo, la distancia coseno. Y aquí vemos ahora que la distancia es cero, ya que la distancia coseno no se basa en la distancia real, sino en la dirección.
Lo importante en este tipo de recuperaciones es que, en realidad, lo que importa es la dirección y no la longitud del vector. Vemos que 111 y 101010 apuntan exactamente en la misma dirección. Eso significa que, con toda probabilidad, según el modelo de incrustación —o tal y como están construidos los modelos de incrustación—, expresan lo mismo.
Es decir, en este caso la distancia es cero. Por lo tanto, son muy similares. La distancia coseno se suele utilizar para las búsquedas por similitud.
En realidad, L2-Distanz solo se utiliza para cálculos matemáticos y no tanto para esta búsqueda de similitudes. A modo de ejemplo, esto es lo que puedo hacer ahora: voy a crear una tabla de Informix. Se trata de una instrucción «Create Table» totalmente normal; tengo un ID, tengo una representación y tengo metadatos, y ahora puedo introducir una serie de valores.
Por un lado, tenemos cero con -100, -100, -100. Una cadena de ejemplo. Y de la misma forma, ahora también puedo insertar otros dos tuplas.
Y lo que puedo hacer ahora es utilizar esta función LVector para crear una nueva tabla en Vector Blade. Es decir, LVector Create Table, le pongo el nombre «uno» a la tabla, dimensión tres, ya que los vectores de esta tabla tienen dimensión tres. De hecho, deben ser exactamente los mismos que ya había introducido anteriormente en la tabla de Informix.
Y lo que hago ahora es utilizar la función «Bulk Sync», tal y como se ha explicado antes. Indico la tabla de Informix, indico la tabla de Vector-Blade, la columna «ID» y la columna «Vector». Y ahora la rutina me ayuda a sincronizar por una vez todos los vectores que ya están en la tabla de Informix con mi tabla de Vector-Blade.
Y lo que puedo hacer ahora es ejecutar una consulta SQL totalmente normal, y vamos a analizarla con más detalle. Tengo dos tablas aquí. Tengo la tabla de Informix y la tabla de Vector Blade.
Utiliza ahora la función de búsqueda con un vector de búsqueda, en este caso el 222. Ese es el que proviene del modelo de incrustación de mi pregunta. Digo que quiero tres vecinos y que se calcule según la distancia coseno.
Lo asigno al tipo «LVector Search Result». Se trata precisamente de esa estructura compuesta por un ID, un vector y un campo de metadatos. Y lo que tengo que hacer ahora es unir estas dos tablas.
Así que cojo el Vector ID de mi tabla de Informix y la columna ID de mi tabla Search de mi Vector Blade, las uno y ordeno el conjunto según la distancia resultante de la búsqueda por similitud. Si ahora ejecuto todo esto, veo que 222 era el más cercano a 101010 según la distancia coseno. Ese es exactamente el ejemplo que hemos visto antes.
La distancia es cero. Con respecto a 004 tenemos una distancia de 0,42 y, con respecto a -100,-100,-100, tenemos la distancia máxima en la distancia coseno, que es dos. Esto significa que están exactamente uno frente al otro y que apuntan exactamente en direcciones opuestas.
Tiene sentido. Así que esa es la construcción de la consulta que puedo hacer al final para calcular realmente una distancia vectorial mediante una única consulta SQL. Y ahora, por supuesto, tengo la posibilidad de utilizar todos los datos que hay en mi tabla de Informix; mediante este ID puedo obtener los valores vecinos y ahora puedo utilizar todas las columnas que tengo en mi tabla de Informix, ya sean datos de cadena adicionales o datos enteros, utilizarlas como contexto en mi LLM y así obtener el contexto que necesito para responder a mi pregunta.
Todo esto, como ya he dicho antes, se basa en gran parte en la lógica que, en realidad, se desarrolla en el nivel de la aplicación. Es decir, aquí también tenemos una pequeña aplicación de demostración en la que se puede mostrar el proceso de principio a fin. Vuelvo a compartir mi pantalla y todavía estoy buscando.
Se trata de una aplicación de demostración que hemos desarrollado internamente con fines de demostración. Lo que podemos hacer aquí es comprobar documentos. Es decir, puedo seleccionar documentos mediante la opción «Subir documento» y, a continuación, subirlos.
Y acabo de coger el boletín de Informix de la semana pasada, que me llegó a través de la lista de distribución de correo electrónico, y ahora puedo subirlo todo. Lo que hace ahora la aplicación es escanear el documento y dividirlo automáticamente en fragmentos lógicos. Aquí utilizamos la segmentación semántica, es decir, intenta dividir el texto en franjas que marquen límites lógicos del documento y, a continuación, utiliza un modelo de incrustación para incrustar el conjunto e insertarlo en Informix.
Ahora vemos aquí que todo ha funcionado. Hemos generado 29 fragmentos y también veo que el documento se ha cargado. Lo que puedo hacer ahora es plantear preguntas que se refieran específicamente a este documento.
Parte del boletín informativo se centró en la función Nullif. Es decir, siempre podemos preguntarnos qué hace realmente esta función. Y lo que vemos ahora aquí, en esta parte, es que en el registro de actividad se solicita todo el proceso y podemos comprobar aquí qué fragmentos se han utilizado realmente.
Y ahora vemos aquí, en este documento, el «Techtip Funktion Nullif», que es el fragmento que ha identificado como el más relevante, precisamente porque se ha incrustado en su totalidad y luego se ha detectado mediante la búsqueda por similitud. Ha utilizado este fragmento y otros más, y ha recurrido a GPT 4.0, indicándole que es un asistente que responde a preguntas sobre documentos; le ha adjuntado estos tres fragmentos y le ha preguntado qué hace la función «Nullif». Y esto es lo que obtenemos ahora como resultado: una visión general de la función «Nullif», su sintaxis, los distintos argumentos y, en principio, también explica lo que hace la función, es decir, devolver «null» cuando los argumentos son iguales.
Y en eso se centra ahora el primer argumento. ¿Hay que diseñar una aplicación de este tipo, por supuesto, de tal manera que se limite a responder únicamente a lo que pueda extraer de los fragmentos, del contexto? Al fin y al cabo, los modelos de lenguaje grandes (LLM) también cuentan con ciertos conocimientos básicos.
Es decir, si ahora preguntara algo que no figure en el documento, podría ser que, aun así, obtuviera una respuesta.
Pero, por otro lado, está, por supuesto, toda mi propiedad intelectual empresarial, todo mi conocimiento dentro de la empresa, que, por supuesto, no es de acceso público en ningún sitio, y puedo ponerlo a disposición de la IA mediante una aplicación de este tipo, y hacerlo de forma segura, si, por ejemplo, utilizo modelos de IA alojados por mí mismo, lo que me da la oportunidad de beneficiarme igualmente de la IA, sin tener que hacer públicos mis datos en ningún sitio. Vuelvo a compartir mi pantalla. ¿Se ha desconectado?
Vale, voy a hacer ahora un breve descanso y echaré un vistazo a las preguntas, porque, en principio, ya hemos terminado la parte dedicada a la Vector Plate. Después, queremos analizar un poco cómo encaja todo esto en el panorama general de la IA. Dr.
Aspiazu pregunta: «¿La IA hará que, en el futuro, los sistemas de bases de datos como Informix dejen de ser necesarios?». Yo creo que no. Creo que necesitaremos aún más la consistencia y la fiabilidad de los sistemas de bases de datos, pero también la posibilidad de trabajar con datos precisamente en «Asset Properties», en el apartado «High Concurrency». Es decir, los agentes utilizarán bases de datos y necesitamos precisamente estas características, que ya se han optimizado e investigado durante décadas.
Precisamente eso es lo que vamos a necesitar. Creo que ese papel cobrará aún más importancia y seguirá siendo la base de datos fiable. ¿Indica la distancia coseno la dirección de un vector?
La dirección de un vector la indica el propio vector, es decir, en principio, la combinación de todas las dimensiones. La distancia coseno indica el ángulo entre ambos vectores. Por lo tanto, el coseno del ángulo es el resultado de la distancia coseno.
Así pues, si nos imaginamos que un vector apunta hacia aquí y otro vector apunta hacia allá, la distancia coseno es exactamente el coseno del ángulo que hay entre ambos. Es decir, un vector que apunta así y otro que apunta así, pero que es mucho más largo —como acabamos de ver—, tienen una distancia coseno igual a cero, porque el ángulo entre ellos simplemente no existe. ¿Cuánto espacio ocupa en los «Spaces» solo este ejemplo del boletín?
No puedo responderle a eso así, sin más. Tendría que volver a consultarlo. Señor Aspeato, usted aún tiene la mano levantada.
¿Quiere hacer alguna pregunta más? De acuerdo. Yo seguiría primero con el tema.
Estaremos encantados de responder a cualquier pregunta al final. Si ahora estáis planeando, en el ámbito empresarial, desarrollar o implementar este tipo de aplicaciones, la pregunta que siempre surge es, naturalmente: ¿puedo utilizar la base de datos que ya tengo o necesito una base de datos vectorial adicional? Y siempre creo que es importante destacar esta diferencia.
Una base de datos multimodal, como HCL Informix, resulta especialmente adecuada cuando ya se dispone de una base de datos relacional y se quiere probarla, se quiere desarrollar este tipo de aplicaciones; por supuesto, tiene la huella de un sistema de bases de datos completo. Todo el sistema está optimizado para cargas de trabajo relacionales. Esto significa que no se obtendrá un rendimiento de recuperación de datos en milisegundos.
Uno se acerca bastante, pero una gran ventaja es que se almacena todo de forma unificada junto con las propiedades de los activos. Es decir, se almacenan conjuntamente los datos vectoriales y los datos relacionales, y, por supuesto, se cuenta con la ventaja de que se trata de un único sistema que hay que mantener. Si ahora se añadiera además una base de datos vectorial independiente —hay varias bases de datos en el mercado—, entonces esta base de datos tendría, naturalmente, un espacio de almacenamiento relativamente reducido.
También puedo implementarlo sin problemas en cualquier dispositivo periférico y obtengo la velocidad óptima para la búsqueda vectorial, pero luego, durante el funcionamiento, debo tener en cuenta que la consistencia es distribuida. Es decir, si parto de la base de que voy a insertar un tuple en mi base de datos relacional y, al mismo tiempo, voy a insertar la incrustación en la base de datos vectorial, entonces, naturalmente, tengo que asegurarme de que se haga a través de «Asset Properties», es decir, o todo o nada. De lo contrario, uno de los dos sistemas quedará desincronizado.
Y, por supuesto, también tengo que tener en cuenta el mantenimiento y la administración de una implementación de este tipo, que queda distribuida. Esto es mucho más complejo que un sistema unificado. Y lo que ya he mencionado antes es la búsqueda híbrida, que es muy importante, porque se necesita en las aplicaciones prácticas.
Y es que se trata precisamente de la combinación de la búsqueda por similitud y la búsqueda basada en palabras clave o en filtros relacionales, que ya conocemos. He aquí un ejemplo sencillo: tengo dos documentos, «Máquina A» y «Máquina B». Una de ellas puedo apagarla desenchufándola, mientras que la otra debería apagarla correctamente a través de la interfaz.
Si ahora, como usuario, planteo la pregunta: «¿Cómo se apaga la máquina B?», puede suceder perfectamente, dependiendo de cómo esté diseñada mi aplicación, que obtenga ambos fragmentos, porque, naturalmente, ambos coinciden, y la IA me dirá entonces que puedo desconectar el enchufe o utilizar la interfaz, ya que, lógicamente, solo puede generar la respuesta a partir del contexto que recibe. Si ahora utilizo la búsqueda híbrida, puedo, por supuesto, descartar el documento de la máquina A mediante la búsqueda por palabras clave, obtener solo el documento de la máquina B como contexto y, con ello, mi LLM también podrá responder correctamente. Es decir, esa es una gran ventaja de sistemas como Informix con Vector Blade.
Puedo seguir utilizando la búsqueda relacional y ampliarla mediante la búsqueda por similitud. En nuestra consulta anterior, también podría haber especificado que solo quería que mi conjunto de resultados incluyera los ID menores que tres o menores que dos. Bien, ¿cómo encaja todo esto ahora en el panorama general y qué hacemos en HCL y Actian para plantearnos todo ello en un contexto más amplio?
El primer punto que quiero abordar es el «Model Context Protocol», que seguramente muchos de vosotros ya conocéis. La razón de ser de este protocolo es sencilla: los modelos de lenguaje grande (LLM) tienen un conocimiento limitado. Los modelos de lenguaje grande tienen un momento de entrenamiento.
A partir de ese momento, en principio, ya no disponen de nuevos conocimientos. Por supuesto, los modelos que utilizamos ahora a través de los servicios de OpenAI o similares tienen acceso a nuevos datos, a Internet y a información en tiempo real, pero queremos que el sistema tenga acceso a nuestros datos en tiempo real dentro de la empresa y también queremos darles la posibilidad de llevar a cabo acciones. Y el problema fundamental ahora es: ¿cómo puedo conectar esos LLM aislados con el mundo exterior y cómo puedo gestionar el contexto?
Y lo que, por supuesto, podría hacer ahora mismo es: podría crear una aplicación y, en ella, integrar el controlador correspondiente a cada herramienta que quiera incorporar. Es decir, el cliente JDBC de mi base de datos, una API de mi servidor de correo electrónico. O cosas por el estilo.
Esto significa que cada una de mis aplicaciones tendría que conocer todas mis API de herramientas. Así que estoy creando n por m integraciones. En realidad, eso es imposible de gestionar y no se puede escalar.
La solución pasa por un lenguaje común para esta capa, y esa es precisamente la motivación que subyace al Model Context Protocol. Se dice que es el conector USB-C de las aplicaciones de IA. Se trata de un estándar de código abierto que Anthropic ha cedido ahora a la Linux Foundation, por lo que es realmente el estándar de facto y que, precisamente, mapea las API específicas de cada herramienta a la API genérica del MCP.
Al hecho de que cada proveedor ponga a disposición un servidor MCP, puedo conectar con él cualquier agente y cualquier LLM. Los agentes tienen la posibilidad —esto forma parte del estándar MCP— de detectar dinámicamente las herramientas que se proporcionan a través del servidor. Es decir, puedo iniciar un agente, y este puede comprobar qué herramientas están disponibles en el servidor MCP y, a continuación, seleccionar las herramientas adecuadas para una tarea concreta, por ejemplo, ejecutar una consulta SQL o modificar un parámetro de configuración.
También hemos puesto en marcha un servidor MCP para HCL Informix. Este permite a los agentes tomar decisiones basadas en los datos reales del sistema, lo que reduce las «alucinaciones». Proporciona acceso tanto a los datos como a los metadatos.
Es decir, con ello permitimos un acceso muy detallado al esquema, a la lógica de negocio y, por tanto, también a las procedimientos y a los propios datos. Hay funciones, hay esquemas y hay «Read SQL», que es una sencilla consulta de lectura de los datos. El camino que seguimos consiste en convertir a los agentes pasivos en usuarios activos.
Es decir, vamos a integrar herramientas de edición y administración en este servidor MCP, que permite precisamente que los agentes puedan introducir datos, modificarlos y realizar tareas administrativas en el sistema de bases de datos. Volveremos sobre este tema más adelante, cuando analicemos la hoja de ruta de los agentes. Por supuesto, hay que tomar esto con cautela.
Hay que tener mucho cuidado y, en ese sentido, también es importante contar con MCP como capa de seguridad entre el mundo de los agentes y el mundo de las bases de datos. Y es que solo ese es el nivel de abstracción que realmente permite, mediante mecanismos de acceso y de gobernanza muy granulares, así como las denominadas «guard rails», limitar lo que el agente puede hacer realmente y estar realmente seguro de que no se alteran los sistemas durante su funcionamiento operativo. El segundo punto que hemos esbozado antes en la introducción son los análisis orientados al diálogo.
Una afirmación muy sencilla: cada cifra de la empresa tiene una historia y eso es, precisamente, lo que al final importa. Cuando hago una pregunta o miro un panel de control, yo mismo aporto un contexto determinado para poder situar esa cifra. Si se trata del número de clientes, ¿coincide con lo que supongo o algo así?
Por supuesto, un agente no dispone de ese contexto. Es decir, si le pregunto a un agente: «¿Cuánto hemos ganado el último trimestre?», el agente puede dar cualquiera de estas cuatro respuestas y, en realidad, cualquiera de ellas sería correcta. Pero solo para determinadas personas de la empresa.
Por lo tanto, es importante proporcionar contexto a un agente para este tipo de análisis. Y ahí es precisamente donde radica el problema. Y es que los modelos de lenguaje grande (LLM) son probabilísticos.
Es decir, puedo formular la misma consulta dos veces y obtener dos respuestas diferentes, porque el agente, basándose en mi esquema de base de datos, ha decidido formular una consulta distinta. Esa respuesta puede ser igual de correcta, pero no en el contexto en el que la solicito, ya que, por ejemplo, pertenezco al departamento financiero. El problema es que los grandes modelos de lenguaje son muy flexibles, pero, como ya he dicho, impredecibles.
Y, por supuesto, el análisis clásico es fiable. Conocemos los paneles de control y la BI clásica, pero son muy rígidos. Es decir, si hoy hago una consulta y quiero saber cuáles han sido nuestras cifras de negocio, es posible que primero tenga que preguntar al equipo de BI, que tendrá que crear primero un panel de control.
Quizá reciba la respuesta a finales de esta semana. La solución que hemos desarrollado para ello es Actian AI Analyst, que combina las ventajas de ambas opciones.
Es flexible y fiable. Se trata, en principio, de un agente de lenguaje natural que me ofrece respuestas fiables y reproducibles, precisamente porque está bien fundamentado en el contexto empresarial. Breve resumen de esta solución que hemos desarrollado.
Podemos realizar consultas empresariales en lenguaje natural y obtenemos análisis al instante. Sin paneles de control, nadie tiene que escribir consultas SQL. Esto significa que todo el mundo, incluso las personas sin conocimientos técnicos, puede utilizar el sistema para obtener información sobre datos de HCL o Informix, por ejemplo.
El analista de IA se basa en un modelo semántico. Este modelo incluye todas las definiciones empresariales y todas las métricas que yo haya publicado o que haya publicado cualquier otra persona. Es decir, este modelo semántico describe, en principio, mi conjunto de datos y mi base de datos.
Cada respuesta muestra además claramente cómo se ha llegado al resultado. Es decir, se pueden ver todos los pasos de razonamiento que ha seguido el agente para realizar la consulta. Se ve la consulta que se ha realizado y se integra fácilmente en las herramientas con las que los equipos trabajan habitualmente: en Slack, en Teams y en el navegador web.
Lo interesante es que puedo ponerlo en marcha en tan solo unas horas y obtengo respuestas a mis resultados en menos de un minuto. Una breve explicación de cómo funciona todo esto. ¿Cómo se consigue que este tipo de consultas sean realmente repetibles y correctas?
Aceptamos las solicitudes comerciales que recibimos. Dejamos que el agente actúe. El agente identifica la intención, describe o crea una plantilla de solicitud, pero no está autorizado a realizar una consulta SQL definitiva.
A continuación, esta plantilla se procesa a través de esta capa semántica, que contiene toda la información empresarial, y allí los marcadores de posición se sustituyen por las definiciones reales. Es decir, si pregunto: «¿Cuántos clientes activos tenemos?», el marcador de posición «cliente activo» se sustituye por la definición real. La consulta se ejecuta entonces en modo lectura.
Así pues, los datos permanecen en la base de datos. Solo obtengo el resultado y, al final, recibo una respuesta fiable. Lo que, por supuesto, se podría argumentar ahora es que, en principio, eso traslada el problema a la creación de esa capa semántica como verdadera fuente de verdad.
Y para eso también contamos con un agente, el «Steward Agent», que crea, mantiene y supervisa esta capa semántica. Este agente toma, por tanto, la documentación, las páginas de Confluence y cualquier otro lugar donde se explique mi lógica de negocio, las integra en la capa semántica y cuenta con un bucle de retroalimentación, de modo que cada vez que realizo una consulta y mantengo una conversación con el agente, este extrae información de ella y mejora dicha capa semántica con el paso del tiempo. El AI Analyst es una forma de comunicarnos con Informix utilizando, en definitiva, un lenguaje realmente natural.
Y, como último punto de hoy, echemos un vistazo de nuevo a hacia dónde se dirige el campo de la IA agentiva. En primer lugar, veamos muy brevemente la diferencia entre la IA agentiva y las aplicaciones habituales. Como ya he dicho al principio: uno de los aspectos más importantes de la IA agentiva es que todo está orientado a objetivos.
El usuario aún puede especificar el destino, pero ya no tiene por qué saber cómo funciona el proceso. Como en el ejemplo anterior: puedo formular una pregunta, puedo especificar el destino y qué respuesta quiero obtener. Sin embargo, ya no tengo que ser capaz de formular la consulta SQL correspondiente.
Los agentes cuentan con una fase de planificación. Esto significa que desglosan las tareas, planifican de forma autónoma y disponen de un ciclo de retroalimentación que les permite ajustar el conjunto durante la ejecución, en función del éxito de los primeros pasos. Cuentan con un contexto y una memoria.
Es decir, si ya he realizado ciertas tareas anteriormente, el sistema puede memorizarlo todo, tanto en la memoria a corto plazo como en la memoria a largo plazo, para ir mejorando progresivamente. Esto significa que, con el tiempo, un agente puede convertirse en mi asistente personal, ya que sabe cuáles son mis preferencias, cómo realizo mis tareas, etcétera. Y aquí es donde entra en juego MCP.
Los agentes pueden utilizar herramientas. Es decir, un agente puede acceder de forma autónoma a sistemas externos a través del MCP, con los mecanismos de seguridad del servidor MCP y las restricciones y los mecanismos de seguridad correspondientes. Y ahora podemos reflexionar sobre ello.
Esto puede servir de inspiración si desea crear diferentes agentes en la empresa utilizando el servidor MCP e Informix. De hecho, este tipo de casos de uso se dan a lo largo de todo el ciclo de vida de los datos. En la fase de captura de datos, se podría decir que los agentes actúan, en realidad, como operadores.
Es decir, puedo contar con agentes para la recopilación de datos. Puedo llamar por teléfono a mi agente y decirle: «Acabo de salir de una reunión con un cliente. Esta es la información que he recabado».
Por favor, añádelo a mi base de datos de clientes potenciales. Puedo crear un agente de migración de datos que transfiera automáticamente los datos de una base de datos a mi base de datos interna. En lo que respecta a la gestión de datos, se podría decir que los agentes actúan, en realidad, como curadores.
Es decir, puedo comprobar la calidad de los datos y enriquecerlos automáticamente. Puedo rastrear el origen de los datos. Esto es muy importante, sobre todo en la era de la IA: ¿de dónde proceden mis mediciones?
¿De dónde proceden mis datos? En cuanto al uso de los datos, como acabamos de ver, los agentes actúan, en realidad, como analistas. Es decir, por supuesto que puedo formular consultas verbales.
Pero también puedo crear un agente para la elaboración de paneles de control o incluso para consultas multimodales. Es decir, mi agente podría consultar una combinación de documentos, correos electrónicos, imágenes y datos relacionales y, a partir de ahí, generar una respuesta. Las posibilidades son muchas.
Y, por supuesto, uno de los principales puntos débiles: la administración de bases de datos. Los agentes actúan como administradores. Pueden ayudar o incluso encargarse de determinadas tareas; por ejemplo, un agente de estado del sistema que comprueba mi sistema las 24 horas del día y que ya avisa de forma proactiva de los problemas o que incluso los resuelve.
O el ajuste de bases de datos. Como todos sabemos, un sistema tiene cientos de parámetros de ajuste. El esquema, la configuración… todo eso se puede ajustar, y un agente puede ayudar a adaptar el conjunto a la carga de trabajo.
Todo esto sirve de inspiración para ver qué se puede llevar a la práctica. ¿Cómo podría ser algo así, lo que se suele hacer? Aquí tenemos nuestra base de datos de usuarios.
Por supuesto, contamos con un servidor MCP para la base de datos de usuarios, de modo que el agente también pueda acceder a ella de forma segura. Y, de alguna manera, dispongo de datos de telemetría o registros que extraigo del sistema. Y lo que debería hacer al desarrollar una aplicación de este tipo: debería guardar todo ello en una base de datos de métricas, para no interferir en el funcionamiento operativo del sistema cuando consulte estas métricas mediante agentes.
El agente tiene ahora acceso a los datos, a los registros y a los datos de telemetría. Esto significa que, por ejemplo, también sabe cuál es el consumo de memoria y, gracias a ello, ahora puede tomar decisiones. Lo que necesita un agente de este tipo para el razonamiento es un modelo de lenguaje grande.
Puedo implementarlo ahora de forma relativamente sencilla, enviando todo el sistema a OpenAI o a Tropic con un modelo alojado en un servidor público. O bien, si prefiero mantener el control, puedo utilizar un modelo privado que aloje internamente en mis propias instalaciones. En cualquier caso, lo que se debería hacer es intercalar una capa proxy que abstraiga todo el proceso y, además, implemente medidas de seguridad adicionales.
Bien explicado: eso es precisamente lo que tenemos previsto hacer cuando creemos agentes preconfigurados, ahora para todos nuestros sistemas de bases de datos. Intercalaremos una capa proxy de este tipo. Cuenta con un entorno de ejecución para modelos locales, se puede conectar opcionalmente a un modelo público y, precisamente, dispone de todas esas medidas de seguridad que son importantes para las empresas.
Así pues, esa es la capa de control entre mi agente y los modelos públicos, por ejemplo, en materia de seguridad y cifrado, pero también de auditoría. Es decir, cada solicitud debe registrarse. Me gustaría ver cuántos tokens se consumen.
Puedo gestionar el presupuesto e identificar y ocultar los datos personales antes de que lleguen realmente a los modelos públicos. Y, en cuanto al calendario: lo que ya publicamos en abril fue la versión 1 del servidor MCP. Se trataba de un inicio seguro con autenticación y acceso de solo lectura.
Hace una semana y media o dos lanzamos la versión 1.1. Con ella ya se puede empezar a utilizar el sistema de forma operativa. Es decir, ya cuenta con las primeras medidas de seguridad.
Contamos con funciones de registro y permitimos operaciones de escritura con «Human in the Loop»; además, todo ello se ampliará aún más en la próxima versión mediante herramientas de administración y diversas funciones de orquestación. Paralelamente, estamos trabajando en los agentes. Es decir, los agentes para la administración de bases de datos estarán disponibles en Early Access hacia finales de año.
Lo que nos parece importante en este sentido, y ya lo he mencionado brevemente antes, es que estamos hablando de los sistemas que, en principio, constituyen el corazón de todas nuestras empresas. No se debe trabajar con agentes en ellos a la ligera y arriesgarse a que se pierdan datos, a que los sistemas se vean comprometidos, etcétera. Es decir, anteponemos la fiabilidad al bombo publicitario.
Queremos crecer de forma segura. Es decir, queremos ir habilitando poco a poco los casos de uso con la seguridad que necesitamos para hacerlo de forma realmente fiable. Queremos tener un control total sobre el uso de la IA y, sobre todo, soberanía.
Es decir, siempre es posible alojar modelos propios y mantener el control total para cualquiera que desee ser independiente de los modelos globales. Intentamos que todo ello interfiera lo menos posible en el entorno de producción. Como acabo de decir, en realidad no es aceptable que estas aplicaciones perturben el funcionamiento operativo, y la autonomía se podrá configurar.
Es decir, un agente de este tipo puede hacer propuestas que yo, como usuario, pongo en práctica por mi cuenta. Mediante el enfoque «Human in the Loop», puede hacer propuestas que yo solo tengo que aprobar, o bien los sistemas pueden funcionar de forma autónoma. Esa es, en cierto modo, la hoja de ruta que marca el rumbo que queremos seguir y hacia donde queremos avanzar con cautela, pero con paso firme.
Bien, a modo de resumen, estas son las ideas más importantes de hoy: HCL Informix se prepara para la IA. La IA está presente allí donde ya están los datos, ya que contamos con el tipo de datos vectorial nativo y con MCP. Hemos visto que se puede «hablar» con los datos y que existen soluciones para permitir el acceso mediante lenguaje natural.
Y ya hemos visto lo que nos depara el futuro. Las herramientas de agente simplificarán y mejorarán el trabajo, tanto la forma en que utilizamos nuestros sistemas como la forma en que los configuramos y los administramos. Bien, y con esto, la conclusión: HCL Informix está preparado para sus casos de uso de IA y estaremos encantados de seguir debatiendo el tema y respondiendo a sus preguntas.
De acuerdo. ¿Podrían obtener resultados diferentes distintas personas con sus consultas en lenguaje natural y otras similares? Exacto, eso puede pasar.
Ese es precisamente el punto que he señalado en la diapositiva con las diferentes respuestas. Este analista de IA conoce mi función como usuario. Puedo crear diferentes usuarios con distintas funciones.
Así pues, el sistema sabe que pertenezco al departamento financiero, cómo defino, por ejemplo, a un cliente activo en mi departamento y, con la misma consulta, otra persona puede obtener un resultado diferente según la definición de métrica que sea la correcta en ese contexto. Todo ello se basa en los roles y, por lo tanto, siempre obtengo el contexto adecuado. ¿Se comparten la memoria a corto y a largo plazo entre los distintos usuarios?
No, es muy importante, porque eso, claro, también puede afectar a los mecanismos de almacenamiento en caché, al almacenamiento de consultas en caché; eso es, sin duda, una vía de acceso muy importante para las fugas o los problemas de seguridad. No, no se divide en partes. ¿Son intercambiables la memoria a corto plazo y el conocimiento a largo plazo?
¿Qué se registra en la auditoría y cuál es el volumen de datos necesario? ¿Dónde se almacenan los datos de auditoría, fuera de la instancia o en el sistema de archivos? Ahora hay dos contextos para ello.
También puedo configurar la memoria a corto plazo y el conocimiento a largo plazo. Puedo personalizar un mensaje del sistema en el contexto del AI Analyst. Con ello, puedo configurar el comportamiento de mi agente y puedo ver lo que tiene almacenado actualmente en su memoria, así como configurarlo.
¿Qué se registra durante la auditoría y cuál es el volumen de datos necesario? Actualmente, todo se registra en una base de datos en nuestras instalaciones. Por supuesto, si ahora se crea un agente genérico, como el de esta arquitectura de referencia que acabo de mostrar, se puede conectar cualquier sistema.
Normalmente se trata de un sistema de base de datos operativo o un sistema NoSQL, en el que se vuelcan estos registros con mucha frecuencia. Y el volumen de datos necesario para ello depende, por supuesto, del tiempo que, por normativa, tenga que mantener la retención de datos. Es decir, si desarrollo un sistema y tengo que mantener todo auditable durante 30 días, entonces tendré que conservarlo durante ese tiempo.
Después, probablemente pueda archivarlo. Y otra pregunta más: ¿también si los roles son los mismos? No, si tengo el mismo rol, puedo diseñar a través de este sistema.
Esa es precisamente la diferencia entre un LLM puro —que puede darme diferentes respuestas a la misma consulta— y nuestro sistema, que resuelve precisamente eso mediante esta traducción por pasos, mediante esta traducción en dos fases. Vale, se nos ha acabado el tiempo. No sé por qué he salido un momento.
En cualquier caso, le agradezco su atención y me encantaría que volviéramos a hablar con motivo de algún evento o, si lo prefiere, por correo electrónico, en caso de que surjan preguntas de seguimiento. Y con esto me despido y le deseo una buena noche. Gracias por sus comentarios.