Cómo organicé mi viaje a Japón con un skill de Hermes Agent

Cómo organicé mi viaje a Japón con Hermes Agent: un skill con cron, Google Maps y Telegram que envía el itinerario por mail y evita repetir comidas.

Guido Joan Pereyra Aprile · 17 de octubre de 2026 · 12 min de lectura

Cómo organicé mi viaje a Japón con un skill de Hermes Agent

Cómo organicé mi viaje a Japón con un skill de Hermes Agent

Me quedé una semana antes de volar a Japón sin saber qué iba a hacer durante 15 días. Llevaba un año con mucho trabajo y metido en IA agéntica con proyectos personales, y la logística se me había quedado sin tocar. Decidí organizar mi viaje a Japón con Hermes Agent: un skill que lee un markdown con vuelos y hoteles, y cada noche me manda por email un itinerario con sitios, transporte, restaurantes y presupuesto. Aquí documento el diseño, los cinco problemas reales que me encontré (Tabelog, comidas repetidas, restaurantes que rechazan turistas, excursiones improvisadas y fallos del cron) y cómo quedó resuelto cada uno.

Contenido

  • Cómo organizar un viaje a Japón con Hermes Agent: arquitectura del skill

  • Qué hacer cuando Tabelog limita las peticiones por IP

  • Por qué Tabelog te sugiere restaurantes que no aceptan turistas

  • Cómo evitar que el agente repita comidas en el itinerario

  • Cómo gestionar excursiones improvisadas en un skill de Hermes Agent sin emails duplicados

  • Cómo mantener actualizada la lista de comidas probadas

  • Errores del cron al enviar el itinerario por email

Cómo organizar un viaje a Japón con Hermes Agent: arquitectura del skill

Partí de una idea mínima, con tres piezas:

  1. Una fuente de verdad en markdown con vuelos, hoteles, fechas y ubicación exacta. En la versión actual vive en /Users/hermes-agent/.hermes/skills/viaje-japon/plan-viaje.md.

  2. Un skill (japan-travel-guide) que cada día consulta ese archivo, averigua dónde estoy y qué ciudad toca, y compone el itinerario.

  3. Un cron que lo ejecuta y envía el resultado por email.

Como mis viajes son sobre todo gastronómicos, el itinerario tenía que incluir restaurantes. Mi criterio inicial: más de 4,5 de puntuación con más de 700 votos.

Para no cambiar de hotel cada dos días hicimos base en ciudades grandes y excursiones de un día desde ahí. La tabla de la versión 2.0.0 del skill quedó así:

Noches

Ciudad

Hotel

12–16 sep

Tokio (Asakusa)

the b asakusa

16–17 sep

Hakone

Hakone Onsen Yuyado Yamanoshou

17–23 sep

Kioto

hotel tou nishinotoin kyoto

23–26 sep

Tokio (Shibuya)

sequence MIYASHITA PARK

El frontmatter del skill es corto: name, description, version y tags (travel, japan, cron). Todo lo importante está en el cuerpo, escrito como reglas numeradas y un workflow del cron. Esa decisión fue la más rentable del proyecto. Un agente que se ejecuta sin supervisión necesita reglas enumeradas, con casos límite y respuestas literales. Por ejemplo: si no hay día de viaje, responde exactamente NO-TRIP. Cada vez que algo fallaba, el arreglo era añadir o endurecer una regla, no tocar código.

Los primeros días funcionaron bien. En Asakusa, el itinerario me llevó a Sensō-ji, y la pagoda de cinco pisos iluminada de noche, con el remate dorado recortado contra un cielo gris oscuro, fue una de las mejores fotos del viaje. Pero pronto empezaron los problemas.

Qué hacer cuando Tabelog limita las peticiones por IP

Había leído que Tabelog es la app de restaurantes de referencia en Japón, así que le pedí al agente que consultara primero allí la puntuación y la contrastara con Google Maps.

El primer problema apareció enseguida: Tabelog limita las peticiones por IP. Un agente que verifica varios restaurantes por comida, tres comidas al día, genera mucho tráfico en poco tiempo. Y el cron lo ejecuta desde la misma IP cada noche.

Cómo organicé mi viaje a Japón con un skill de Hermes Agent

Mis opciones eran dos: racionar las consultas a Tabelog o abandonarlo. Racionar tenía un coste oculto: si el agente se queda sin cuota a mitad de la verificación, produce datos a medias sin avisar. Todavía no había descubierto el segundo problema de Tabelog (el siguiente apartado), pero este ya me hacía dudar de la dependencia.

Al final tomé las dos decisiones juntas, y la regla 7 del skill quedó así:

Restaurants — Google Maps is the ONLY rating source. Rank by Google rating, prefer ≥ 4.5 with solid review count (4.3–4.5 acceptable if the specialty justifies it). Never fabricate a rating — "sin verificar" if not found. No Tabelog ratings.

Esa línea hace tres cosas. Elimina el bloqueo por IP. Relaja el umbral cuando la especialidad lo justifica (4,3–4,5), porque con 4,5 y 700 votos a secas dejaba fuera sitios excelentes de barrio. Y obliga a escribir «sin verificar» antes que inventar una nota. Con LLMs, esa última cláusula no es opcional.

La regla 8 protege la fuente nueva: nada de navegador en vivo contra Google Maps y un máximo de unas 15–20 consultas web de verificación por ejecución. Prefiero perder una verificación a quemar la fuente.

Por qué Tabelog te sugiere restaurantes que no aceptan turistas

Este fue el fallo más molesto en la práctica. Tabelog es tan local que el agente me proponía restaurantes muy caseros, donde el personal no estaba acostumbrado a tratar con extranjeros. Varias veces no nos quisieron recibir. Llegábamos con hambre, tras una caminata, a un local pequeño que ya tenía colas o reservas internas, o directamente sin idioma común, y nos despachaban en la puerta.

El problema es de sesgo de la fuente. Tabelog puntúa a los locales por los locales. Un 3,8 en Tabelog suele equivaler a un sitio excelente, pero eso no dice nada de si aceptará a dos turistas sin reserva. Cambiar a Google Maps mitigó ese sesgo, porque sus reseñas incluyen muchos visitantes extranjeros y la ficha suele indicar si hay menú en inglés o si hace falta reservar.

Además, cambié el formato: en lugar de un único restaurante por comida, el skill debe dar al menos tres opciones por comida (desayuno, almuerzo y cena), cerca de donde estoy en ese punto del itinerario y diferenciadas por estilo o precio (clásico, barato, moderno). Cada opción lleva nota de Google, número de reseñas, precio por persona, si necesita reserva y enlace de Maps. Si el primer sitio no nos recibe, tenemos dos alternativas a pocos minutos. En el desayuno pido exactamente dos opciones, preferiblemente cafeterías de especialidad con horario verificado.

El resultado se vio en el plato. En una comida cerca de Kioto me tocó un cuenco de pollo a la parrilla sobre arroz, con cebollino fresco, puerro asado y una montaña de nori en tiras, junto a una cerveza. Era un sitio fiable para extranjeros y con buena nota verificada, justo lo que buscaba.

Cómo evitar que el agente repita comidas en el itinerario

El segundo problema era más sutil. Ya había comido ramen el día anterior y el itinerario me lo volvía a sugerir. El agente no tenía memoria de lo que yo había comido, solo de lo que él había sugerido, y eso es muy distinto.

La solución fue convertir la comida en estado explícito. Creé references/comidas.md, una lista por ciudad de platos que quiero probar, con tres estados:

  • [ ] pendiente.

  • [x] comido, con lugar y fecha.

  • 🔁 repetible: se puede volver a sugerir.

La regla 5 del skill dice que solo sugiere platos que yo he añadido a esa lista y nunca uno ya comido salvo que sea repetible. Por Telegram escribo «añade <comida> a <ciudad>» y el agente lo apende al archivo y lo confirma en una línea.

Cómo organicé mi viaje a Japón con un skill de Hermes Agent

Añadí además una cláusula para el caso en que una ciudad se queda sin pendientes: no mandar un informe de comidas vacío, sino buscar las especialidades típicas locales de esa ciudad y orientar las opciones hacia ellas. Solo me pregunta por Telegram si ni así encuentra especialidades fiables. En ese caso el informe avisa de que las propuestas no vienen de comidas.md.

Con la misma lógica nació references/zonas.md: puedo pedir «zona <atracción> en <ciudad> el <fecha o libre>» y el skill la cubre primero. Si sobra material, elige lo esencial y deja el resto pendiente; si falta, completa con barrios cercanos. En Kioto, por ejemplo, el recorrido por Kiyomizu-dera salió de ahí. La imagen de dos mujeres en kimono subiendo la escalera hacia la pagoda roja bajo el pórtico de entrada es de ese día.

Cómo gestionar excursiones improvisadas en un skill de Hermes Agent sin emails duplicados

Este es el problema que más me costó razonar. Nuestras excursiones de un día las decidíamos la víspera en función del clima. El skill, en cambio, necesita anticipación y odia la sorpresa. Añadí una regla: cualquier día puedo mandar un mensaje por Telegram («voy a <ciudad> el <fecha>») y el skill ajusta el itinerario del día siguiente.

Al poco empezaron los síntomas: me llegaban dos emails para el mismo día, uno para la ciudad planeada y otro para la improvisada. Y la lista de comidas quedaba incoherente.

Seguí el rastro hasta dos causas:

  1. Dos caminos de generación. El cron generaba el informe del día D con el estado que había a las 21:00. Si yo mandaba el aviso de excursión después, el manejador de Telegram también disparaba una generación. Eran dos ejecuciones independientes, sin saber una de la otra.

  2. Estado que se acumulaba en vez de reemplazarse. El protocolo hacía append a references/excursions.md. Si cambiaba de idea (Hakone por Nikko, por ejemplo), el archivo tenía dos líneas para la misma fecha y el skill no sabía cuál ganaba.

Lo resolví con cuatro cambios:

  • Una línea por fecha, con reemplazo. El protocolo sustituye la línea <fecha> <ciudad> si ya existe, en lugar de añadir otra. El archivo pasa a ser una función fecha → ciudad.

  • Un único emisor. El manejador de Telegram solo modifica estado y confirma («anotado: 20 sep, Nara»). No envía nada. Quien genera y manda informes es el flujo del cron, con las mismas reglas anti-duplicación.

  • Un informe por fecha objetivo. Antes de componer, el skill lee los informes anteriores en ~/.hermes/cron/output/. Si ya existe uno para esa fecha y la ciudad cambió, genera uno nuevo, lo marca como actualización y declara nulo el anterior en la primera línea. Si no cambió nada, no reenvía.

  • Más margen. Pasé la generación a dos días antes (D-2) a las 21:00 JST, que equivalen a las 14:00 CEST en el cron. Así recibo el informe con un día entero de colchón para pedir cambios, y la actualización por clima pasa a ser una excepción en lugar de la norma.

En las excursiones el formato también cambió: el desayuno se hace en la ciudad de destino, cerca de la primera parada o de su estación principal, y las comidas van en tres listas separadas, nunca mezcladas: almuerzo en destino, cena en destino y cena cerca del hotel, por si volvemos tarde.

Cómo mantener actualizada la lista de comidas probadas

El efecto secundario de las excursiones era que comidas.md no se actualizaba bien. La causa era una suposición cómoda del agente: si lo había sugerido en un itinerario, lo daba por comido. Si luego cambiaba el plan, los platos de la ciudad cancelada quedaban marcados [x] sin haberlos tocado, y los de la ciudad real, sin marcar.

La corrección fue una regla tajante, la segunda mitad de la regla 5: nunca asumir que un plato se comió solo porque se sugirió. Solo mi informe explícito marca [x]. Por la noche escribo por Telegram qué comí y dónde, y el agente anota lugar y fecha y confirma brevemente. Es un pequeño ritual de un minuto, pero convierte el estado en algo fiable. El workflow del cron termina actualizando comidas.md y respondiendo con el informe completo también en el chat.

Errores del cron al enviar el itinerario por email

Aparte de los problemas de diseño, tuve fallos operativos que cualquiera que monte un agente con cron va a encontrarse:

  • Variables de entorno. El shell del cron no carga ~/.hermes/.env, así que el SMTP fallaba. La solución es ejecutar set -a; . ~/.hermes/.env; set +a antes de lanzar send_report.py.

  • Contraseña de aplicación de Gmail. Gmail rechaza las contraseñas de aplicación con espacios y devuelve 5.7.8 BadCredentials. Normalicé con pw = ''.join(pw.split()).

  • Destinatarios. send_report.py tenía como TO solo mi dirección y Arturo no recibía nada. Lo corregí y ahora verifico en la salida que aparezca sent -> [...] con ambos. Tras cualquier parche del script, reenvío si el primer envío salió incompleto.

  • Markdown crudo. Yo no leo markdown, así que el script lo convierte a HTML (negritas, tablas, enlaces) y la regla prohíbe dejar ** o -- visibles.

  • Tipo de cambio. Un día el agente usó un valor recordado de 163 JPY/EUR cuando el real rondaba 181, y el total en euros salió mal. Ahora se consulta en vivo cada ejecución (curl -sL https://api.frankfurter.dev/v1/latest?base=JPY&symbols=EUR) y el informe indica la fecha de la cotización.

  • Cierres semanales. Los museos de Ueno cierran los lunes; quedó anotado como pitfall para ajustar la tarde.

Mirando atrás, lo que funcionó no fue un modelo más listo, sino tratar al agente como un sistema distribuido pequeño: un archivo por cada tipo de estado, un único emisor, reglas idempotentes y cero suposiciones sobre lo que «seguramente» pasó. Montar el skill en una semana me salvó el viaje, y cada fallo se convirtió en una regla escrita. Si vas a hacer algo parecido, empieza con las reglas 3, 5 y 7 de mi skill y deja que el viaje te vaya diciendo cuáles faltan.