<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>javisantana.com</title>
        <description>Pixels are the humble bricks with which one can build cathedrals</description>
        <link>http://javisantana.com</link>
        <atom:link href="http://javisantana.com/feed.xml" rel="self" type="application/rss+xml" />
        	
        
            
        
            
            <item>
                <title>Learn to cook</title>
                <description>&lt;blockquote&gt;
  &lt;p&gt;Patrick, you should learn to cook. Don’t learn to cook because you want to eat food - learn to cook because if you learn to cook you will have an excuse for the rest of your life to bring people over to your house.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— patio11’s mom&lt;/p&gt;

&lt;p&gt;If this resonated → &lt;a href=&quot;/inspiration/2026-04-18-experience-in-movies.html&quot;&gt;Robert Rodriguez on experience&lt;/a&gt; · &lt;a href=&quot;/2026/01/30/programador.html&quot;&gt;Programador&lt;/a&gt;&lt;/p&gt;
</description>
                <pubDate>Thu, 16 Jul 2026 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/inspiration/2026-07-16-learn-to-cook.html</link>
                <guid isPermaLink="true">http://javisantana.com/inspiration/2026-07-16-learn-to-cook.html</guid>
            </item>
            
        
            
            <item>
                <title>Robert Rodriguez on experience in movies</title>
                <description>&lt;blockquote&gt;
  &lt;p&gt;The most important and useful thing you need to be a filmmaker is “experience in movies,” as opposed to “movie experience.” There’s a difference. They always tell you in film school and in Hollywood that in order to be a filmmaker you need to get “movie experience” so you can work your way up in the business. The reasoning being that by working on other films, even as a production assistant, you get to see firsthand how others make movies. Now, that’s exactly the kind of experience you don’t need. You don’t want to learn how other people make movies especially real Hollywood movies, because nine times out of ten their methods are wasteful and inefficient. You don’t need to learn that!&lt;/p&gt;

  &lt;p&gt;“Experience in movies,” on the other hand is where you yourself get a borrowed video camera or other recording device and record images then manipulate those images in some kind of editing atmosphere. Whether you use old ¾” video editing systems, VCR to VCR, or even computer editing. Whatever you can get your hands on. The idea is to experience creating your own images and/or stories no matter how crude they are and then manipulating them through editing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— Robert Rodriguez, &lt;em&gt;Rebel Without a Crew&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some people who join Tinybird do it to learn how to do things. Most of the times makes more sense to go on your own and figure things out than being in an already designed position from where you don’t see most of the details that make a company successful (or not that successful)&lt;/p&gt;
</description>
                <pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/inspiration/2026-04-18-experience-in-movies.html</link>
                <guid isPermaLink="true">http://javisantana.com/inspiration/2026-04-18-experience-in-movies.html</guid>
            </item>
            
        
            
            <item>
                <title>Programador</title>
                <description>&lt;p&gt;Cuando vas al notario, además de sentirte ajeno a todo aquel ritual, te preguntan tu nombre, DNI, estado civil y, por razones que ChatGPT me ha aclarado, tu profesión. Siempre es un momento complicado y responder “programador” me cuesta. Y lo tengo que reconocer, es así porque creo que van a pensar que soy “un cualquiera”. Hay que ser gilipollas, aunque no voy mal encaminado, hay un tufillo clasista en ese tema (dejando a un lado la parte práctica y legal)&lt;/p&gt;

&lt;p&gt;Empecé mi carrera profesional delante de un teclado, pero no como programador, empecé como “digitalizador”, es decir, alguien que copiaba lo que ponía en un papel a una base de datos, en una empresa de toda la vida, una empresa del metal.&lt;/p&gt;

&lt;p&gt;Aquello era horrible, tanto que me busqué la vida para automatizar el proceso y no tener que hacerlo a mano. Tenía 22 años y mi primer ordenata lo compré justo un año antes (Pentium III, 256mb de ram) así que no tenía mucha idea y tampoco había mucha información, Google estaba arrancando y aún sacabas la mayor parte de información del IRC (para GenZ, una especial de slack/discord pero bien hecho). Te tocaba trabajar y entender, no había ChatGPT.&lt;/p&gt;

&lt;p&gt;Más tarde me convertiría en programador, programé videojuegos, software para embebido, backend, frontend, seguramente todas las posiciones por las que un buen caraboli pueda pasar… pero la realidad es que más de la mitad de mi vida adulta no he programado activamente de forma profesional (pero sí personal, nunca he dejado de programar)&lt;/p&gt;

&lt;p&gt;He hecho sobre todo roles de liderazgo técnico, he trabajado en entornos de alto rendimiento, startup o en retos relativamente importantes, he montado y liderado empresas, productos, desastres… pero no lo he hecho porque realmente quisiese hacerlo, pasaba por allí y me tocó.&lt;/p&gt;

&lt;p&gt;La realidad que me cuesta aceptar, y la razón para escribir este post, es que nunca más voy a volver a ser un programador, primero porque hay gente mucho mejor que yo, pero lo más importante, porque realmente lo que sé hacer bien es lo otro, por mucho que me empeñe. Es duro pensar que lo que haces bien no es lo que realmente quieres hacer bien.&lt;/p&gt;

&lt;p&gt;Aunque bueno, más duro es &lt;a href=&quot;https://www.anthropic.com/news/claude-opus-4-5&quot;&gt;ver como una máquina es mejor que tú en lo que se suponía eras bueno&lt;/a&gt;. Los LLMs aún no están al nivel del un humano pero lo van a estar en meses, así que aunque quisiera ser programador, más me vale que lo haga en mis ratos libres y no para pagar las facturas.&lt;/p&gt;

&lt;p&gt;Imagino que hay gente que se ha dado cuenta mucho antes que yo o que simplemente tenían la vocación de ser “líderes”, gestores, emprendedores o lo que sea, a mi me cuesta ponerme ese título, me parece que me estoy traicionando a mi mismo cuando asumo un rol por rol, sin que sea un medio para un fin. No quiero ser emprendedor por ser emprendedor, quiero ser emprendedor porque no me quede otro remedio para conseguir lo que quiero.&lt;/p&gt;

&lt;p&gt;Siempre me he dicho a mi mismo que aunque tenga un rol, lo haré como realmente crea que hay que hacerlo, no siguiendo las &lt;a href=&quot;https://javisantana.substack.com/p/a-la-segunda-todo-sale-bien-la-recetita&quot;&gt;recetas mágicas de nadie&lt;/a&gt;. Así que simplemente tengo que asumir que nunca fui programador, fui lo que tuve que ser en ese momento para conseguir lo que quería (ayudar agricultores, pintar mapas, analizar datos…)&lt;/p&gt;

&lt;p&gt;Siento este post amigos, sé que os importa muy poco, pero me lo necesitaba escribir.&lt;/p&gt;
</description>
                <pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2026/01/30/programador.html</link>
                <guid isPermaLink="true">http://javisantana.com/2026/01/30/programador.html</guid>
            </item>
            
        
            
            <item>
                <title>Recortes 2025</title>
                <description>&lt;p&gt;Como es tradición, además de asustarte con el título del email, por la razón que sea la gente -especialmente los empleados de mi empresa- piensa que voy a recortar algo (que efectivamente este año he recortado bastantes cosas), voy a hacer mi tradicional recopilación de cosas que me han gustado o, mejor dicho, dejado huella de alguna forma. Las de &lt;a href=&quot;https://javisantana.substack.com/p/recortes-del-2023&quot;&gt;2023&lt;/a&gt; y &lt;a href=&quot;https://javisantana.substack.com/p/recortes-2024&quot;&gt;2024&lt;/a&gt; por si tienes interés.&lt;/p&gt;

&lt;p&gt;Como el pasado, este año me ha dejado menos momentos estelares. Nuevamente seguro que es cosa de la edad y mi estado vital. Además este año ha sido de extremos, bastante radical en lo personal y profesional, pero tranquilos, que no os voy a dar la brasa con mi problemas.&lt;/p&gt;

&lt;p&gt;Aquí va la lista de cosas personales/profesionales del año:&lt;/p&gt;

&lt;p&gt;El CEO de Snowflake siempre da en el clave el tío. Este año pasé a ser CEO de Tinybird y podría firmar con sangre estas palabras, especialmente la parte de energía. Empujar todos los días es carísimo a nivel mental.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/6d6c5b6f-3bf412e5-e5f8-4981-82b9-0d4f477f4c8a_671x900.jpeg&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;Remote work is individual convenience at the expense of group effectiveness&lt;/p&gt;

&lt;p&gt;Guillermo Rauch, CEO de Vercel, siempre aparece en estas recopilaciones, esta vez con un &lt;a href=&quot;https://x.com/rauchg/status/1881115564573438395&quot;&gt;tuit&lt;/a&gt; controvertido con el que no puedo estar más de acuerdo. Creo que el trabajo en remoto está bien y funciona, pero elimina algunas partes de la interacción humana, que aún siendo “prescindibles” las considero parte fundamental de la vida de una persona (que tenga un mínimo interés por realizarse en su trabajo). Posiblemente sea algo añejo, mi hija por ejemplo vivo en un entorno “remoto” constante (habla con sus amigas por videoconferencia) y seguramente se ria de esto cuando lo lea en el futuro.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://x.com/Julian/status/1893532650037764286&quot;&gt;Esta&lt;/a&gt; me duele especialmente. La tendencia a hacer lo que me gusta constantemente, aunque muchas veces de forma inconsciente, es horrible.&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;Working hard for a long time without asking “what are the better things to be working on” is a hidden form of laziness.&lt;/p&gt;

&lt;p&gt;Jason Fried posteaba &lt;a href=&quot;https://x.com/jasonfried/status/1860057872648012088&quot;&gt;esto&lt;/a&gt;. Real como la vida misma. Se ve que estos años son de encontrar mi camino, por eso estas cosas&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/1a97e365-88d61e84-1568-428d-b37a-3dbcf413ac00_922x1078.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://x.com/tommytrxnh/status/1970143299496472954&quot;&gt;Esta&lt;/a&gt; se me ha quedado grabada en el hipotálamo amigos:&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;YOU CANNOT COMPETE WITH SOMEONE WHO IS HAVING FUN&lt;/p&gt;

&lt;p&gt;No hay nada como alguien que está disfrutando de lo que hace. Lo veo en personas - especialmente deportistas- que cierta edad que aún se mantienen competitivos (Fernando Alonso, Carlos Sainz -me duele matizar esto, pero el padre-, John Carmack, Tim Sweeney, Jonathan Blow, DHH…) porque viven lo que puto hacen.&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;This is a goodbye letter I am sending friends to tell them that I am on my way to Switzerland, where my life will end on March 27.&lt;/p&gt;

&lt;p&gt;Este es el comienzo del email que Daniel Kahneman envió a su familia y amigos. No he leído thinking fast and slow pero conocía al autor. &lt;a href=&quot;https://archive.ph/YMSa3&quot;&gt;Este artículo&lt;/a&gt; explica desde un punto de vista muy personal la decisión. Un quote más.&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;I am still active, enjoying many things in life (except the daily news) and will die a happy man. But my kidneys are on their last legs, the frequency of mental lapses is increasing, and I am ninety years old. It is time to go.&lt;/p&gt;

&lt;p&gt;Pagaría un buen dinero por leer ese email pero algo me dice que es mejor no leerlo (o incluso escribir el tuyo propio, pero sin irte a Suiza, claro)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.deulmacadiz.com/p/whisky-tango-romeo&quot;&gt;Whisky Tango Romeo&lt;/a&gt;, es un relato de Javier Cañada. Javier está escribiendo novela corta y relatos relacionados con el tiempo y me están gustando a pesar de años de no tolerar la ficción. Le sigo desde hace años y me gusta mucho la gente que es capaz de cambiar de registro siguiendo su línea y personalidad (cosa que valoro especialmente en esta era post chatGPT). Os recomiendo una de sus novelas, &lt;a href=&quot;https://www.amazon.es/dp/B0F1RF32JV&quot;&gt;Santa Olalla&lt;/a&gt;, see lee en una tarde y si estás en tus 40s seguramente te guste aún más.&lt;/p&gt;

&lt;p&gt;Este año hay más parte moñas de lo esperado, estáis avisados, ahí voy:&lt;/p&gt;

&lt;p&gt;Alguien que contraté hace unos años me escribió esto. He contratado a cientos de personas, muchas de esas personas ahora tienen carreras espectaculares, no es que quiera atribuirme todo el éxito pero un poco sí. Nunca me habían escrito nada así. Gracias amigo, you made my year.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/1d70d1d5-3a749f6b-c11f-437a-b402-5e977f223ac1_1108x1148.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Esta foto con Diego Mariño junto con &lt;a href=&quot;https://x.com/diegomarino/status/1981810895022903777&quot;&gt;su tuit&lt;/a&gt;. La foto no tiene nada de especial, salvo que me estoy riendo que no suele ser habitual, pero hay algo que no se ve que sí es especial: alguien (al que le cuesta especialmente expresarse en estos términos) haciendo un esfuerzo por animarte. No se ve todos los días.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/4f93630c-23db4a7c-90cc-4117-9ffd-16c3794c4281_4096x3072.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;Si sientes ese dolor, mantenlo&lt;/p&gt;

&lt;p&gt;Cuanto más te mueves con el dolor, mejor&lt;/p&gt;

&lt;p&gt;Ahí es donde está el éxito&lt;/p&gt;

&lt;p&gt;Todos sienten el dolor y pocos de ellos saben realmente como manejar el dolor.&lt;/p&gt;

&lt;p&gt;Ellos serán exitosos&lt;/p&gt;

&lt;p&gt;Debes entender que el dolor es parte del éxito&lt;/p&gt;

&lt;p&gt;Combate el dolor con algo positivo.&lt;/p&gt;

&lt;p&gt;De Kipchoge en este &lt;a href=&quot;https://www.tiktok.com/@carloss_athlete/video/7488789993738079510&quot;&gt;video&lt;/a&gt;. Este año me he acordado de esta mierda unas cuantas veces en mi cabeza mientras hacia series corriendo para hacer una 10k por debajo de 50 minutos (al final hice algo menos de 48 consiguiendo uno de mis TODO vitales a los 43 años)&lt;/p&gt;

&lt;p&gt;Me voy a hacer una self-quote aquí. Este año Aitor García Rey se fue inesperadamente.  Es “curioso” (mi léxico no me da para más) porque justo hablaba de lo poco que sabíamos de nuestros padres (en el 26 aniversario de la muerte del mio) y &lt;a href=&quot;https://x.com/_aitor/status/1855672539877343348&quot;&gt;Aitor respondía&lt;/a&gt;. Mantengo un diario para mi hija, para que sepa exactamente qué coño pasaba por mi cabeza, a veces las decisiones se ven mejor con cierto contexto (y este año da para novela). Aún no sé como hacerselo llegar de forma fiable en el futuro.&lt;/p&gt;

&lt;p&gt;Escribía esto sobre Aitor en el slack de la empresa&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;Hey, kind of sad news today, the CTO of Devengo, one of our customers, passed away. It’s not just a customer, he is a pretty well know guy in the tech community in Spain, some people at this company had a good relationship with him (me included). It’s super sad to see someone that young leaving. So if you have some time, I’d try to read some of the things he wrote in his blog or listen to some of his talks –&amp;gt;
&lt;a href=&quot;https://x.com/_aitor/status/1968657411826393187&quot;&gt;https://x.com/_aitor/status/1968657411826393187&lt;/a&gt;
&lt;a href=&quot;https://aitor.is/writing/&quot;&gt;https://aitor.is/writing/&lt;/a&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=ikTqt0Vot_s&quot;&gt;Unexpected inhabitants: algorithms, ethics, and emergency |&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El año pasado compartía un artículo de reflexiones de un ex empleado de Palantir. Este año toca compartir&lt;a href=&quot;https://rbarbadillo.github.io/tinybird&quot;&gt; las reflexiones de una ex-empleada de Tinybird&lt;/a&gt;. Esta salida ha dolido de verdad. Pero bueno, siempre digo que “nunca vayas a un pueblo peor que el tuyo” que aplicado a las empresas es algo así como “vete siempre a una empresa mejor” y creo que en este caso se ha cumplido a la perfección, así que aunque las salidas de empleados no sean lo más agradable del mundo, siempre ayuda ver que progresan.&lt;/p&gt;

&lt;p&gt;Y para dejar un poco de buen sabor de boca,&lt;a href=&quot;https://www.youtube.com/watch?v=GDucR5b4rJk&quot;&gt; este documental sobre un taller artesano japonés&lt;/a&gt; (Naito engineering) es una auténtica delicia. Si no te gustan los coches seguramente no lo disfrutes tanto pero en el fondo habla de una forma de ver la vida y el trabajo. Antes de morir profesionalmente seguramente monte “Santana engineering” o algo similar (no me gusta mucho mi apellido, la verdad), con la misma filosofía pero en software/datos.&lt;/p&gt;

&lt;p&gt;Y aquí lo voy a dejar. Ha habido podcasts, películas y otras cosas que me han gustado, pero nada me ha marcado demasiado.&lt;/p&gt;
</description>
                <pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/12/08/recortes-2025.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/12/08/recortes-2025.html</guid>
            </item>
            
        
            
            <item>
                <title>Miyamoto on team energy</title>
                <description>&lt;p&gt;Miyamoto talking about cinema scenes in videogames but end up describing the most powerful metric in a team: the energy&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We have seen several examples of these cinema scenes, but we positioned these sequences as only one part of development. In Zelda, there are over 1 hour and 30 minutes of cinema scenes. But the staff involved in creating these scenes was only 3 individuals for most of the development process, and in the latter stages, only 6 or 7. The reason behind using such a simple process, as I am sure you have all experienced in the workshop, is that there is a total limit on team energy. There is a limit to the work a team can do, and there is a limit to my own energy. We opted not to use that limited time and energy on pre-rendered images for use in cinema scenes, but rather on tests on other inter-active elements and polishing up the game to create a product that players can enjoy and play comfortably.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20171024043859/http://www.ign.com/articles/1999/03/19/gdc-miyamoto-keynote-speech&quot;&gt;GDC: Miyamoto Keynote Speech&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
                <pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/inspiration/2025-11-10-miyamoto-on-team-energy.html</link>
                <guid isPermaLink="true">http://javisantana.com/inspiration/2025-11-10-miyamoto-on-team-energy.html</guid>
            </item>
            
        
            
            <item>
                <title>Comentarios del director</title>
                <description>&lt;p&gt;Ryanair tiene dos cosas maravillosas: a pesar de humillar a la gente en forma de reglas aleatorias, todavía aplauden al aterrizar, y la menos impresionante, te permite viajar a aeropuertos -que de otra forma estarían muertos- por lo que te cuesta una cena sobre mantel (para los mileniales: los manteles son como unas movidas para poner encima de las mesas, que solían ser de tela. También se usaban cubiertos para comer hasta que la optimización mató al sentido común)&lt;/p&gt;

&lt;p&gt;El caso es que cogí uno de esos vuelos a Treviso, imagino que es la forma barata de ir a Venecia pero en este caso fui a Padua (que es una maravilla de ciudad aún no colonizada por el turista de Ryanair) que está a tiro de piedra de Verona, donde pasó toda la trama de novela adolescente-renacentista que hemos idealizado y creado algo histórico (Romeo y Julieta, para milenials). Es bastante increíble lo bien que venden los Italianos lo que tienen, empezando por la comida.&lt;/p&gt;

&lt;p&gt;Paseando por Verona dimos con la catedral. Yo nací y pasé una tristísima infancia en un pueblo con dos iglesias tremendas, no son catedrales pero harían sombra, literal, a muchas de las catedrales construidas en la misma época. Cuando ves algo todos los días pierde el valor por mucho que tenga, así que, aunque se que las catedrales son obras increíbles en muchos sentidos, me cuesta apreciarlo. En cualquier caso entré por inercia y poco de presión social, como buen discípulo de Ryanair.&lt;/p&gt;

&lt;p&gt;Ojo, por un momento pensé que me salvaría de entrar, cerraban ese día antes porque había un concierto más tarde, pero ya había pagado, así que aunque fuesen 5 minutos tenía que rentabilizar la inversión. Justo al pasar el NFC se empezó a escuchar música clásica, la acustica era espectacular y ya solo esos 5 segundos merecieron la pena. Estaban ensayando lo que después sería el concierto y me pude acercar a los músicos que casi podía tocar los platillos.&lt;/p&gt;

&lt;p&gt;El ensayo no era una reproducción del concierto pero sin vestir de gala, el director paraba cada poco tiempo para dar indicaciones, repetir una parte, ajustar el volumen (me perdonen por no usar la jerga adecuada) e iba haciendo pruebas de algunas partes de la obra, incluido el coro.&lt;/p&gt;

&lt;p&gt;En resumen, disfruté más es esa media hora que de las obras del mismo tipo, todas ellas ejecutadas a la perfección, que he visto en mi vida.&lt;/p&gt;

&lt;p&gt;Seguramente disfruté tanto no solo porque fue inesperado si no porque me aprecio mucho más los detalles cuando ves cómo está hecho, cuando se cometen errores, se aprecia mejor el esfuerzo y lo que cuesta mejorar ese último 10-20% (que nadie más va a notar, solo Dios lo ve).  Me gusta más ver una repetición de un combate de Topuria comentado por Topuria que el propio combate, una repetición de la Indy500 con las decisiones tomadas por Palou que la carrera o decisiones de diseño que alguien tomó en una aplicación. Disfruté mucho más del podcast de la serie Chernobyl donde Mazin cuenta las decisiones que tomaron (por ejemplo, el idioma del rodaje) que de la ya de por si buenísima serie.&lt;/p&gt;

&lt;p&gt;Hubiese pagado el doble de la entrada para ver el ensayo completo.&lt;/p&gt;
</description>
                <pubDate>Sat, 02 Aug 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/08/02/comentarios-del-director.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/08/02/comentarios-del-director.html</guid>
            </item>
            
        
            
            <item>
                <title>Que viene el lobo</title>
                <description>&lt;p&gt;Cada semana se anuncian unas cientos de empresas que, dicen, van a cambiar el panorama de &lt;ponga aquí=&quot;&quot; su=&quot;&quot; industria=&quot;&quot;&gt; usando IA. En esa misma semana empresas levantan millonadas argumentando que lo hacen para jugar al juego de la IA. Y las empresas de toda la vida, los Google, los Microsoft y demás ballenas se convierten en delfines anunciando modelos e integraciones con productos actuales.&lt;/ponga&gt;&lt;/p&gt;

&lt;p&gt;Por otro lado tienes a unas cuantas otras empresas, nacidas al calor de la IA, haciéndose los gallitos con el crecimiento de ARR. Me pregunto cual es el objetivo de anunciar a los 4 vientos cuanto ingresas, imagino que es una manera de decir “aquí el que voy a ganar soy yo”, pero no lo tengo claro.&lt;/p&gt;

&lt;p&gt;Es bastante obvio que hay un cambio. Bitcoin tardó en llegar a la boca del pueblo bastante más de un lustro. Esto está ya en la cabeza de todo dios en menos de 3 años. Si tienes interés, haz una búsqueda en google trends (bitcoin vs chatgpt):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/51c2c55f-752e91be-778a-4e93-96cb-286ee31b39c6_2282x758.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Y hay un dilema tremendo si eres emprendedor/empresario es, a donde pego el tiro? las opciones que veo en mi cabeza:&lt;/p&gt;

&lt;p&gt;Puedes elegir pasar y esperar (hacer un Apple lo llaman) pero sinceramente me parece una estrategia arriesgada. Te van a pasar por la derecha (salvo que seas Apple y puede que también, espera que otros se pongan a hacer hardware). No tengo pruebas, tampoco dudas.&lt;/p&gt;

&lt;p&gt;Puedes elegir integrar IA en tu servicio/producto/empresa. Parece la más sensata, usar la IA con cierta cabeza para tener esas mejoras e ir integrandote en la ola poco a poco, sin asustar a tus clientes actuales, haciendo una transición ordenada y con relativo poco riesgo.&lt;/p&gt;

&lt;p&gt;Pero claro, si eres una empresa de VC y estás con un crecimiento que no está mal, pero no es el “hockey stick” que esperas (que es el 95% de las startups que no están muriendo) , es muy tentador ir a coger la ola más grande y hacer que tu posicionamiento sea IA first. Ves como las empresas que usan cualquier cosa que sea IA como canal de distribución, algunas dando más pena que gloria, pero consiguiendo cierta tracción, así que tú, con tu WhatEverFy que es el nuevo Uber de la venta de coliflor con colores que puede elegir, te montas en la ola y ahora eres WhatEverFy.ai y las coliflores son las mismas pero creadas con un modelo propio de IA.&lt;/p&gt;

&lt;p&gt;Lo que en ningún caso puedes hacer es estarte quieto (en mi opinión). Nadie sabe donde va a caer el gordo pero tienes que empezar a poner a tu equipo en guardia, empezar a meter a todos en la cabeza que hay que estar un poco al día. Cuando todo empiece a asentar un poco es mejor que te pille con los deberes hechos. Muchas empresas van tarde, incluso en Tinybird que es una empresa donde todo el mundo es bastante espabilado, hay gente que no estaba poniéndose manos a la obra.&lt;/p&gt;

&lt;p&gt;En Tinybird dedicimos hace 8 meses que o hacíamos un cambio o estábamos mal. Somos una herramienta para desarrolladores y los desarrolladores de hace 1 año no son los mismos que los de ahora. Pensarás que estoy exagerando y que tardará en llegar a sectores más tradicionales. Como buen heterobásico que soy generalizo anécdotas pero esto es demasiado goloso como para que hasta el más tonto no se de cuenta: los sistemas automáticos de código son mejores que muchos desarrolladores y hasta las cárnicas se han dado cuenta.&lt;/p&gt;

&lt;p&gt;Hagas lo que hagas con tu producto, tienes la otra parte de la estrategía y es qué coño vas a hacer a nivel de procesos internos de compañóa. Es un cambio fuerte, la inercia es altísima y hacer cambiar a gente de entre 30 y 40 años su forma de trabajar con una máquina no es trivial, incluso aunque sea gente con mucha experiencia con la informática. Cada cosa que hago me doy cuenta de las oportunidades perdidas para haberlas hecho mejor y más rápido, pero no lo hago así porque hay fricción. Fricción en mi cerebro, no en las herramientas.&lt;/p&gt;

&lt;p&gt;Y esto pasa en muchos trabajos. Por suerte los humanos nos podremos dedicar a hacer otras cosas mientras la IA hace lo que realmente hace bien, pero hay que adaptarse. Eso o afiliarse a un sindicato que luche para que los robots no reemplacen puestos de trabajo tratando de retrasar lo inevitable y sacar unas cuantas “prejubilaciones” y “recolocaciones”.&lt;/p&gt;
</description>
                <pubDate>Wed, 04 Jun 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/06/04/que-viene-el-lobo.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/06/04/que-viene-el-lobo.html</guid>
            </item>
            
        
            
            <item>
                <title>When the chef cooks at home</title>
                <description>&lt;p&gt;Chefs usually manage “large” kitchens. They have a few cooks and have to deal with a constant flow of dishes they need to dispatch on time. It’s not just cooking, it’s the whole process: from designing the recipe, getting the raw food, and cooking, to serving it on time, hiring, firing, company financials…. I can’t imagine how hard it must be to handle all of that.&lt;/p&gt;

&lt;p&gt;In software companies, things are pretty similar. It’s not just the product; it’s designing, developing, keeping everything up, serving customers, and so on.&lt;/p&gt;

&lt;p&gt;Chefs also cook at home, small dishes maybe, just for their families, or something they want to test. They do what (and this is a guess) they love and what pushed them to become chefs in the first place: creating good food.&lt;/p&gt;

&lt;p&gt;On weekends, I wake up at 6:00 AM so I have a few hours to work/learn on whatever I want before everyone else wakes up. It’s my “cook for my family” time, but in this case, I don’t do anything for them; I code without the constraints of the production environment. I do it for myself, to test new things or just enjoy it.&lt;/p&gt;

&lt;p&gt;I’m working on a project using an ESP32, a small microcontroller with super limited resources (if you compare it with a modern laptop), but it has everything: connectivity, storage, a little bit of RAM, a pretty decent ARM processor, and good toolchain and library support.&lt;/p&gt;

&lt;p&gt;So, I wake up, make myself a large coffee, and start coding my own data compressor to store GPS positions in the most efficient way so I can review the laps I do with my “racecar”. No AI, just Vim and g++.&lt;/p&gt;
</description>
                <pubDate>Wed, 21 May 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/05/21/when-the-chef-cooks-at-home.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/05/21/when-the-chef-cooks-at-home.html</guid>
            </item>
            
        
            
            <item>
                <title>Como uso los LLM </title>
                <description>&lt;p&gt;Lo que pueden hacer los LLMs es básicamente un sueño hecho realidad para mí. Soy un “builder”, me gusta construir y me parece magia todo lo que está pasando, es como revivir la época cuando aprendí a programar. Peeero como siempre que llega algo nuevo, está lo que idealizamos y luego la vida real, cuando la tecnología toca suelo, hay que hacerla funcionar y adaptarla al caso real. Aquí voy a ir al caso práctico, para hablarte de cómo la IA va a cambiar el futuro futuro, puedes ir a LinkedIn y leer a todos los chamanes, que no dejan de ser los mismos middle managers que no saben ni por dónde les viene, que han encontrado la nueva vía de hacerse notar.&lt;/p&gt;

&lt;p&gt;Voy a dejar por escrito cómo estoy usando estos sistemas a día de hoy, como digo, a nivel práctico, enfocado en la construcción de productos (seguramente haga algún update de vez en cuando), dejando a un lado las preguntas de todos los días que antes hacías a Google y ahora las hago a Grok (pago suscripción a Twitter y con ella tengo acceso a deepsearch que es bastante bueno). y ChatGPT. Esto ya lo saben hacer hasta los niños de 6 años, incluso dan cursos en las empresas de como usar ChatGPT, así que no voy a entrar en detalle.&lt;/p&gt;

&lt;p&gt;Una nota sobre las herramientas; uso exclusivamente dos herramientas e intento hacerlas al nivel más básico posible. Con los años te das cuenta que profundizar en detalles te hace perder foco y el tiempo en cosas que aportan bastante poco, es mejor centrarte en la funcionalidad core y listo.&lt;/p&gt;

&lt;p&gt;La primera es Cursor. Si no sabes que es, seguramente este post no sea para ti, si realmente estás interesado, buscarás que es y lo probarás.&lt;/p&gt;

&lt;p&gt;La segunda es &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;llm&lt;/code&gt;, una herramienta de línea de comandos que tiene lo justo y necesario para trabajar con LLMs a alto nivel, sin tener que usar el API de OpenAI y permite automatizar fácilmente. Algunos ejemplos básicos que cubren el 99% de lo que uso y explican la herramienta.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ llm &quot;this is my prompt&quot;
$ llm -m gemini-2.0 &quot;this is the prompt&quot;
$ llm --system &quot;you are an expert on this&quot; &quot;the prompt&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;usos-prácticos&quot;&gt;&lt;strong&gt;Usos prácticos&lt;/strong&gt;&lt;/h1&gt;

&lt;p&gt;“Vibe codeo” bastante, concretamente hay dos herramientas que he “programado” que uso en mi día a día para hacer mi trabajo:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El editor de texto que estoy usando para escribir esto es una aplicación, toda contenida en un “index.html” que me permite escribir y además usar LLMs para ayudarme con cosas como corregir el inglés, contarme si hay frases que a un nativo le suenen mal, reescribir el documento como si fuese “Pérez Reverte”, hacerlo más corto, etc.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/141f2a83-2672e6ab-9bbf-44fc-b453-b647c2e77c1f_3454x1748.png&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Para ello uso Cursor, abro un nuevo proyecto, creo un index.html y le pido lo que quiero dejando claro que quiero una aplicación html/css/vanilla javascript.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La herramienta que uso a nivel de producto para entender el “journey” de los usuarios de Tinybird, desde que llegan a la web, cuando se registran y como usan el producto. Uso el mismo sistema que antes, Cursor + app básica escrita en html/javascript, integrando con las API de Tinybird y PostHog para recoger datos y mostrarlos de una forma lógica.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/113a11da-25a1298c-b0eb-43ff-8d74-da3b5ea052e7_2310x1552.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Son aplicaciones muy muy sencillas, muy ad-hoc a como me gusta hacer las cosas. Son un puto traje a hecho por ti con un implante cerebral del mejor sastre del mundo vs uno de Zara de 119.93€. Es absurdo que en unos meses las aplicaciones, no solo las escriba quien las necesita, sino que las aplicaciones no sean fijas y no cambien todo el rato (sí, ahora estoy haciendo de chaman)&lt;/p&gt;

&lt;p&gt;A nivel de gestión de producto &amp;amp; marketing:&lt;/p&gt;

&lt;p&gt;Dejando a un lado que en Tinybird ya no se hace una feature sin pensar como aplicar esto, de hecho acabamos de rehacer una parte del producto donde el foco está en mejorar el trabajo con LLMs. No tienes que convertir tu producto en un chat, pero usarlo para tareas que son un auténtico peñazo es un buen comienzo.&lt;/p&gt;

&lt;p&gt;En Tinybird genero una newsletter cada dos semanas donde enviamos los artículos que el equipo ingeniería de Tinybird lee. Me gusta saber qué lee la gente del equipo y esto es una excusa perfecta. Además, mantiene a la gente que sigue Tinybird un poco más pegada a la empresa. El sistema es simple: hay un pequeño bot que lee un canal del Slack interno de la compañía y en base a un prompt genera el correo para enviar a la lista de correo.&lt;/p&gt;

&lt;p&gt;Para que nadie se sienta estafado por leer algo generado automáticamente le he dado personalidad propia al bot. Esto es una parte del prompt:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Get the comments people made for each link and generate a brief comment about it. It should be funny, like making fun of the people in the company, but in the possitive [positive] way. We love to troll each others [each other] in this company. You act as an editor, you should have your own voice and speak in first person. This was the last one@newsletter.md
- Try to guess the company position based on their comments
- Do not mention names just leave what you think is the position
also introduce you at the beggining [beginning], your name is lebrelbot you work at tinybird, an AI bot and don&apos;t care about what other people say about
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;En las primeras iteraciones le dije que fuese un poco “pasivo-agresivo” y tuve que quitarlo porque los LLMs son especialmente buenos dando donde duele.&lt;/p&gt;

&lt;p&gt;Para estas automatizaciones uso llm, por ejemplo, el comando para generar la lista de correo es:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uv run llm  -m gemini-2.0-flash --system &quot;$(cat prompt) today is $(date)&quot; &quot;$(cat 2025-03-28/chat) `curl -s https://www.tinybird.co/blog-posts/rss.xml`&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Le paso el prompt que comentaba antes, la fecha para que sepa donde estamos, el chat según viene de Slack (sin formatear, para qué perder el tiempo) y el RSS con los posts del blog Tinybird (que incluye al final de la lista). Te puedes suscribir &lt;a href=&quot;https://faster.tinybird.co/schema-evolution&quot;&gt;aquí&lt;/a&gt; si tienes curiosidad. &lt;a href=&quot;https://gist.github.com/javisantana/55550118e3030476ffd762c13f1246fa&quot;&gt;Este&lt;/a&gt; es la última edición.&lt;/p&gt;

&lt;p&gt;Al hilo de lo anterior, pensé: puede un LLM emular el estilo de escritura de una persona leyendo lo que escribe en los canales públicos de Slack. Hice un pequeño test de usar mi estilo de escritura (pero como si fuese americano nativo) y meter el bot en un canal de slack a que respondiese cuando alguien le pregunte. Era bastante cabroncete por ser honestos y tuve que pararlo. Los hijos y los LLM reflejan los peores actos de cada uno.&lt;/p&gt;

&lt;p&gt;Como anecdota, cuando le pedí que hiciera un perfil de la persona se me olvidó “de escritura” y me sacó un perfil de todas las personas del equipo. No sé si es efecto horóscopo (escribe lo que quieres escuchar) pero es muy bueno calando a la gente.&lt;/p&gt;

&lt;p&gt;Me parece un tema bastante interesante, de hecho hay más gente haciendo esto &lt;a href=&quot;https://every.to/podcast/100-ai-personas-said-you-d-click-this&quot;&gt;para simular audiencias&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;También lo uso para generar informes del comportamiento de los usuarios de Tinybird a nivel de producto, es decir, qué hace un usuario, donde se atasca, qué hace bien. En Tinybird guardamos todos los eventos (nuestro producto sirve para guardar datos, así que parece obvio) de lo que hacen los usuarios. Es una tabla muy con &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_id, timestamp, event_data&lt;/code&gt;. En vez de usar una herramienta a medida, símplemente cojo los eventos y hago pipe al llm. Es algo más complejo que esto porque elimino bastante información para reducir los tokens que necesito enviar al LLM (aunque gemini tiene un contexto muy aceptable) y eliminar detalles de errores y otras cosas que no sean puro metadato.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl https://api.tinybird.com/v0/pipes/user_events?user_id=lebrel | llm -m gemini-2.0-flash --system &quot;You are an expert in analyzing user behavior on Tinybird, a web platform to work with analytical data. The first thing you&apos;ll recieve [receive] is a JSON with all the events from an user using Tinybird (a web platform to work with analytical data), generate a summary of what the user did.
    You should include the following sections with a title for each one:
        1) relevant dates
        2) a timeline of events per day/week/month
        3) what errors the user hit
&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;El prompt es mucho más extenso, pero te haces una idea. Y si no, preguntame y te lo paso completo.&lt;/p&gt;

&lt;h1 id=&quot;otros-temas&quot;&gt;&lt;strong&gt;Otros temas&lt;/strong&gt;&lt;/h1&gt;

&lt;p&gt;Sin un orden particular.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;optimización-de-sql-y-tablas-ejemplo&quot;&gt;Optimización de SQL y tablas, &lt;a href=&quot;https://gist.github.com/javisantana/56a8262dcd65e2b511e5cc3cd6705b07&quot;&gt;ejemplo&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Generar títulos y draft de posts sobre clusters de temas de los que quiero escribir:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Busco una lista de correo de la que me guste el estilo. Copio la lista completa en un fichero (sí, todo el contenido junto)&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voy a Grok research y le pregunto que en base a Reddit y otros sites me genere 50 títulos con, por ejemplo, “top problems and use casesdata engineers when dealing with real time data”. Copio la lista en otro fichero.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pongo cursor en modo agente (que va haciendo cosas por ti de forma semi desatendida) y le digo: “generate a blogpost for each title following the style of this mailing list. Add examples on how to do it with Tinybird”. Además le paso el prompt super curado que tenemos para Tinybird (tú ya tienes el prompt para tu producto, verdad?) Hice un test de pasarle a mis compañeros el post para que me lo revisaran (como si lo hubiese escrito yo) y solo una persona se percató de que era 100% automático. Un ejemplo de lo que genera, &lt;a href=&quot;https://gist.github.com/javisantana/52cb019c7e77ff676d1941a7a37ccacb&quot;&gt;“how to build a recommendation engine”&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;uso-bastante-lo-de-generar-visualizaciones-ad-hoc-para-cosas-por-ejemplo-para-hacer-debugging-de-ciertas-cosas-un-ejemplo-con-una-traza-gps&quot;&gt;Uso bastante lo de generar visualizaciones ad-hoc para cosas, por ejemplo, para hacer debugging de ciertas cosas. Un &lt;a href=&quot;https://x.com/javisantana/status/1877953983001620629&quot;&gt;ejemplo con una traza GPS&lt;/a&gt;.&lt;/h2&gt;

&lt;h2 id=&quot;en-general-cualquier-cosa-que-me-llega-abro-un-terminal-copio-el-contenido-en-un-fichero-y-tiro-un-cat-file--llm---system-whatever-i-want-to-do&quot;&gt;En general, cualquier cosa que me llega, abro un terminal, copio el contenido en un fichero y tiro un &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat file | llm --system &quot;whatever I want to do&quot;&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Hago bastante el “critica esto que he escrito de forma dura, se un poco pasivo agresivo manteniendo el humor, esto me ayuda mucho a mejorar”. Te advierto que es duro leer las críticas y si tienes la piel fina, no lo hagas. Ningún humano que no tenga problema social va a ser tan sincero. Aún no he probado a pasarle una foto y decirle lo mismo, pero si no ha pasado, va a pasar.&lt;/p&gt;

&lt;p&gt;Estoy en una fase ahora donde estoy empezando a automatizar de forma desatendida, por ejemplo para hacer matching de problemas reportados en Slack con tickets en gitlab, pero aún no he profundizado casi nada, así que lo dejo para la siguiente edición.&lt;/p&gt;
</description>
                <pubDate>Sun, 30 Mar 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/03/30/como-uso-los-llm.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/03/30/como-uso-los-llm.html</guid>
            </item>
            
        
            
            <item>
                <title>We need a new git</title>
                <description>&lt;p&gt;A few years ago continuous integration systems were not a thing. You were lucky if you worked for a company with automation, so imagine having something that runs for every commit. We were using CVS, subversion and other similar non-distributed version control systems.&lt;/p&gt;

&lt;p&gt;Those systems were like git, you didn’t have a local repo, you needed to push into a remote server, but that’s what we are doing most of the time these days as collaboration happens remotely. Git fundamentally only moved things locally and allowed better merges and collaboration. That’s great but we paid a high price: git is hard to use.&lt;/p&gt;

&lt;p&gt;With git, continuous integration system started to be popular, it was not entirely “thanks to” git, but it happened at the same time. Modern guys were doing nodejs projects, started using React and testing was mandatory, so did CI.&lt;/p&gt;

&lt;p&gt;It was like magic, each push an automatic system would run and give you feedback: OK/NOK.&lt;/p&gt;

&lt;p&gt;I’m changing topics for a second. Big data was slow because 20 years ago, hardware was not as fast as today. People got used to slow batch job processes, and that’s what they have been doing since then. A few years ago, someone realized that processing a lot of data didn’t have to be that slow and created a different kind of databases.&lt;/p&gt;

&lt;p&gt;15 years have passed and we are still changing things locally and still need to commit manually and wait for a long feedback loop to know if we are doing great. We got used to it, we didn’t even know we gave up on speed, we assume CI cycles are slow, we have to wait minutes from the time we type the last character to the OK.&lt;/p&gt;

&lt;p&gt;This is becoming super important as LLMs are starting to be “in the loop”. And as humans, they make a lot of mistakes, but if you give them feedback, they usually manage to fix them.&lt;/p&gt;

&lt;p&gt;Speed is a feature and I think there is room (and hardware resources) to have a system that gives us feedback every time we type a new character.&lt;/p&gt;
</description>
                <pubDate>Wed, 26 Mar 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/03/26/we-need-a-new-git.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/03/26/we-need-a-new-git.html</guid>
            </item>
            
        
            
            <item>
                <title>Neglecting opportunities</title>
                <description>&lt;blockquote&gt;
  &lt;p&gt;Most of us have this natural tendency to overly obsess over the outcome—the promotion or the beach body or the big sale—and to neglect the joy of the journey, thereby undermining our ability to actually make it across the finish line. What this means is that we may have to pay extra close attention to whether we’re neglecting opportunities to make the current phase of our journey more rewarding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think a lot about this lately. Some things in the journey of a startup are stressful and hard (layoffs, people leaving, not performing at the level other expect). It’s a cliché, I know, but that doesn’t mean it’s less true.&lt;/p&gt;

&lt;p&gt;Lewis Kallow in this &lt;a href=&quot;https://every.to/p/building-an-unstoppable-mindset&quot;&gt;every.to article&lt;/a&gt;&lt;/p&gt;
</description>
                <pubDate>Tue, 25 Mar 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/inspiration/2025-03-25-the-journey.html</link>
                <guid isPermaLink="true">http://javisantana.com/inspiration/2025-03-25-the-journey.html</guid>
            </item>
            
        
            
            <item>
                <title>Iceberg and S3Tables</title>
                <description>&lt;p&gt;Iceberg is the latest trend in the data space, and for good reason (it allows different products to share data in a standard way) Everyone is thinking, &lt;em&gt;“I’ll store all my data in Iceberg to avoid vendor lock-in.”&lt;/em&gt;. But I’m not so sure that’s actually true.&lt;/p&gt;

&lt;p&gt;In theory, Iceberg hasn’t won yet. There are other data lake format implementations out there. However, Databricks and Snowflake (the two largest cloud companies) put the format in the spotlight when they battled to acquire Tabular, the company behind the Iceberg spec. Databricks won, but in theory, everyone benefits from the increased adoption.&lt;/p&gt;

&lt;p&gt;AWS recently announced an extension to S3 to manage Iceberg tables (S3 Tables). This move surprised me, S3 is AWS’s flagship product, so adding native support for Iceberg suggests they’re seeing something we aren’t (probably S3 usage stats).&lt;/p&gt;

&lt;p&gt;Iceberg itself isn’t a complex format, understanding the spec is a matter of spending a few hours reading the spec and then a few questions to your favourite GPT. But managing it? That’s where things can quickly become a headache. That’s exactly what S3 Tables aim to solve: no need to worry about compaction, cleanup, or spinning up EMR clusters to let Spark handle it (since Spark is, afaik, the only battle-tested Iceberg implementation).&lt;/p&gt;

&lt;p&gt;Moving data into S3 Tables is convenient, you get all the power and cost benefits of S3 without the management overhead. Of course, data compaction at scale is expensive, especially if you’re dealing with massive datasets. &lt;a href=&quot;https://www.linkedin.com/posts/javisantana_a-quick-analysis-on-the-s3-tables-pricing-activity-7270077869572202496-b3dv?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAAB5CrEBAM8MJvKmk4L86t3SIsJYVrGEF9k&quot;&gt;I made the math&lt;/a&gt; and some of our customers would spend $60k a day (for a real time workload)&lt;/p&gt;

&lt;p&gt;But here’s the catch: if you want to process that data, you’ll likely need to do it within AWS. Egress costs make running outside of AWS nearly impossible. So in the end, AWS wins.&lt;/p&gt;

&lt;p&gt;So you’ll not have vendor lock-in in your database engine but you’ll have a different kind of vendor lock-in.&lt;/p&gt;
</description>
                <pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/02/28/iceberg-and-s3tables.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/02/28/iceberg-and-s3tables.html</guid>
            </item>
            
        
            
            <item>
                <title>España y startups</title>
                <description>&lt;p&gt;El otro día teníamos &lt;a href=&quot;https://x.com/cuenca/status/1893376686952980697&quot;&gt;esta&lt;/a&gt; conversación en twitter (para algunas cosas sigo pensando en pesetas, imaginate cambiar el nombre a twitter) donde Jaime Novoa la clava:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/b618b360-2354d5e1-f5e3-4bbb-9293-1da343e568f7_1284x1430.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Es muy difícil hacer una lista mejor que esa, así que no la voy a tocar pero lo que sí voy a hacer es dar el punto de vista desde dentro, el de fundador de una startup.&lt;/p&gt;

&lt;p&gt;Por dar contexto, en 2006 monté mi primer empresa de producto que &lt;a href=&quot;https://javisantana.com/2017/11/13/adios-agroguia.html&quot;&gt;vendí 11 años después&lt;/a&gt; (todos ellos siendo muy rentable), trabajé en CARTO como CTO (más de +$30M de financiación cuando yo me fui, ahora estarán cerca de los 100) y llevo 5 años como fundador de Tinybird, una empresa deep tech que ha levantado unos 70 kilos con inversores Americanos y Europeos. En ambas compañías hemos conseguido llegar a varios millones de ARR. Además he trabajado en otras startup que se la pegaron, en el gran corporate, como freelance y en industrias tradicionales. Sé lo que es sufrir&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sobre “Risk takers”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No sé a quien se refería, si a los emprendedores o al resto de jugadores (inversores, clientes) pero es cierto, pero el punto de vista del fundador es muy claro: te la juegas con todo.&lt;/p&gt;

&lt;p&gt;Si eres un chaval y arrancas, seguramente no tengas ni un puto duro y o bien tienes padres que van en Porsche Panamera Turbo o te va a tocar ir muy lean unos años. En mi caso vengo de una familia muy pobre, no de pedir por la calle, pero de pasarlas putas sí. Me he pegado mis 20s trabajando todos los fines de semana para ir a vender a los agricultores mi producto por los pueblos, por dar una referencia.&lt;/p&gt;

&lt;p&gt;Si tienes cierta edad, puede que tengas unos ahorros, pero ahora juegas con tu familia. Se puede emprender de 9 a 5 seguramente, yo aún no lo he visto.&lt;/p&gt;

&lt;p&gt;No solo es un tema de dinero, puede que te sobre, es un tema de mentalidad. Si has sido pobre llegar y fundirte 25 millones de dolares (ups, perdón, de euros) en un año, 300 mil pavos en un dominio, $400k al año en un perfil concreto es muy duro y difícil de asimilar. Pero lo tienes que hacer. Mucho de ese dinero lo vas a tirar, pero para conseguir cosas, hay que fallar.&lt;/p&gt;

&lt;p&gt;Cuando voy a SF y hablo con algún emprendor de ahí, además de que se venden muy bien, les importa tres narices la mayoría de cosas que aquí nos asustan. Que hay que pivotar porque aparecen los LLMs, se pivota, que hay que tirar clientes, se tiran. No &lt;a href=&quot;https://javisantana.substack.com/p/sin-mirar-atras&quot;&gt;miran atrás&lt;/a&gt;. Esto hay que mamarlo y en España, donde tu entorno está más cerca del funcionariado que de todo su jugarse el capital en 3 años, no es fácil.&lt;/p&gt;

&lt;p&gt;Personalmente a mi me está costando y dudo que con 43 años vaya a cambiar de forma radical. El día que voy a tomar una decisión jodida al acostarme siempre &lt;a href=&quot;https://javisantana.com/2014/12/25/cierre-2014.html&quot;&gt;veo la foto de mis abuelos que tengo en la mesita de noche&lt;/a&gt; y no puedo evitar pensar que me echarían una peta monumental. Ya no están aquí, pero lo que ves en casa durante años es difícil darle la vuelta.&lt;/p&gt;

&lt;p&gt;El VC en España tampoco es que sea muy aventurado, espero que esto &lt;a href=&quot;https://x.com/samuelgil/status/1894028331248398478&quot;&gt;cambie&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cultura pro-empresa&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No voy a entrar mucho aquí porque tocamos tema político, sindical y creencias estúpidas como que seguimos en una economía donde poner fundas de asientos en una cadena de producción es lo que levanta un país, pero solo os diré una cosa: yo no digo a nadie de fuera del entorno startup en lo que trabajo.&lt;/p&gt;

&lt;p&gt;No digo que tengo una empresa, evito hablar del tema porque no me apetece dar explicaciones de nada. Y cuando lo cuento, me arrepiento, muchas veces hay preguntas perniciosas o incredulas. No les culpo, te imaginas estar hablando con &lt;a href=&quot;https://x.com/javisantana/status/1663487228603932673&quot;&gt;alguien con problemas más que patentes de comunicacion con otros seres humanos &lt;/a&gt;diciendote que ha levantado 70M, trabaja haciendo no sé que de bases de datos y quiere reventar el mercado. Necesitamos que la persona que monta una empresa, especialmente del tipo “todo o nada” no sea un bicho raro. O aceptar a los que hay bichos raros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Más capital&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Por suerte nosotros vamos bien servidos. Pero vamos bien porque tenemos un track record y nos ha permitido levantar capital “facilmente”. Esto no viene símplemente por estar ahí: nos hemos labrado a pulso, trabajando 16 horas al día para sacar &lt;a href=&quot;https://javisantana.com/2013/06/27/como-aguantamos-una-portada-de-google.html&quot;&gt;proyectos increíbles&lt;/a&gt;, peleando cada partido, contando como lo hacíamos, yendo a jugar con los mejores (o intentandolo), etc, etc.&lt;/p&gt;

&lt;p&gt;Puedo entender que alguien empezando lo tenga crudo y seguramente lo de “risk takers” vaya también por la gente de VC (y otras entidades que meten gallina) se atrevan con gente que parece lista, pero que no pinta tan bien en el excel. Lo peor es que perdemos todas las oportunidades cuando empiezan, me &lt;a href=&quot;https://x.com/javisantana/status/1640065997225820162&quot;&gt;autorreferencio&lt;/a&gt; un tuit de hace dos años (no voy a dejar pasa esta y aprovecho a darme la razón)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/40d8c0f0-265d2fbb-db24-43b7-9d8d-32ba7df5d5ca_1306x564.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Esto que digo no existe, pero se puede generar con dinero y la gente adecuada (más sobre esto después)&lt;/p&gt;

&lt;p&gt;Luego tenemos las ayudas estatales y europeas, pero muchas de ellas terminan en proyectos, y perdonad la expresión, de mierda o creados ad-hoc para trincar la subvención. La industria de la ayuda europea.&lt;/p&gt;

&lt;p&gt;**Más referentes **&lt;/p&gt;

&lt;p&gt;Tenemos references, el mismo Joaquín Cuenta que empeza el hilo en twitter es uno de ellos y hay otros muchos en el panorama español. El problema es que muchas veces desaparecen porque pasan de estar todo el puto día con gente apuntando a lo que hacen o han hecho mal. Para qué se van a tener que estar exponiendo, poniendo su opinión o inviertiendo su energía? Por qué esa gente no está contando en prime time lo que han hecho? por qué no todo el mundo sabe su historia?&lt;/p&gt;

&lt;p&gt;Yo estoy lejos de ser un referente pero aún así ya hay gente que te veta (y no me refiero a que te trate de cancelar, que ese es el menor de los problemas) y te toca los huevos por lo que dices y haces, por tener una opinión [empresarial] que compartes públicamente.&lt;/p&gt;

&lt;p&gt;Esto tiene mucho que ver con la cultura “pro-empresa”. Mientras el empresario sea el enemigo, estamos jodidos.&lt;/p&gt;

&lt;p&gt;Seguramente alguien esté pensando que estoy exagerando pero, dime, cuantos programadores/técnicos españoles conoces detrás de una empresa? empresarios con “salsa” conoces de casualidad al amigo Jose Elías porque se ha puesto a poner videos en youtube y da gracias, así que imaginate a un programador o alguien en la escena técnica levantando una empresa.&lt;/p&gt;

&lt;p&gt;No solo los fundadores son referentes. Hay mucha gente que trinca bastante pasta como empleado, pero… cuantos conoces? ninguno porque no dicen ni pio, lo sabes por corrillos, los intuyes porque de un día para otro hacen pequeñas inversiones como “angel”. Pero eso solo lo ves si estás dentro, poca gente de “fuera” ve que puedes hacer dinero y divertirte (en vez de estar haciendo reuniones con mid managers sobre gilipolleces en el corporate medio)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;…Y clientes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hablando de risk takers. La diferencia cuando vas a una reunión con un empresote americano a una empresa española es brutal. La gente de España normalmente te quiere usar, pero el hecho de ser una empresa jóven te pone unas barreras tremendas. Empezando por el proceso de pasar por múltiples reuniones que aportan poco, luego el proceso de compras, seguridad, compliance y todo el tinglado, una startup no tiene el personal para estar liado 6 meses.&lt;/p&gt;

&lt;p&gt;Que algunas empresas grandes confién en ti es la noche y el día en los primeros días (en una empresa B2B) . Para nosotros el hecho de que una empresa grande nos apoyase al principio fue clave, eternamente agradecido por ello y por todo lo que he aprendido de ellos. Bueno, y ellos de nosotros, seguramente se hayan ahorrado bastantes millones de euros gracias a lo que han aprendido.&lt;/p&gt;

&lt;p&gt;En general, digamos que España es un pais que prefiere pagar por software en hombre-horas. No entendemos que innovar no se hace dando a la manivela sobre lo mismo con mucha gente.&lt;/p&gt;

&lt;p&gt;Y no voy a comentar más de este tema, no soy tan risk taker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Varias décadas de ventaja construyendo tech&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El tiempo no podemos acelerarlo, de momento. Aquí lo único que podemos hacer es hacer lo que ha hecho China: remangarse, apretar los dientes y sufrir como perros. Nos va a tocar vivir un poco peor me temo. O es que nos hemos pensado que las cosas salen símplemente por tener una legislación más favorable?&lt;/p&gt;

&lt;p&gt;Traer gente que lo haya hecho es también importante, pero digamos que no es trivial importar gente. Quien coño quiere venir a España si no es para vivir mejor. Y vivir mejor significa no dejarte los cuernos sacando algo adelante (perdón por la crudeza)&lt;/p&gt;

&lt;p&gt;Repito, hay que sufrir para sacar algo adelante y darte muchísimas hostias antes de llegar a algo que funciona. Mucha gente viene y comenta (por seguir con Freepik, empresa de Cuenca) “pero mira los de Magnific, en 2 años lo han petado y vendido a Freepik”. Bueno, sí, pero no miras los otros +20 años que &lt;a href=&quot;https://x.com/javilop&quot;&gt;Javi&lt;/a&gt; (y Emilio, claro) ha estado haciendo proyectos de tecnología, creando tutoriales de como hacer juegos, etc.&lt;/p&gt;

&lt;p&gt;Y lo creerás o no pero en otros países el trabajo duro está bien visto :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chorradas del gobierno&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Las leyes laborales no están pensadas para startups, están pensadas para gente que va a una cadena de producción, para la gran empresa. Cuando una startup se tiene que adaptar a la ley laboral significa que el riesgo es mayor para el empleado y para la empresa (y no hablo de casos como Glovo). En un mercado de empleados de alta cualificación las reglas del juego actuales son absurdas.&lt;/p&gt;

&lt;p&gt;Quién va a a arriesgarse a algo con muy poca probabilidad de éxito si te puedes ir a un banco, cobrar un sueldo decentísimo, hipoteca al 0%, bonus del 150% anual, guardería, gimnasio y jubilación a los 55? Si no hay un incentivo claro la gente no es gilipollas y va por el camino fácil (pero aburrido, no lo hagas)&lt;/p&gt;

&lt;p&gt;Ojo, no todo el mundo hace las cosas por dinero, la mayoría por suerte no, pero la gente tiene hijos, casas y otros temas y llega un punto, precisamente el punto donde tienes mucha experiencia, que no te apetece correr según que riesgos.&lt;/p&gt;

&lt;p&gt;El tema calentito de las stock options es absurdo, no tiene sentido que tengas que tributar por algo que no existe y seguramente nunca lo haga. Igual que antes, para que me voy a arriesgar si no voy a sacar nada en claro.&lt;/p&gt;

&lt;p&gt;Por otro lado, y perdonen mis amigos abogados: necesitamos sacar un poquito a los abogados de las instituciones (un 50% en toda Europa según Grok deepsearch) y poner más gente que no sea tan conservadora (que es el trabajo de un abogado)&lt;/p&gt;

&lt;p&gt;Para terminar, el otro tema es el exit tax. Si yo tengo una startup con una valoración alta, estoy jodido si quiero irme a otro pais, por ejemplo, para vender a muerte en USA o irme allí a contrar a gente que no hay en España.&lt;/p&gt;

&lt;p&gt;No hablemos de la cultura tecnológica. Ahí tenemos al amigo &lt;a href=&quot;https://x.com/JaimeObregon&quot;&gt;Jaime Gómez-Obregón&lt;/a&gt; sacando las vergüenzas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tecnología&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta no la ha puesto Novoa, pero la pongo yo. A nivel técnico estamos en pañales y nos da exactamente igual. A veces veo a gente cualificada hacer cosas manualmente que, con unas semanas de estudio, podrían automatizar o por lo menos saber que se puede automatizar. Por amor de dios, esa gente luego se monta unos videos en tiktok espectaculares, hay que invertir horas para saber hacer esa mierda.&lt;/p&gt;

&lt;p&gt;Este ejemplo parece muy tonto, pero el hecho de que la gente no vea alcanzable la tecnología como herramienta. Voy un poco más allá. Intenta encontrar gente en España que trabaje en lenguages de bajo nivel, que sepa o esté interesado en software de infra/sistemas (algunos no sabéis ni de lo que estoy hablando, lo cual me da más la razón).&lt;/p&gt;

&lt;p&gt;Intenta encontrar a alguien en España interesado en entrenar modelos base LLM. Hay 4 gatos. Ahora, busca frontend react developer, te salen hasta debajo de las piedras. La razón: nadie se atreve porque no lo han visto. Yo siempre pregunto en las entrevistas técnicas “has entrado a ver como funciona X proyecto a bajo nivel” y raro es cuando alguien ha echado un ojo. No lo miran porque nadie les ha dicho que se puede hacer, no han visto a nadie hacerlo.&lt;/p&gt;

&lt;p&gt;Si no hay densidad de personas así, no hay deep tech y por tanto te dejas fuera las grandes oportunidades. No todo el monte son ecommerces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sobre “la pasta”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En todo este post he estado hablando de pasta, puedes traducirlo directamente a valor, a veces no es directo, pero es una buena forma de entenderlo.&lt;/p&gt;

&lt;p&gt;Como decía, la mayoría de la gente que hacemos esto no estamos aquí por el dinero, de hecho verás mucho emprendedor con exits montando más cosas. No lo hacen por la pasta, lo hacen por que es su motivación, su forma de vivir. &lt;a href=&quot;https://javisantana.substack.com/p/la-ultima-bala&quot;&gt;Yo no entiendo mi vida sin esta mierda&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Soluciones&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El rant es importante y seguramente estarás diciendo, vale, y qué podemos hacer? pues creo que lo único que podemos es remar y ser valientes. Obviamente no hay nada que una persona individual pueda hacer a nivel global, más bien es la contribución de muchas, durante tiempo.&lt;/p&gt;

&lt;p&gt;A nivel individual siempre intento escribir sobre mi experiencia, dar charlas cada vez que me invitan, dar consejo a emprendedores cuando me lo piden. Siempre que veo a alguien joven en la empresa, de los que ves que tienen madera, intento animarle, “cuando montes tu empresa, no hagas esto”, etc. Algunos de ellos se han ido a trabajar a empresotes, con salarios super altos y han decidido no emprender :/&lt;/p&gt;

&lt;p&gt;Es suficiente? ni idea, siempre pienso que podría contar mucho más de cosas de “go to market” de tecnología, company building, cultura, open source, licencias y demás mierdas variadas por las que todo el mundo pasa, especialmente en la fase de 0-10M ARR que es donde creo que puedo aportar más. Sin embargo, para llegar ahí alguien ha tenido que lanzarse a montar algo.&lt;/p&gt;

&lt;p&gt;Donde seguramente pueda aportar muy poco es en intentar convencer/influenciar a algún político. Al menos un diputado me sigue en twitter, pero están más preocupados por otros temas por desgracia.&lt;/p&gt;

&lt;p&gt;Sinceramente, siendo yo poco amigo de la patria en general, últimamente me está doliendo un poco (por no ser más explícito) el tema de US vs EU (especialmente el puto meme del tapón de la botella) y digo yo que algo habrá que hacer. Incluso desde España, que como dice un buen amigo, hemos nacido para salir a ser segundos.&lt;/p&gt;
</description>
                <pubDate>Tue, 25 Feb 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/02/25/espana-y-startups.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/02/25/espana-y-startups.html</guid>
            </item>
            
        
            
            <item>
                <title>Terminado y cerrado</title>
                <description>&lt;p&gt;Me gustan los coches, si por mi fuese tendría un parking con una casa pegada. No estoy ahí -aun- pero tengo la suerte de tener algún que otro coche de fin de semana. Uno de ellos, un Lotus Elise, es un coche peculiar, es un coche diseñado a mediados de los 90 con una premisa clarísima: ser lo mas ligero posible. Es una auténtica obra de ingeniería, aunque la intra historia tiene bastante más de cinta aislante que de glamour. Bueno, como todas las historias que salen bien. Hay un documental &lt;a href=&quot;https://www.youtube.com/watch?v=a-TiFKr2yb0&quot;&gt;por aquí&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para ilustrar, os dejo una foto del susodicho en &lt;a href=&quot;https://www.circuitricardotormo.com/&quot;&gt;el mejor circuito del mundo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/b95bb46d-70a1f30e-e2a5-4e16-8ff6-afd4c2c6f7cd_1500x500.jpeg&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Lo llevaron al extremo, y cuando llevas las cosas al extremo o bien sale algo muy complejo o muy sencillo, Puedes ver la imagen del interior del coche debajo de estas lineas para saber lo que fue en este caso. No quiere decir que no tenga innovaciones súper interesantes, ya sabes que cuando pones límites siempre salen soluciones diferentes, te obliga a pensar, y pensando, echando horas, es como salen las cosas. Si quieres leer más, échale un ojo a la biografía de Colin Chapman o escucha alguna entrevista de los diseñadores del Lotus Elise, Julian Thomson y Richard Rackham, se aprende mucho de diseño de producto. Te dejo esta maravilla de quote de Dave Minter, que de nuevo, tiene mucho que ver con hacer producto.&lt;/p&gt;

&lt;blockquote&gt;

&lt;/blockquote&gt;

&lt;p&gt;Lotus never used to take cars back in transporting, always drove them. And quite rightly, you can learn so much about the car just driving it. It was always said you have got to pretend you are a customer… we were really trying to develop a car&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog_images/imported/f9e992fb-aa9ed25e-1742-46f8-99cb-e65eed317d21_800x450.jpeg&quot; alt=&quot;Image&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;En España solo hay un concesionario oficial Lotus, así que depende de donde vivas te toca hacerlo todo a ti mismo, no puedes transportar el coche 800km para hacer un simple cambio de aceite. Por suerte con unos vídeos de YouTube, una comunidad en WhatsApp, un poco de maña o tiempo, y tienes el 99% resuelto.&lt;/p&gt;

&lt;p&gt;La cosa no queda ahí, uso este coche para ir al circuito y hay ciertas cosas que hay que hacer para evitar matarte: cambiar aceite regularmente, comprobar pastillas, verificar que el circuito de frenos está bien, comprobar que los tornillos están apretados como tocan, revisar si hay algo doblado, agrietado, y ver si hay alguna pérdida de aceite que pueda terminar en un incendio. No te quieres jugar el tipo en una frenada.&lt;/p&gt;

&lt;p&gt;El hecho de que puedas modificar y “reparar” tu mismo el coche te hace apreciarlo más. Sabes de qué pie cojea cuando empieza a hacer un ruido, te sientes orgulloso cuando algo funciona como debe. Esto ya de por si hace que automáticamente sea tu coche favorito, por muy buenos que sean los otros.&lt;/p&gt;

&lt;p&gt;Pero eso no es lo mejor, viniendo del mundo del software, donde nada está acabado, es reconfortante hacer algo y terminarlo. Una vez aprietas el último tornillo ya está, todo listo, está terminado, el producto está completo. En mundo del software es un fractal, siempre puedes seguir cambiando y añadiendo de forma sencilla, no existen las limitaciones de las leyes de la física.&lt;/p&gt;

&lt;p&gt;Es muy reconfortante ver un producto terminado. Podría ser mejor, podría mejorarse el diseño, pero alguien en un momento determinado lo cerró y es lo que salió. Nadie más va a cambiarlo, es lo que hay. El mundo del software nunca nada está terminado y no deja de ser una fuente de frustración constante. Imagino que los diseñadores de cosas ligadas a leyes de la física sueñan con tener la elasticidad del software, pero como dije antes, las restricciones a veces son buenas.&lt;/p&gt;

&lt;p&gt;A veces que el mundo del software no se respeta a si mismo, y a su trabajo, lo suficiente. Todo es de usar y tirar.&lt;/p&gt;
</description>
                <pubDate>Sun, 23 Feb 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/02/23/terminado-y-cerrado.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/02/23/terminado-y-cerrado.html</guid>
            </item>
            
        
            
            <item>
                <title>Building a simple SQL Agent from Scratch</title>
                <description>&lt;p&gt;More people are talking about AI Agents than actual agents exist. And likely the ratio of builders to people talking about building is 1:10000 and still here I am talking about agents, so looks like I’m contributing with more shit. I hope not but you know, I’m just a guy with a blog.&lt;/p&gt;

&lt;p&gt;(BTW, if you want to read the code with a better formatting, just read the post &lt;a href=&quot;https://javisantana.com/2025/01/25/sql-agent.html&quot;&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I wanted to create a really simple SQL Agent to teach myself how to do it, no libraries to simplify the process, just a bash script using the llm cli tool. I don’t actually like bash so much but it’s somehow limited so that allows me to focus on the actual problem.&lt;/p&gt;

&lt;p&gt;The basic algorithm to generate a working SQL based on a user question would work like (python-ish):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chat = LLM(system=&quot;You are an expert SQL query generator, write the SQL given the prompt.&quot;)
answer = chat(prompt)
while not is_correct(answer):
    answer = chat(answer + &quot;.Please fix the SQL query.&quot;)
print(answer)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, in theory, I’d just need to write a function (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_correct&lt;/code&gt;) that tells the model if the SQL is rigth, and the initial prompt. In theory.&lt;/p&gt;

&lt;p&gt;Ok, so let’s build a super simple agent that write SQL given a prompt. For simplicity the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_correct&lt;/code&gt; function will return “OK” if the SQL runs. It’s is not a good criterion but it’s a good starting point to understand the dynamics. I’m using DuckDB (a embedded analytics database I’m testing lately) as runtime (so I don’t need to run a fully fledged database server)&lt;/p&gt;

&lt;h2 id=&quot;1-generate-a-sql-query-for-a-given-prompt&quot;&gt;&lt;strong&gt;1. Generate a SQL query for a given prompt&lt;/strong&gt;&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;LLM=&quot;llm -m gemini-2.0-flash-exp&quot;
prompt=&quot;You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. Generate a SQL query for: $1&quot;
flags=&quot;&quot;

while true; do
    # Generate SQL using LLM
    SQL=$($LLM $flags &quot;$prompt&quot;)
    # Remove any ``` markers from the SQL
    SQL=$(echo &quot;$SQL&quot; | sed &apos;s/^```sql$//g&apos; | sed &apos;s/```$//g&apos; | sed &apos;s/^```//g&apos;)
    
    # Try executing the SQL with DuckDB and capture the error output
    ERROR=$(echo &quot;$SQL&quot; | duckdb 2&amp;gt;&amp;amp;1)
    if [ $? -ne 1 ]; then
        echo &quot;*** DONE!&quot;
        echo &quot;$SQL&quot;
        break
    else
        echo &quot;*** Invalid SQL generated, retrying...&quot;
        echo &quot;*** Error: $ERROR&quot;
        # Include the error in the next LLM prompt
        prompt=&quot;Previous attempt failed with error: $ERROR Please fix the SQL query.&quot;
        flags=&quot;-c&quot; # to tell LLM to continue the conversation
        sleep 1
    fi
done

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It kind of works. If you test with prompts that don’t require any table, it gives you pretty decent SQL queries.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./sqlagent_1.sh &quot;generate a the fibonacci series&quot;
./sqlagent_1.sh &quot;generate the game of life&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the game of life needs a few iterations but that’s ok, it’s a recursive query. I coded that query myself a few years ago and it took me an afternoon.&lt;/p&gt;

&lt;p&gt;But if we ask for stuff like&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./sqlagent_1.sh &quot;generate a histogram with table http_requests&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It fails. It does because we are asking to do something that can’t do as the http_requests table doesn’t exist. Humans are constanty asking for stuff that is not possible, so we need a way to stop the agent to try. Is there a way so the LLM can say “enough is enough” and stop trying?&lt;/p&gt;

&lt;p&gt;To be honest, sometimes it finds the way and generates things like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT
    CASE 
        WHEN request_time &amp;lt; 1 THEN 1
        WHEN request_time &amp;lt; 2 THEN 2
        WHEN request_time &amp;lt; 3 THEN 3
        WHEN request_time &amp;lt; 4 THEN 4
        WHEN request_time &amp;lt; 5 THEN 5
        WHEN request_time &amp;lt; 6 THEN 6
        WHEN request_time &amp;lt; 7 THEN 7
        WHEN request_time &amp;lt; 8 THEN 8
        WHEN request_time &amp;lt; 9 THEN 9
        ELSE 10
    END AS bucket,
    count(*) AS count
FROM (SELECT random()*10 as request_time FROM range(100))
GROUP BY bucket
ORDER BY bucket;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-2-trying-to-stop-the-agent&quot;&gt;&lt;strong&gt;Step 2: trying to stop the agent&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;So I tried adding a “If you unable to fix it, just return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select &apos;STOP&apos;&lt;/code&gt;” to the prompt but it does not work :)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;prompt=&quot;Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select &apos;STOP&apos;`.&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But being more explicit and setting the prompt to:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;prompt=&quot;Previous attempt failed with error: $ERROR Please fix the SQL query. If you can&apos;t find the tables just return select &apos;STOP&apos;.&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;it works (added the iteration number as debugging info)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-- [ITERATION 0] --------------------------------------
*** Invalid SQL generated, retrying...
*** Error: Catalog Error: Table with name http_requests does not exist!
Did you mean &quot;pg_sequences&quot;?
LINE 5:     http_requests
            ^
-- [ITERATION 1] --------------------------------------
*** DONE!

SELECT &apos;STOP&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-3-using-some-the-data-inside-the-database&quot;&gt;&lt;strong&gt;Step 3: using some the data inside the database&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Generating a query that do not read any data is not really useful, so let’s add a step where we pass the duckdb database to the agent so it can use the data inside it.&lt;/p&gt;

&lt;p&gt;There are just two changes to the agent: 1) we pass to the prompt a list of columns for each table with the type (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;information_schema.columns&lt;/code&gt; table) 2) we pass the database to the agent so it can use the data inside the database.&lt;/p&gt;

&lt;p&gt;In this case I’m using a duckdb database I generated with all the views I track on this website. It looks like this&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;✗ duckdb test.db -c &quot;describe web_requests&quot; 
┌─────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│ column_name │ column_type │  null   │   key   │ default │  extra  │
│   varchar   │   varchar   │ varchar │ varchar │ varchar │ varchar │
├─────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ timestamp   │ UINTEGER    │ YES     │         │         │         │
│ session_id  │ VARCHAR     │ YES     │         │         │         │
│ action      │ VARCHAR     │ YES     │         │         │         │
│ version     │ VARCHAR     │ YES     │         │         │         │
│ payload     │ VARCHAR     │ YES     │         │         │         │
└─────────────┴─────────────┴─────────┴─────────┴─────────┴─────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/bash

LLM=&quot;llm -m gemini-2.0-flash-exp&quot;

DB=$1

# Get the list of columns for each table
tables=$(duckdb $DB -c &quot;select table_name, column_name, data_type, is_nullable  from information_schema.columns&quot;)

prompt=&quot;You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. The list of columns for each table is:\n $tables. Generate a SQL query for: $2&quot;
flags=&quot;&quot;

ITERATION=0
while true; do
    # Generate SQL using LLM
    echo &quot;-- [ITERATION $ITERATION] --------------------------------------&quot;
    SQL=$($LLM $flags &quot;$prompt&quot;)
    # Remove any ``` markers from the SQL
    SQL=$(echo &quot;$SQL&quot; | sed &apos;s/^```sql$//g&apos; | sed &apos;s/```$//g&apos; | sed &apos;s/^```//g&apos;)
    
    # Try executing the SQL with DuckDB and capture the error output
    ERROR=$(echo &quot;$SQL&quot; | duckdb $DB 2&amp;gt;&amp;amp;1)
    if [ $? -ne 1 ]; then
        echo &quot;*** DONE!&quot;
        echo &quot;$SQL&quot;
        res=$(duckdb $DB -c &quot;$SQL&quot;)
        SQL=$($LLM $flags &quot;the query ran successfully, check the results and return &apos;select &apos;STOP&apos; if you think is that what the initial prompt asked for. Otherwise, return a new the SQL query.&quot;)
        echo &quot;$res&quot;
        SQL=$(echo &quot;$SQL&quot; | sed &apos;s/^```sql$//g&apos; | sed &apos;s/```$//g&apos; | sed &apos;s/^```//g&apos;)
        if [[ &quot;$SQL&quot; == *&quot;STOP&quot;* ]]; then
            break
        fi
    else
        echo &quot;*** Invalid SQL generated, retrying...&quot;
        echo &quot;*** Error: $ERROR&quot;
        # Include the error in the next LLM prompt
        #prompt=&quot;Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select &apos;STOP&apos;`.&quot;
        prompt=&quot;Previous attempt failed with error: $ERROR\n Please fix the SQL query. If you don&apos;t find the tables just return select &apos;STOP&apos;.&quot;
        flags=&quot;-c&quot; # to tell LLM to continue the conversation
        sleep 1
    fi
    ITERATION=$((ITERATION + 1))
done

duckdb $DB -c &quot;$SQL&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When running it, it can generate queries but the results are not good.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;✗ ./sqlagent_3.sh test.db &quot;visitors per day&quot;
-- [ITERATION 0] --------------------------------------
*** Invalid SQL generated, retrying...
*** Error: Catalog Error: Scalar Function with name date does not exist!
Did you mean &quot;datesub&quot;?
...
                    ^
-- [ITERATION 8] --------------------------------------
*** DONE!

SELECT CAST(TO_TIMESTAMP(timestamp/1000) AS DATE) AS day, COUNT(DISTINCT session_id) AS visitors
FROM web_requests
GROUP BY day
ORDER BY day;
┌────────────┬──────────┐
│    day     │ visitors │
│    date    │  int64   │
├────────────┼──────────┤
│ 1970-01-21 │     7502 │
└────────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I see many ways to fix this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run the query and ask the LLM if it thinks it correct based on the initial prompt.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Give more info about the data (like a sample) so in the case of the example, it can actually see how the timestamp look like&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put humans in the loop so it can give more context, save the context for future runs and so on. Actually that’s what people are doing, check this example from Uber on how they do it: &lt;a href=&quot;https://www.uber.com/en-TW/blog/query-gpt/&quot;&gt;QueryGPT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I tested 1 (as you see) and it does not work well, and tested 3 that, of course, works.&lt;/p&gt;

&lt;p&gt;And this could go on and on, but I think I now understand how to build an super simple SQL Agent and I hope you did too.&lt;/p&gt;
</description>
                <pubDate>Mon, 27 Jan 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/01/27/building-a-simple-sql-agent-from.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/01/27/building-a-simple-sql-agent-from.html</guid>
            </item>
            
        
            
            <item>
                <title>Building a simple SQL Agent from Scratch</title>
                <description>&lt;p&gt;There are more people talking about AI Agents than actual agents. And likely the ratio of builders to people talking about building is 1:10000 and still here I am talking about agents, so looks like I’m contributing with more shit. I hope not but you know, I’m just a guy with a blog.&lt;/p&gt;

&lt;p&gt;I wanted to create a really simple SQL Agent to teach myself how to do it, no libraries to simplify the process, just a bash script using the &lt;a href=&quot;https://llm.datasette.io/en/stable/index.html&quot;&gt;llm cli tool&lt;/a&gt;. I don’t actually like bash so much but it’s somehow limited so that allows me to focus on the actual problem.&lt;/p&gt;

&lt;p&gt;The basic algorithm to generate a working SQL based on a user question would work like (python-ish):&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;You are an expert SQL query generator, write the SQL given the prompt.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_correct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.Please fix the SQL query.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, in theory, I’d just need to write a function (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_correct&lt;/code&gt;) that tells the model if the SQL is rigth, and the initial prompt. In theory.&lt;/p&gt;

&lt;p&gt;Ok, so let’s build a super simple agent that write SQL given a prompt. For simplicity the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_correct&lt;/code&gt; function will return “OK” if the SQL runs. It’s is not a good criterion but it’s a good starting point to understand the dynamics. I’m using DuckDB (a embedded analytics database I’m testing lately) as runtime (so I don’t need to run a fully fledged database server)&lt;/p&gt;

&lt;h2 id=&quot;1-generate-a-sql-query-for-a-given-prompt&quot;&gt;1. Generate a SQL query for a given prompt&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;LLM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;llm -m gemini-2.0-flash-exp&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. Generate a SQL query for: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Generate SQL using LLM&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LLM&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$flags&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$prompt&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Remove any ``` markers from the SQL&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^```sql$//g&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/```$//g&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^```//g&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;# Try executing the SQL with DuckDB and capture the error output&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | duckdb 2&amp;gt;&amp;amp;1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-ne&lt;/span&gt; 1 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*** DONE!&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;break
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*** Invalid SQL generated, retrying...&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*** Error: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ERROR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Include the error in the next LLM prompt&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Previous attempt failed with error: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ERROR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; Please fix the SQL query.&quot;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-c&quot;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# to tell LLM to continue the conversation&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi
done&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It kind of works. If you test with prompts that don’t require any table, it gives you pretty decent SQL queries.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./sqlagent_1.sh &lt;span class=&quot;s2&quot;&gt;&quot;generate a the fibonacci series&quot;&lt;/span&gt;
./sqlagent_1.sh &lt;span class=&quot;s2&quot;&gt;&quot;generate the game of life&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the game of life needs a few iterations but that’s ok, it’s a recursive query. I coded that query myself a few years ago and it took me an afternoon.&lt;/p&gt;

&lt;p&gt;But if we ask for stuff like&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./sqlagent_1.sh &lt;span class=&quot;s2&quot;&gt;&quot;generate a histogram with table http_requests&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It fails. It does because we are asking to do something that can’t do as the http_requests table doesn’t exist. Humans are constanty asking for stuff that is not possible, so we need a way to stop the agent to try. Is there a way so the LLM can say “enough is enough” and stop trying?&lt;/p&gt;

&lt;p&gt;To be honest, sometimes it finds the way and generates things like:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;CASE&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_time&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-2-trying-to-stop-the-agent&quot;&gt;Step 2: trying to stop the agent&lt;/h2&gt;

&lt;p&gt;So I tried adding a “If you unable to fix it, just return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select &apos;STOP&apos;&lt;/code&gt;” to the prompt but it does not work :)&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Previous attempt failed with error: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ERROR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; Please fix the SQL query. If you are not able to fix it, just return &lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;STOP&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But being more explicit and setting the prompt to:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Previous attempt failed with error: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ERROR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; Please fix the SQL query. If you can&apos;t find the tables just return select &apos;STOP&apos;.&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;it works (added the iteration number as debugging info)&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ITERATION 0] &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Invalid SQL generated, retrying...
&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Error: Catalog Error: Table with name http_requests does not exist!
Did you mean &lt;span class=&quot;s2&quot;&gt;&quot;pg_sequences&quot;&lt;/span&gt;?
LINE 5:     http_requests
            ^
&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ITERATION 1] &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; DONE!

SELECT &lt;span class=&quot;s1&quot;&gt;&apos;STOP&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-3-using-some-the-data-inside-the-database&quot;&gt;Step 3: using some the data inside the database&lt;/h2&gt;

&lt;p&gt;Generating a query that do not read any data is not really useful, so let’s add a step where we pass the duckdb database to the agent so it can use the data inside it.&lt;/p&gt;

&lt;p&gt;There are just two changes to the agent:
1) we pass to the prompt a list of columns for each table with the type (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;information_schema.columns&lt;/code&gt; table)
2) we pass the database to the agent so it can use the data inside the database.&lt;/p&gt;

&lt;p&gt;In this case I’m using a duckdb database I generated with all the views I track on this website. It looks like this&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;✗ duckdb test.db &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;describe web_requests&quot;&lt;/span&gt; 
┌─────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│ column_name │ column_type │  null   │   key   │ default │  extra  │
│   varchar   │   varchar   │ varchar │ varchar │ varchar │ varchar │
├─────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ timestamp   │ UINTEGER    │ YES     │         │         │         │
│ session_id  │ VARCHAR     │ YES     │         │         │         │
│ action      │ VARCHAR     │ YES     │         │         │         │
│ version     │ VARCHAR     │ YES     │         │         │         │
│ payload     │ VARCHAR     │ YES     │         │         │         │
└─────────────┴─────────────┴─────────┴─────────┴─────────┴─────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The code:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;LLM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;llm -m gemini-2.0-flash-exp&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Get the list of columns for each table&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;tables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;duckdb &lt;span class=&quot;nv&quot;&gt;$DB&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;select table_name, column_name, data_type, is_nullable  from information_schema.columns&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;You are an expert SQL for duckdb query generator. Do not write any explainations, just the SQL query. The list of columns for each table is:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$tables&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;. Generate a SQL query for: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;ITERATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Generate SQL using LLM&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-- [ITERATION &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ITERATION&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;] --------------------------------------&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LLM&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$flags&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$prompt&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Remove any ``` markers from the SQL&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^```sql$//g&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/```$//g&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^```//g&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;# Try executing the SQL with DuckDB and capture the error output&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;ERROR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | duckdb &lt;span class=&quot;nv&quot;&gt;$DB&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-ne&lt;/span&gt; 1 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*** DONE!&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;duckdb &lt;span class=&quot;nv&quot;&gt;$DB&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LLM&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$flags&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;the query ran successfully, check the results and return &apos;select &apos;STOP&apos; if you think is that what the initial prompt asked for. Otherwise, return a new the SQL query.&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$res&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;SQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^```sql$//g&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/```$//g&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/^```//g&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;STOP&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
            &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;break
        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi
    else
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*** Invalid SQL generated, retrying...&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*** Error: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ERROR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Include the error in the next LLM prompt&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;#prompt=&quot;Previous attempt failed with error: $ERROR Please fix the SQL query. If you are not able to fix it, just return `select &apos;STOP&apos;`.&quot;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Previous attempt failed with error: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ERROR&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; Please fix the SQL query. If you don&apos;t find the tables just return select &apos;STOP&apos;.&quot;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-c&quot;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# to tell LLM to continue the conversation&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ITERATION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;ITERATION &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done

&lt;/span&gt;duckdb &lt;span class=&quot;nv&quot;&gt;$DB&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SQL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When running it, it can generate queries but the results are not good.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;✗ ./sqlagent_3.sh test.db &lt;span class=&quot;s2&quot;&gt;&quot;visitors per day&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ITERATION 0] &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Invalid SQL generated, retrying...
&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; Error: Catalog Error: Scalar Function with name &lt;span class=&quot;nb&quot;&gt;date &lt;/span&gt;does not exist!
Did you mean &lt;span class=&quot;s2&quot;&gt;&quot;datesub&quot;&lt;/span&gt;?
...
                    ^
&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ITERATION 8] &lt;span class=&quot;nt&quot;&gt;--------------------------------------&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;***&lt;/span&gt; DONE!

SELECT CAST&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;TO_TIMESTAMP&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;timestamp/1000&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; AS DATE&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; AS day, COUNT&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;DISTINCT session_id&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; AS visitors
FROM web_requests
GROUP BY day
ORDER BY day&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
┌────────────┬──────────┐
│    day     │ visitors │
│    &lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt;    │  int64   │
├────────────┼──────────┤
│ 1970-01-21 │     7502 │
└────────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I see many ways to fix this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run the query and ask the LLM if it thinks it correct based on the initial prompt.&lt;/li&gt;
  &lt;li&gt;Give more info about the data (like a sample) so in the case of the example, it can actually see how the timestamp look like&lt;/li&gt;
  &lt;li&gt;Put human in the loop so it can give more context, save the context for future runs and so on. Actually that’s what people are doing, check this example from Uber on how they do it: &lt;a href=&quot;https://www.uber.com/en-TW/blog/query-gpt/&quot;&gt;QueryGPT&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I tested 1 (as you see) and it does not work well, and tested 3 that, of course, works.&lt;/p&gt;

&lt;p&gt;And this could go on and on, but I think I now understand how to build an super simple SQL Agent and I hope you did too.&lt;/p&gt;

</description>
                <pubDate>Sat, 25 Jan 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/01/25/sql-agent.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/01/25/sql-agent.html</guid>
            </item>
            
        
            
            <item>
                <title>You don't learn, then start</title>
                <description>&lt;p&gt;“You don’t learn, then start. You start, then learn”
&lt;em&gt;- Sahil Lavingia, founder of Gumroad&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m amazed by how many people don’t use this way of learning.&lt;/p&gt;
</description>
                <pubDate>Thu, 23 Jan 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/inspiration/2025-01-23-you-dont-learn.html</link>
                <guid isPermaLink="true">http://javisantana.com/inspiration/2025-01-23-you-dont-learn.html</guid>
            </item>
            
        
            
            <item>
                <title>I love SQL</title>
                <description>&lt;p&gt;I used to dislike SQL. I learned to deal with databases using ORMs and when I needed to open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;psql&lt;/code&gt; to understand what was going on I felt like I was losing my time dealing with SQL. Why would someone want to deal with SQL when having a simple API in the language you are working with?&lt;/p&gt;

&lt;p&gt;After some time I moved to the database infra space (as CTO of a company dealing with hundreds of postgres databases) and I started to work with SQL every day. It was not that bad and I learned to appreciate it little by little. And I finally loved it, I have done all sorts of crazy stuff with it, like rendering my company logo, &lt;a href=&quot;https://x.com/javisantana/status/1259535333043077124&quot;&gt;recreating a game of life simulation&lt;/a&gt; and starting a company (that raised +50M) where the only interface is SQL.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, I don’t think ORMs are useless, quite the opposite, I think they are good and developers should use them (they help you to avoid making mistakes) but that shouldn’t stop someone from learning SQL. You are going to find it somewhere in your professional career, there are many systems that only talk SQL (actually all the database systems end up talking SQL, hello MongoDB). And being honest, any developer can learn the basics of SQL in a few hours. That plus a few LLM prompts and you are good to go for 90% of the use cases. Learning a little bit of database internals is also good, that’ll put you in the top 1% of developers worldwide.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;there is one thing about SQL that amazes me: the runtime. It’s so simple, a simple text language, you send it over the wire and boom, you have results&lt;/strong&gt;. No &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt;, just a simple language, focused on a simple (but powerful) task, that  lived many iterations of databases and systems over 50 years (!!!). That’s hard to beat, I don’t know so many designs that survived that long.&lt;/p&gt;

&lt;p&gt;You have local runtimes (sqlite, duckdb), you can install postgres, mysql… you can go to neon and get a postgres database in a few milliseconds. You don’t even need data locally, you can query data in S3 and other places, you can even query data coming from other processes, it’s just amazing how simple it is. There is no other programming language with this simplicity.&lt;/p&gt;

&lt;p&gt;I know SQL is not perfect when you need to work with complex logic. You can always twist it but you end up with crazy 2000 line SQL queries. In 20 years working with it I only found a few cases where it gets actually unmanageable to run data operations but many times it’s easy to use any other procedural language. You need to like functional programming as well, but you get used to thinking in that way after a while.&lt;/p&gt;

&lt;p&gt;I’ll probably end my career writing SQL or talking to an LLM that writes SQL :)&lt;/p&gt;
</description>
                <pubDate>Thu, 16 Jan 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/2025/01/16/i-love-sql.html</link>
                <guid isPermaLink="true">http://javisantana.com/2025/01/16/i-love-sql.html</guid>
            </item>
            
        
            
            <item>
                <title>Owning your own internet space</title>
                <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve always been a firm believer in owning your own internet space, and I continued to do that by blogging&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://newsletter.vickiboykis.com/archive/everything-i-did-in-2024/&quot;&gt;Vicky Boykis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Websites seem to be less and less important as everybody is just looking at 10 second videos all the time. I love having my space, with my name on it, where I talk about what I like, with my own design and shape.&lt;/p&gt;
</description>
                <pubDate>Sat, 04 Jan 2025 00:00:00 +0000</pubDate>
                <link>http://javisantana.com/inspiration/2025-01-04-owning-your-internet-space.html</link>
                <guid isPermaLink="true">http://javisantana.com/inspiration/2025-01-04-owning-your-internet-space.html</guid>
            </item>
            
        
    </channel>
</rss>
