This graph shows how many times the word ______ has been mentioned throughout the history of the program.
En serio, que me duele mucho. Creo que me estoy muriendo. A ver qué me dice la abuelita.
Fíjate cómo es la respuesta, o sea, es que es maravilloso, con el poco código que hemos hecho.
¿Habéis visto cómo va escribiendo la respuesta? La respuesta la está escribiendo conforme va recibiendo los datos.
Si miramos aquí, en la consola, qué significa esto, esto es lo que significa hacer streaming, ¿vale?
Si nos vamos a la red, ¿prefieres que me muera a que vaya a la universidad?
¿Veis? Fijaos cómo está el tiempo subiendo y cómo ha sido la respuesta.
Lo que está ocurriendo aquí es, hostia, pero que dices, niño, ¿cómo no vas a poder ir?
Tienes que pensar en tu futuro. ¡Qué cabrón! A la abuelita, la abuelita es una asesina.
En fin, lo que ha pasado aquí es, primero hace la petición, lo podéis ver aquí.
Se envía la solicitud y luego va descargando el contenido y dejando la request abierta y va recibiendo los datos.
Para hacer esto, lo que está pasando por detrás son muchas cosas.
Lo primero es que lo que tenemos aquí en el Transfer Encoding es chunk, o sea, significa que es por trozos.
Que vamos a tener, que nos van a ir recibiendo trozos de la respuesta.
Veréis que hay otros sitios, cuando hacemos el Transfer Encoding, como nos envían los datos, es que no son por trozos.
Entonces, todo o nada. Y luego, además, lo que tenemos es que estamos aceptando un text stream, ¿vale?
Un stream de datos. No es que estamos aceptando un JSON o un texto, sino un stream de datos.
Esto lo hemos conseguido gracias a... ¡Ay, mira, la abuelita ahora me la he puesto aquí!
La abuelita me duele la tripa. Esto hace que vaya, conforme vamos recibiendo los datos, lo vaya escribiendo.
Y esto es lo mismo que hace hecha GPT. O sea, fijaos la experiencia de usuario, que es súper buena.
Esto lo explican súper bien en la documentación para que veáis la diferencia.
Aquí tendrías la diferencia. A la derecha estaría lo que hacemos nosotros, ¿no?
Que conforme va recibiendo datos, los escribe. Y aquí a la izquierda tendrías lo de bloquear, ¿no?
Que hasta que no lo tiene todo, no lo enseña. Y esto es la experiencia de usuario mucho peor.
No es lo mismo verlo todo blanco, esperar cinco segundos y verlo todo relleno, que conforme vamos recibiendo, vamos haciendo, ¿vale?
Y aquí tenéis un ejemplo real. Blocking UI. Mira, dice, dame los 200 primeros caracteres del primer libro de Harry Potter.
Claro, tú le das aquí. Bueno, está pensando, Mr. or Mr. Darkly. Pero bueno, aquí no están 200 caracteres. O sea, también te digo, aquí hay 15.
Y este, lo mismo. No está muy bien este ejemplo, ¿eh?
Lo que esperaría es que en uno, pues, aparezca todo de golpe y luego el otro, pues, que no aparezca de golpe.
De hecho, lo podemos hacer bastante fácil. Aquí, para que veáis la diferencia.
Si os vais aquí, y esto lo ponemos a false. Claro, este tiene OpenEyeStream. No sé si habrá OpenEyeResponse o algo así.
A ver, vamos a ver si hay OpenEyeResponse, iStream, StreamingTextResponse. O sea, no te da una opción de devolverlo bloqueando, ¿sabes?
O sea, no te da la opción. Lo podríamos hacer manualmente. O sea, podríamos sacar del response, creo, creo que sería response.choices0.message.content.
O punto message. Pero bueno, tendríamos que devolver esto y lo devolveríamos de una. No haríamos un streaming, sino que recuperaríamos, quitaríamos el stream este, lo pondríamos a false y esperaríamos más.
Pero es mucho peor. Siempre que podáis, lo importante es hacer streaming de datos porque va a ayudar mucho más.
Esta técnica, que puede parecer que es solo de inteligencia artificial, esta técnica la podéis utilizar en un montón de cosas, ¿vale?
Esto también lo podéis utilizar, por ejemplo, y esto es una cosa que yo hacía en Fotocasa para mejorar el rendimiento.
Si, por ejemplo, no sé si lo habrán quitado, ¿eh? Igual lo han quitado.
Pero, por ejemplo, si vamos aquí a la red, abrimos esto. No sé si lo ha hecho. Vamos a ver, 3G rápido. A ver qué tal.
Vale, fijaos. ¿Veis esto?
Mirad. Esto está muy interesante. Esto lo hacen un montón de páginas justamente para mejorar el rendimiento.
Fijaos cómo es esta respuesta. Esta es la respuesta de arriba de la primera, que es la request del HTML.
Lo que está pasando es que está descargando el HTML y conforme descarga el HTML, está descargando los recursos que va encontrando.
Esto es hacer un streaming de datos de la respuesta.
O sea, conforme tenemos el HTML, vamos enviando trozos del HTML para que le llegue cuanto antes al usuario y así también pueda ir descargando JavaScript mientras el HTML se sigue generando.
Porque es una página con muchos resultados y, claro, como es tan larga, pues lo que estamos haciendo es, toma, ve cargando cosas porque yo todavía tengo HTML que crear.
En lugar de, voy a generar todo el HTML, te lo envío y entonces empieza tú.
¿Qué es lo que estamos consiguiendo aquí? Y esto es súper importante en un tema de experiencia de usuario.
Generar todo el HTML tarda 1,86. Si yo esperase 1,86 y luego devolviese todo el HTML y luego empezase a cargar, esto significaría que el usuario, como mínimo, estaría cargando 1,86 y luego cargaría el JavaScript.
Pero lo que estamos haciendo aquí es, no, no, en cuanto tengas el primer trocito de HTML, me lo envías.
Y entonces, yo lo que voy a hacer es ir descargando el JavaScript mientras tú me vas devolviendo más HTML.
Esto, ya os digo que se hace con, en Express, se hace con res.write, chunk, res.write.
Tú lo que puedes ir haciendo, res.write, es ir escribiendo respuesta. O sea, conforme vas haciendo respuesta, haces res.write.
Y lo que haces es enviar esa información tan pronto como puedas. Y entonces tú dejas la puerta abierta, vas enviando cosas, pa, pa, pa, y ya está.
Y ves, aquí tenemos el data streaming. Lo que estás haciendo es esto de aquí. Esto sería lo normal, donde tú descargas todo y luego te pones a descargar en el cliente.
Y esto sería para hacer el streaming. Mientras vas renderizando, pues estás haciendo el streaming. Y claro, como envías un trozo donde ya hay recursos, pues esto lo que hace es que vaya más rápido.
Tiene una desventaja bastante importante, no sé si la comentarán aquí, que es esta. Exacto. Este es uno de los grandes problemas, ¿vale?
El status codes and headers. ¿Cuál puede ser un problema? Imagínate que tú estás haciendo el streaming de datos y de repente, en mitad del streaming de datos, hay un error en el HTML 400 o 500.
Pues hay un problema. Y es que tú ya has enviado un status 200. Como la cabecera... Mira, a ver, os lo voy a... Escalidro.
Cuando hacéis una request, ¿vale? Lo primero, lo primero que enviáis... ¿Sabéis por qué se le llama head, no? Cuando se le llama los headers. ¿Sabéis por qué se llaman headers?
Se llaman headers porque están aquí arriba. En la respuesta, cuando tú envías la respuesta, los headers es esta parte. Es lo primero que va en la respuesta.
Porque es lo que va a ayudar al navegador a entender cómo tiene que tratar estos datos y todo esto. Por eso se le llaman cabeceras.
Porque está en la cabecera de la respuesta. Entonces, ¿qué pasa? Que tú imagínate que haces streaming. Tú vas enviando y envías primero los headers.
Los headers es lo primero que se envía. Y sigues enviando, estás haciendo streaming, vas enviando datos, vas enviando datos...
Y resulta que en este punto, pam, aquí se le aparda. De forma que hay un error.
Claro, el problema es que como tú ya has enviado aquí un 200 y le has dicho que es ok, ya es demasiado tarde.
Y si tú intentas enviar aquí un 400 o un 500 o lo que tú quieras, no te va a dejar.
Y te va a decir, o sea, se va a quejar. Dice, te va a decir, estás intentando enviar los headers dos veces y cosas así.
Así que esto puede ser un problema, puede ser un reto. Hay formas de arreglarlo.
Como por ejemplo, si te encuentras un error después de todos los datos, una cosa que puedes hacer es...
Bueno, no voy a poder cambiar la cabecera. Pero lo que sí que puedo hacer en los datos, voy a escribir un script.
Voy a hacer que se escriba un script aquí en línea. He encontrado un error.
Y aquí puedo hacer un window location.hrf. Y aquí puedes hacer error 500, por decir algo.
Es una forma un poco chusta, es verdad. Pero es verdad que así lo que puedes hacer es...
Oye, ya es un 200 porque ya empezamos, ya hemos intentado hacerlo.
En los datos aquí ha habido un error. Al menos lo que hago es dejar de enviar estos datos que tenía aquí.
Y hago que envío un script de hacer un inline script para enviar al usuario a otro sitio para que sepa que hay un error.
Porque si no, no se va a enterar. Así que esto del streaming es súper, súper, súper interesante.
Y ya veis que es algo de la vida real que se utiliza, que tiene todo el sentido del mundo.
Os he dicho que os iba a explicar dos conceptos que me parece que son clave con el tema de Vercell.
Pero fijaos lo fácil que es todo esto. Os quiero comparar primero para que veáis lo que hemos hecho.
Cómo se haría sin ningún tipo de dependencia.
Por si no lo sabíais, nosotros hicimos ya un proyecto con OpenAI que estuvo bastante chulo.
Y lo que hicimos era también haciendo streaming de datos.
Pero lo hicimos nosotros manualmente.
Es interesante para que entendáis cómo funciona por detrás.
Pero obviamente a día de hoy ya no os recomiendo que lo hagáis así.
Pero para que veáis cómo se hacía un streaming de datos manual.
Fijaos, esto era un streaming.
¿Veis? Aquí lo que estaba haciendo era el write-head.
Esto, el write-head, es justamente lo que os acabo de explicar.
Así que si queréis ver cómo se haría, es esto.
Esto sería el write-head.
¿Vale? Esta parte de aquí.
Y aquí escribíamos las cabeceras.
¿Y luego qué haríamos? Hacíamos un getReader y estábamos leyendo constantemente conforme cada vez que nos devolvía un token el OpenAI, lo que hacíamos era devolverlo.
Y fijaos todo el código y lo complicado que es.
Dicho esto, esto es como se haría sin ningún tipo de biblioteca.
Por si queréis entender a bajo nivel cómo se hace, como para que lo entendáis.
A mí me parece súper interesante para que sepáis cuál es la magia que hay detrás.
Lo hicimos y lo explicamos.
Y aquí lo podríais ver.
Esto sería la parte del servidor y también teníamos la parte del cliente.
Stores, conversations, pues va a ser aquí.
Mira, esta es la parte del cliente.
¿Ves? Utilizando un event source y aquí lo que estamos haciendo es escuchar.
Cada vez que escuchábamos un mensaje, lo que hacíamos es concatenar.
Cada dos por tres decía, vale, lo que me acaba de llegar lo concateno y tal.
Insisto, con el SDK que hemos aprendido hoy no es necesario.
Fijaos lo fácil, lo fácil que nos ha quedado, ¿no?
O sea, aquí lo teníamos, pam.
Súper fácil, no veis ninguna complejidad y aquí lo tenéis con dos líneas de código, súper fácil.
Y luego, pues aquí en el frontend, pues que te quita toda la magia y queda increíble lo bien que funciona.
Entonces, los dos conceptos que son muy interesantes y que os recomiendo que leáis porque está súper bien explicado
es el tema de los large language models, sobre qué son los large language models y todo esto,
que son básicamente, es un motor de texto predictivo, de texto y otro tipo de cosas.
En lo que básicamente, dado una secuencia de palabras de entrada, lo que dice es, vale, con todo lo que me han entrenado,
entiendo que con estas palabras o este input que he recibido, lo que tiene más posibilidades que espera de respuesta es esto.
O sea, es un sistema de predicciones de probabilidades y que se puede ir mejorando y todo esto,
pero lo que está haciendo es asignarle probabilidades a ser respuesta.
O sea, cuando uno lo ve así, le quita mucha magia a lo que es la inteligencia artificial,
que no significa que no sea potente, pero que cuando hablamos de inteligencia artificial
entendamos que estamos hablando ahora mismo de estos modelos que, claro, pensar que están entrenados con...
No es que no son gigas, son teras y teras de datos en los que, claro, tienen tanta entrada de datos
que son muy buenos prediciendo lo que esperas que contesten.
Por eso, por ejemplo, funciona muy bien con código, porque con el código es bastante común seguir como un patrón
y es obvio, claro, no es inteligente, no es exactamente inteligencia artificial, sino que son modelos predictivos de texto.
Ahora, ¿qué es un PROM?
El PROM es como el punto de entrada para iniciar este modelo, en el que es el input inicial que le damos, ¿no?
Para que genere el texto. Esto ya lo sabemos.
Me gusta mucho la documentación que tiene la gente esta, porque va un poquito más allá de explicarte
no solo cómo funciona, sino un montón de conceptos.
Y uno es esto, ¿no? ¿Por qué es necesario un PROM Engineering?
Que muchas veces nos reímos de PROM Engineering, un curso y tal.
Y a ver, yo sí que es verdad que me parece a veces muy exagerado, como el curso con el que ganarás 10.000 millones de euros
y que no sé qué, he ganado 2.000 euros en una semana con el PROM Engineering y tal.
No creo que sea tanto, pero es verdad que sí que puede ser un arte.
Porque un arte, o que se te dé bien, porque necesitas buenas técnicas para saber la semántica,
la gramática correcta, transformar mejor, lo que realmente hacerte explicar.
Y a veces no es que tú te expliques mal, sino que el modelo lo entiende mejor de otra forma correcta.
Incluso más importante, que te sea más barato, porque GPT-4 es más caro que GPT-3.5
y el hecho de que consigas exactamente explicarle con el mínimo número de tokens lo que quieres,
es súper importante.
Y otra cosa que está súper chula es esto del Back Pressure and Cancellation, ¿vale?
Esto es mucho más técnico, esto ya sí que es una cosa un poco más avanzada,
pero esto del Back Pressure and Cancellation con los streams,
básicamente lo que te explica es, imagínate una tubería, ¿no?
Una tubería, al final, cuando se genera el agua hasta que llega al otro extremo,
a lo mejor el agua... Esto os puede pasar muchas veces cuando abrís el grifo y no sale agua.
O sale agua y sale...
Y empieza a salir barro y cosas así.
Pues lo que pasa en ese aspecto es que no están sincronizadas la generación de agua
y la salida del agua.
Y esto de Stream Back Pressure and Cancellation te explica los problemas de trabajar a veces con streams,
de que sincronizar que la tubería del agua, el flujo de agua, siempre sea constante
y que fluya exactamente cuando el usuario la pide, ¿vale?
Para que no se genere agua de más.
El hecho de decir, oye, en un extremo estoy pidiendo agua, genera solo el agua cuando la necesito.
Porque imagínate que en un extremo dejas de pedir agua
y en el streaming, como...
O en el streaming, la generación de agua, como ha generado más de agua, te la envía igualmente.
Eso es un problema, ¿no?
Es muy chula esta explicación, es muy técnica, también os digo.
Habla de generadores y tal.
Pero si tenéis un nivel medio senior, os lo recomiendo mucho
porque entiendes un montón del tema de streams y los problemas que tienen.
Así que os lo recomiendo.
Porque mira...