
Esto es parte de una serie en curso: vea la primera y la segunda publicación.
Una monstruosidad espantosa. Cualquier ingeniero experimentado ha visto una: código tan vasto, arriesgado y difícil de entender que nadie se atreve a tocarlo. No hay pruebas unitarias; cualquier cambio es motivo de un pequeño infarto. Los únicos que se atreven a acercarse son los veteranos, aquellos que ya estaban presentes cuando se creó el monstruo, y solo se acercan cuando no hay alternativa. Es obsoleto, no está modularizado y las dependencias están desactualizadas. El componente es demasiado peligroso para modificarlo seriamente.
Recuerdo la primera monstruosidad con la que me topé. Una función de 5000 líneas, fundamental para las operaciones de un negocio que valía cientos de millones de dólares; casi nadie se atrevía a tocarla. Cuando se rompió, equipos enteros se despertaron en plena noche. Todo el desarrollo de la empresa se ralentizó debido a la dependencia de este componente clave. Se gastaron millones de dólares intentando gestionar el monstruo.
¿Qué tiene todo esto que ver con las indicaciones de LLM? ¡También pueden volverse monstruosas! Es tan aterrador cambiarlas que nadie las toca. O, por el contrario, los equipos intentan corregirlas y provocan una avalancha de incidentes.
Los clientes no quieren pagar por software que solo funciona correctamente los martes y jueves; exigen fiabilidad constante y un flujo constante de nuevas funciones. Al desarrollar sistemas de alta fiabilidad a largo plazo, es fundamental permitir que la aplicación evolucione, manteniendo el sistema en funcionamiento constante. Esto aplica tanto a las aplicaciones impulsadas por IA de generación como al software tradicional.
¿Cómo lograr una aplicación con IA robusta y no una monstruosidad? Esta serie abarca más de una docena de enfoques. Todos parten de un principio: en lugar de una instrucción enorme, se buscan múltiples instrucciones más pequeñas y enfocadas, cada una con el objetivo de resolver un único problema.
La modularización consiste en descomponer un sistema complejo en componentes más pequeños, autónomos y reutilizables. En la ingeniería de software tradicional, esto implica escribir funciones, clases y servicios que realizan una tarea específica. En el contexto de la ingeniería de indicadores para LLM, la modularización implica dividir un indicador grande y monolítico en indicadores más pequeños y específicos, cada uno diseñado para realizar una tarea única y bien definida.
La modularización permite introducir cambios en el sistema de forma segura a lo largo del tiempo. Su importancia aumenta cuando:
Es necesario comprender todas estas dimensiones al planificar el sistema.
Pero, ¿cómo contribuye específicamente la modularización al mantenimiento del sistema? Los principales beneficios se describen a continuación.
El rendimiento de los prompts LLM es inherentemente inestable. Su naturaleza es tal que cualquier cambio puede afectar la salida de forma impredecible. Este riesgo se puede gestionar dividiendo los prompts grandes en componentes, donde un cambio solo puede afectar el rendimiento de una parte del sistema. Incluso si un prompt falla, el resto del sistema funcionará como antes del cambio.
Pero ¿qué ocurre si las indicaciones funcionan como una cadena? ¿Acaso romper un componente no rompería la cadena? Sí, sí, pero el daño se reduce en este escenario. Una salida errónea en una cadena de indicaciones puede proporcionar entradas defectuosas a las indicaciones posteriores, pero cada componente seguiría funcionando como antes del cambio en el conjunto de entradas válidas. Comparemos esto con la alteración de una indicación enorme: el cambio puede (¡y lo hará!) afectar a toda la lógica codificada en esa indicación. No se ha dañado un aspecto del sistema, sino potencialmente todas sus partes.
(Operar cadenas de indicaciones de forma segura es un capítulo futuro de la serie. Es necesario planificar para diversos tipos de fallos y tener planes de contingencia. Pero esto queda fuera del alcance de este artículo).
Cualquiera que haya escrito pruebas unitarias sabe que una función simple que realiza una sola función es mucho más fácil de probar que una función compleja que intenta realizar muchas cosas diferentes. Lo mismo ocurre con las indicaciones: una indicación pequeña y específica se puede probar con mucha más profundidad, tanto de forma manual como automatizada.
Un amplio conjunto de evidencia muestra que las indicaciones más cortas tienden a tener mejor rendimiento que las más largas: 1 , 2 , 3 .
La investigación sobre los efectos de la multitarea en el rendimiento de las indicaciones es más heterogénea: 4 , 5. Una indicación perfectamente optimizada puede, en las circunstancias adecuadas, realizar múltiples tareas. Sin embargo, en la práctica, es mucho más fácil optimizar las indicaciones enfocadas, con las que se puede realizar un seguimiento del rendimiento en una única dimensión principal. Se recomienda buscar indicaciones más enfocadas siempre que sea posible.
Explicar las complejidades de un tema superrápido con 3 mil palabras a un nuevo miembro del equipo es todo un reto. Y por mucho que se explique, los únicos que comprenderán esta bestia serán los autores colaboradores.
Se puede implementar mucho más rápido un sistema de indicaciones en el que cada parte sea relativamente simple; los ingenieros comenzarán a ser productivos antes.
Al utilizar diferentes modelos en distintas partes del sistema, puede lograr ahorros significativos en costos y latencia sin afectar la calidad de la respuesta.
Por ejemplo, un indicador que determina el idioma de entrada no tiene que ser especialmente inteligente: no requiere el modelo más reciente y costoso. Por otro lado, el indicador que genera la respuesta basándose en la documentación podría beneficiarse de un razonamiento en cadena de pensamiento integrado en modelos de alta gama.
La mayoría de las aplicaciones basadas en software requieren la adición segura de funciones durante largos periodos de tiempo. Sin embargo, existe una excepción. Las aplicaciones prototipo no están diseñadas para un mantenimiento prolongado; no incorporarán nuevas funciones ni están diseñadas para una alta fiabilidad. Por lo tanto, no pierda tiempo con la modularización al crear prototipos. De hecho, la mayoría de los patrones de esta serie no son aplicables a las aplicaciones prototipo. Al crear un prototipo, avance rápido, verifique las incógnitas críticas y luego descarte el código.
Otra consideración es saber cuándo detener la modularización. Gestionar indicaciones adicionales implica una sobrecarga, y si los beneficios de una mayor modularización son bajos, se debe dejar de fragmentar el sistema.
Si modularizar los prompts fuera trivial, todos lo harían. Para gestionar muchos prompts en un sistema, es necesario invertir en infraestructura; sin ella, se generará un caos. Estos son los requisitos mínimos para la infraestructura de prompts del LLM:
Capacidad para agregar indicaciones de forma rápida, sencilla y estandarizada. Esto es especialmente importante cuando las indicaciones se cargan desde fuera del código base. Consulte el Principio II: Cargar indicaciones de forma segura (si es realmente necesario) .
Capacidad de implementar indicaciones de forma automatizada.
Capacidad de registrar y supervisar entradas/salidas de indicaciones individuales.
Capacidad de agregar pruebas automatizadas que cubran indicaciones.
Una forma de realizar un seguimiento fácil del gasto de tokens/$ en varias indicaciones.
Veamos cómo funciona en la práctica la construcción de un sistema impulsado por Gen AI con y sin modularización.
Estás desarrollando una aplicación de soporte técnico y estás decidido a implementarla con un solo mensaje. En la versión más simple, puedes imaginar un mensaje monolítico que genere respuestas mientras carga la documentación relevante a través de RAG .
Parece fácil y atractivo, ¿verdad? Pero al añadir funciones, surgen problemas con esta arquitectura:
Quiere responder mensajes en una lista fija de idiomas, pero no gestionar otros. Para lograrlo, agregue instrucciones rápidas para responder solo en ciertos idiomas y haga que el LLM devuelva el campo "idioma" para fines de informes.
Quiere clasificar todas las conversaciones. Añada el campo "etiqueta" a la salida del mensaje.
Si el usuario no está satisfecho, escala el caso a soporte técnico. Añade la variable de salida "escalate_to_human" junto con las instrucciones en el mensaje.
Se necesita una traducción de todos los mensajes enviados para auditoría interna. Devuelva el campo "traducido" con un mensaje en inglés.
Se necesita protección para garantizar que la aplicación nunca pregunte a los usuarios sobre su ubicación ni por quién votaron en las últimas elecciones. Agregue instrucciones rápidas y pruébelo manualmente.
¿Necesitas un resumen de cada conversación? Añade el campo "resumen" a cada salida.
Quizás estés empezando a ver el problema: este mensaje ahora tiene seis salidas. Probarlo será un caos. Agregas compatibilidad con otro idioma y, de repente, tu aplicación empieza a mostrar el resumen en español en lugar de inglés. ¿Por qué? Quién sabe, las salidas de LLM son inestables, así que cambiar el mensaje tiene resultados impredecibles.
¡Felicidades! ¡Has creado un monstruo! Con el tiempo, crecerá y causará aún más dolor.
Se utiliza una cadena de indicaciones y una indicación de clasificación completamente independiente. La indicación original, de gran tamaño, se ha modularizado hasta el punto de ser práctica.
Un mensaje detecta el idioma, otro proporciona la traducción, otro determina si el usuario está molesto y escala el problema a los humanos, el mensaje de respuesta genera la respuesta y la barrera de seguridad verifica el cumplimiento de la respuesta. Las salidas de un mensaje se encadenan para ser las entradas del siguiente; el código tradicional puede operar entre estos mensajes para, por ejemplo, verificar la elegibilidad del idioma, sin involucrar a los LLM.
Un cambio todavía puede romper un mensaje determinado, pero los riesgos se reducen enormemente porque:
Obtendrás todos los beneficios de la IA Gen, pero los riesgos se reducen considerablemente. Además, puedes usar modelos más económicos para algunos componentes y ahorrar dinero.
La modularización permite aislar errores, mejorar la mantenibilidad y construir un sistema más fiable. Incluso las aplicaciones de tamaño moderado tendrán docenas, si no cientos, de indicaciones de componentes. Divida las indicaciones hasta que cada una realice una sola tarea y hasta que los beneficios de una mayor modularización se vean compensados por la mayor complejidad operativa. Modularizar las indicaciones es fundamental para que sus aplicaciones basadas en IA sigan siendo fiables y sigan añadiendo funciones a largo plazo. Ya existen muchos sistemas "monstruo", ¡así que tenga cuidado de no crear nuevos!
Si te ha gustado esta serie, suscríbete para recibir más publicaciones.