Saltar al contenido
coden

Programación con agentes de IA: encargo, pruebas y revisión

Cómo escribirle al agente un encargo con criterios de aceptación, por qué trabajar en pasos pequeños, un experimento con pruebas de mutación (cuántos errores detectan las pruebas del camino feliz, los casos límite y las pruebas basadas en propiedades), quién debe escribir las pruebas y a qué prestar atención.

10 minutos Matěj Žalmánek 2026-10-16

Serie Fundamentos de IA Parte 51 de 52
  1. Mapa de la inteligencia artificial: qué es cada cosa y cómo encaja todo
  2. Breve historia de la inteligencia artificial: de Turing a los modelos de lenguaje
  3. Datos, modelo, entrenamiento, inferencia: los conceptos básicos en un solo ejemplo
  4. Cómo leer la notación matemática: símbolos, subíndices, sumatorios y cuantificadores
  5. Conjuntos y relaciones: las piezas con las que se construyen las definiciones en IA
  6. Funciones y aplicaciones: por qué todo modelo de IA es una función
  7. Lógica y demostraciones: las reglas con las que se razona en IA
  8. Grafos y árboles: la estructura sobre la que se apoyan la búsqueda y las redes neuronales
  9. Álgebra lineal para aprendizaje automático: vectores, matrices y por qué la IA sobre todo multiplica
  10. Derivadas, gradiente y regla de la cadena: cómo sabe un modelo en qué dirección aprender
  11. Descenso de gradiente: cómo aprende un modelo
  12. Qué es la probabilidad: del lanzamiento de un dado a los axiomas de Kolmogórov
  13. Probabilidad condicionada y teorema de Bayes: cómo cambiar una estimación cuando te enteras de algo nuevo
  14. Variable aleatoria y función de distribución: una sola función que lo dice todo sobre la distribución
  15. Función de densidad y función de probabilidad: por qué una variable continua tiene probabilidad cero en cada punto
  16. Esperanza, varianza y covarianza: dónde está una distribución, qué anchura tiene y qué se relaciona con qué
  17. Resumen de las distribuciones de probabilidad: Bernoulli, binomial, Poisson, uniforme, normal y exponencial
  18. Ley de los grandes números y teorema central del límite: por qué las medias se comportan de forma tan predecible
  19. Estadística y estimación de parámetros: máxima verosimilitud, MAP e intervalos de confianza
  20. Teoría de la información: entropía, entropía cruzada y divergencia KL, el origen de las funciones de pérdida
  21. Búsqueda en el espacio de estados: BFS, DFS, UCS y A* con animaciones
  22. Juegos y adversarios: minimax, poda alfa-beta y Monte Carlo tree search
  23. Satisfacción de restricciones: backtracking, propagación de restricciones y AC-3 con un sudoku y un mapa de regiones checas
  24. Lógica y sistemas basados en conocimiento: encadenamiento hacia delante y hacia atrás, resolución y sistemas expertos
  25. Planificación: STRIPS, el mundo de bloques, heurísticas a partir de la relajación y la planificación en los agentes de IA
  26. Búsqueda local y recocido simulado: un viajante por las capitales regionales checas
  27. Algoritmos genéticos: población, cruce, mutación y selección en una mochila para la excursión
  28. Estrategias evolutivas y programación genética: de la regla de un quinto a la ley de Kepler
  29. Optimización por colonia de hormigas: cómo un rastro de feromona encuentra un camino corto
  30. Optimización por enjambre de partículas: inercia, experiencia propia y experiencia del enjambre
  31. Neuroevolución: evolución de los pesos y la forma de las redes neuronales, y el algoritmo NEAT
  32. Conjuntos difusos y funciones de pertenencia: cómo escribir «algo cálido»
  33. Lógica difusa y probabilidad: por qué «algo cálido» no significa «probablemente cálido»
  34. Lógica difusa: t-normas, implicaciones y reglas SI–ENTONCES
  35. Control difuso: Mamdani y Sugeno, del ventilador al péndulo invertido sobre un carro
  36. Agrupamiento difuso: el algoritmo fuzzy c-means y los puntos entre clusters
  37. Sistemas neuro-difusos (ANFIS): reglas difusas que aprenden de los datos
  38. Cadenas de Markov: matriz de transición, distribución estacionaria y PageRank
  39. Modelos ocultos de Márkov: el algoritmo de Viterbi y el casino deshonesto
  40. Redes bayesianas: por qué está mojada la hierba y qué significa eso para el aspersor
  41. El filtro de Kalman: cómo estimar a partir de mediciones imprecisas dónde está un coche y a qué velocidad va
  42. Tipos de aprendizaje automático: supervisado, no supervisado, semisupervisado y más
  43. Prompts, contexto y RAG: cómo darle a un modelo la información correcta
  44. Autocodificadores variacionales (VAE): imágenes nuevas a partir de un espacio latente continuo
  45. Redes generativas adversarias (GAN): el falsificador contra el detective
  46. Modelos de difusión: cómo el ruido puro se convierte en una imagen
  47. Procesos de decisión de Markov: estados, acciones, recompensas y ecuaciones de Bellman
  48. Q-learning y DQN: aprender el valor de las acciones, de la tabla a la red neuronal
  49. Gradiente de la política y PPO: aprender directamente la política
  50. Cómo funciona un agente de IA: un modelo de lenguaje en un bucle con herramientas
  51. Programación con agentes de IA: encargo, pruebas y revisión
  52. Evaluación y seguridad de los sistemas de IA: cómo medir y cómo vigilar

Temario completo de la serie 52 de 83 temas publicados

Qué te llevarás
  • Qué debe incluir el encargo para el agente para que sepa cuándo ha terminado.
  • Cuántos errores detectan pruebas de distinta minuciosidad y por qué importan los casos límite.
  • Qué errores cometen los agentes y cómo encontrarlos al revisar.

Conviene conocer antes: Cómo funciona un agente de IA: un modelo de lenguaje en un bucle con herramientas, Cómo abordar el desarrollo con agentes de IA

En la entrega anterior vimos cómo funciona un agente por dentro. Esta entrega es práctica: cómo encargarle trabajo, cómo revisarlo y cómo no perder de vista lo que ocurre en el proyecto. La mayoría de los consejos se resumen en una frase: tienes que darle al agente una forma de saber por sí mismo que ha terminado, y tú necesitas una forma de comprobarlo. Con un experimento veremos cuánto importa.

Continúa las entregas Cómo funciona un agente de IA y Cómo abordar el desarrollo con agentes de IA, donde se habla más del papel de la persona y de por qué conviene dejar que el agente proponga primero una solución.

1. El encargo: qué, dónde y cómo saber que está terminado

Definición — criterios de aceptación

Los criterios de aceptación son condiciones verificables que el resultado debe cumplir para que la tarea esté terminada: un comportamiento concreto («para un carrito vacío devuelve 0 CZK»), pruebas que deben pasar, un comando que debe terminar sin errores. A diferencia de la descripción del objetivo, de ellos se puede decidir sin ambigüedad si se cumplen.

Dicho de forma sencilla

«Ordena tu cuarto» es un objetivo. «No hay nada en el suelo, la cama está hecha y la papelera está vacía» son criterios de aceptación. Solo con ellos se puede decir si el cuarto está ordenado, y el niño (o el agente) sabe cuándo puede parar.

Definición — la especificación como supuesto y compromiso

Los criterios de aceptación se pueden escribir con precisión mediante una terna de Hoare (C. A. R. Hoare, 1969; la notación actual usa llaves): {P} C {Q} dice que si antes de ejecutar el programa C se cumple la condición P y C termina, después se cumple la condición Q. P es el supuesto (lo que el programa puede esperar de la entrada), Q el compromiso (lo que debe cumplirse sobre el resultado). Un programa es correcto respecto a la especificación si la terna se cumple para todo estado que satisface P.

{ castka ≥ 0 }   s := sazba(castka)   { s = 0 para castka < 1 000;  s = 0,05 para 1 000 ≤ castka < 5 000;  s = 0,1 en otro caso }

castka ≥ 0
supuesto: de un importe negativo la función no responde; está aquí para dejar claro qué no hace falta probar y qué debe vigilar quien la llama
s := sazba(castka)
el programa del que hablamos; la especificación no dice nada sobre cómo está escrito, así que el agente puede cumplirla como quiera
s = 0 / 0,05 / 0,1
compromiso: exactamente tres tramos y sus límites (≤ o <); justo aquí es donde más se falla, por eso los valores 999, 1 000, 4 999 y 5 000 forman parte de los criterios de aceptación
La línea «Terminado cuando» del encargo de abajo es ese mismo compromiso, escrito de forma que se pueda comprobar con una prueba.

Un buen encargo para un agente tiene cuatro partes. No porque sea un ritual, sino porque cada una corresponde a algo que, de otro modo, el agente tendría que adivinar:

Objetivo: En la página del carrito muestra un descuento según el importe (desde 1 000 CZK 5 %, desde 5 000 CZK 10 %).

Contexto: El cálculo del precio está en src/kosik.js, la plantilla en pohledy/kosik.ejs.
El envío se calcula de forma parecida en la función doprava(); mantén el mismo estilo.

Restricciones: No cambies el formato de los datos en la base de datos. Nada de bibliotecas nuevas.

Terminado cuando:
- la función sazba(castka) devuelve 0 / 0,05 / 0,1 y las pruebas de test/kosik.test.js pasan,
- añades pruebas para los límites 999, 1 000, 4 999 y 5 000 CZK,
- npm test termina sin errores y me enseñas las diferencias (git diff).

El contexto le ahorra al agente la búsqueda (y a ti tokens; véase el coste del contexto). Las restricciones impiden que «resuelva» el problema de otra forma de la que quieres. Y los criterios de aceptación le dan la retroalimentación sin la cual las tareas largas se desmoronan.

2. Pasos pequeños, revisiones frecuentes

En la entrega sobre agentes calculamos que una tarea de veinte pasos, cada uno de los cuales sale bien en el 90 % de los casos, sin revisión solo sale bien en el 12 % de los casos. La misma regla vale para ti: cuanto mayor es el trozo de trabajo que el agente entrega de una vez, más difícil es revisarlo y más cara sale la corrección. Consecuencias prácticas:

  • Un cambio = una tarea. Divide lo grande en pasos que se puedan revisar por separado (primero el cálculo, luego la visualización, luego el cambio en los datos).
  • El control de versiones como red de seguridad. Antes de cada tarea, un estado limpio en git; después, un commit. Si algo se estropea, vuelves atrás con un solo comando.
  • Lee las diferencias, no el resumen. El «hecho, todo funciona» del agente es una afirmación. Las diferencias en el código (git diff) y la salida de las pruebas son evidencias.

Péxito = pn

p
probabilidad de que un paso salga bien; está aquí porque cada paso es una nueva oportunidad de equivocarse
n
número de pasos entre dos revisiones; está en el exponente porque las probabilidades de pasos independientes se multiplican, y con cada paso más sin revisión la probabilidad cae de forma geométrica, no lineal
Péxito
probabilidad de que todo el tramo entre revisiones salga bien y no haya que rehacer nada
Simplificando: los pasos fallan de forma independiente y el error solo se detecta en la revisión.
Ejemplo — cuánto trabajo ahorra revisar cada cinco pasos

Sin revisión: 0,920 = 0,1216, es decir, el 12 %. Con una revisión cada cinco pasos: 0,95 = 0,5905 para cada uno de los cuatro tramos.

Supongamos que la revisión siempre detecta el error y que el tramo estropeado se rehace. El número de intentos hasta el primer éxito sigue una distribución geométrica con media 1 / Péxito. Así, la tarea completa sin revisión cuesta de media 20 / 0,1216 ≈ 165 pasos; con una revisión cada cinco, 4 · 5 / 0,5905 ≈ 34 pasos, y con una revisión tras cada paso, 20 / 0,9 ≈ 22 pasos. Los pasos pequeños no aumentan la probabilidad de que un paso salga bien: reducen cuánto trabajo se tira cuando hay un error.

3. Las pruebas como seguro: un experimento

¿Cuánto ayudan las pruebas? Tomamos cinco funciones pequeñas como las que un agente escribe a diario (media, año bisiesto, recorte de texto, búsqueda binaria y descuento según el importe) y creamos de ellas versiones estropeadas a propósito.

Definición — pruebas de mutación

Un mutante es una copia del programa con un único cambio pequeño y automático: < en lugar de <=, - 1 en lugar de + 1, || en lugar de &&, otra constante. Un conjunto de pruebas detecta al mutante si al menos una prueba falla con él. La proporción de mutantes detectados mide lo bien que las pruebas vigilan los errores. Los mutantes que se comportan igual que el original (equivalentes) no se cuentan.

Dicho de forma sencilla

Es como probar un detector de humo: enciendes a propósito una vela pequeña y miras si pita. Aquí metes a propósito en el código un pequeño error (justo del tipo que cometen las personas y los agentes) y miras si las pruebas lo notan.

A partir del código fuente de las cinco funciones se generaron automáticamente 32 mutantes. 3 de ellos nunca se diferenciaron del original en 2 000 entradas aleatorias (por ejemplo, a[s] <= x en lugar de a[s] < x en la búsqueda binaria, donde la igualdad ya la trataba la línea anterior), así que quedan 29 errores reales. Frente a ellos, cuatro niveles de pruebas, cada uno de los cuales incluye también los anteriores:

  • Camino feliz: uno o dos ejemplos típicos por función. Son las pruebas que salen cuando solo se dice «añade pruebas».
  • Casos límite: lista vacía, límites de los tramos (999, 1 000, 4 999, 5 000 CZK), los años 1900, 2000 y 2100, un texto justo en el límite de longitud.
  • Propiedades: 200 entradas aleatorias y reglas que deben cumplirse siempre: el resultado del recorte tiene como mucho max caracteres y es el comienzo del texto, el índice encontrado señala de verdad el número buscado, la media coincide con un cálculo escrito de otra forma.
0 %25 %50 %75 %100 % 0 de 29 ninguna 15 de 29 camino feliz 26 de 29 + casos límite 28 de 29 + propiedades nivel de pruebas (acumulativo, cada uno incluye los anteriores) errores detectados (% de 29)
Cuántos de los 29 errores detectan las pruebas

Las pruebas del camino feliz solo atrapan 15 de los 29 errores (el 52 %). Pasan incluso con una función que para una lista vacía devuelve 1, con un descuento que no empieza hasta 1 001 CZK o con una búsqueda binaria que en una lista de un solo elemento no encuentra nada. Añadir los casos límite sube la proporción al 90 %, y las pruebas de propiedades, al 97 %.

funciónmutantescamino feliz+ casos límite+ propiedades
prumer5455
jePrestupny8557
zkrat3233
najdi6366
sazba7177

MS = |O| / ( |M| − |E| )

MS
puntuación de mutación (mutation score): la proporción de errores reales que atrapa el conjunto de pruebas; es la altura de la barra en el gráfico
|O|
número de mutantes detectados (al menos una prueba falló con ellos, o se colgaron o entraron en un bucle infinito); lo que las pruebas saben hacer
|M|
todos los mutantes generados: la reserva de errores con la que medimos las pruebas
|E|
mutantes equivalentes; se restan porque no se diferencian del original y, por tanto, ninguna prueba puede detectarlos; de lo contrario, ni unas pruebas perfectas llegarían al 100 %

En nuestro caso |M| = 32 y |E| = 3, así que el denominador es 29. Camino feliz: 15 / 29 = 0,517; con casos límite, 26 / 29 = 0,897; con propiedades, 28 / 29 = 0,966. El procedimiento con el que se obtuvieron las cifras:

para cada función f:
    original = compila(código de f)
    para cada operador (de → a) y cada aparición de «de» en el código:
        mutante = compila(código con esta aparición de «de» sustituida por «a»)
        si el mutante nunca se diferencia del original en 2 000 entradas aleatorias:
            equivalente: no se cuenta, siguiente
        para cada nivel de pruebas u:
            detectado[u] = alguna prueba del nivel u falla con el mutante,
                           este se cuelga o entra en bucle (tope tras 10 000 iteraciones)
MS(u) = número de detectados en el nivel u / número de no equivalentes

Lo más interesante es el año bisiesto. Los casos límite dejan pasar a los mutantes que consideran bisiesto cualquier año par o que descartan los años divisibles entre diez, es decir, también el año 2020. Las pruebas con los años 1900, 2000 y 2100 no lo notan, porque para esos años casualmente responden bien. La prueba de propiedades con años aleatorios encuentra ambos errores. El único mutante que sobrevivió a todo es (rok % 4 === 0 && rok % 100 !== 0) || rok % 200 === 0: solo se diferencia en los años divisibles entre 200 pero no entre 400 (200, 600, 1000…), y entre los 200 años aleatorios no hubo ninguno así. Ni siquiera las pruebas aleatorias son omnipotentes.

Psobrevive = ( 1 − q )n

q
proporción de entradas en las que el mutante se diferencia del original; 1 − q es la probabilidad de que una sola entrada aleatoria no lo detecte
n
número de entradas aleatorias; las entradas se sortean de forma independiente, así que las probabilidades de «no lo detectó» se multiplican
Psobrevive
probabilidad de que las n entradas no den con el mutante: el error pasa aunque la propiedad esté bien escrita
Las pruebas aleatorias atrapan con fiabilidad los errores que se manifiestan a menudo; los raros, solo con muchas entradas.
Ejemplo — por qué sobrevivió el mutante del año divisible entre 200

La prueba de propiedades sortea años entre 0 y 2 399. El mutante rok % 200 solo se diferencia del original en 6 de ellos (200, 600, 1000, 1400, 1800, 2200), así que q = 6 / 2400 = 0,0025 y con n = 200 sobrevive con probabilidad (1 − 0,0025)200 = 0,606, más o menos en seis de cada diez intentos. Que sobreviviera no fue mala suerte, sino el resultado esperado.

El mutante rok % 2 se diferencia en 600 años (q = 0,25): sobrevive con probabilidad 0,75200 ≈ 10-25, es decir, prácticamente nunca. El mutante rok % 10 se diferencia en 96 años (q = 0,04) y sobrevive con probabilidad 0,96200 = 0,00028.

¿Cuántas entradas harían falta para atrapar al mutante del año 200 con una probabilidad del 95 %? De (1 − q)n ≤ 0,05 se sigue n ≥ ln 0,05 / ln(1 − q) = −2,996 / −0,00250 ≈ 1 196,8, es decir, 1 197 entradas. Sale más barato añadir el caso límite del año 200 o 600: quien conoce la regla del calendario sabe dónde buscar.

4. Quién debe escribir las pruebas

En el experimento escribimos las pruebas a partir del encargo (lo que debe hacer la función), no a partir del código. Eso es esencial. Cuando el agente escribe el código y enseguida sus pruebas, existe el riesgo de que las pruebas solo repitan lo que hace el código, errores incluidos. Pasan y no demuestran nada. Ayuda lo siguiente:

  • Las pruebas (o al menos su lista) primero. Deja que el agente escriba primero las pruebas según el encargo, léelas, y solo después el código. Los criterios de aceptación del encargo son exactamente esa lista.
  • Enumerar los casos límite. Entrada vacía, un solo elemento, límites de los tramos, números negativos, tildes y diacríticos. El agente a menudo los añade por su cuenta, pero solo si los pides.
  • Propiedades donde se pueda. «Una lista ordenada sigue ordenada», «codificar y decodificar = texto original», «el nuevo cálculo da lo mismo que el antiguo». Bibliotecas como fast-check (JavaScript) o Hypothesis (Python) generan solas las entradas aleatorias.
Definición — pruebas basadas en propiedades (property-based testing)

En lugar de pares concretos de entrada y salida se escribe una propiedad que debe cumplirse para todas las entradas, y la herramienta la comprueba con muchas entradas generadas al azar. Cuando encuentra un contraejemplo, normalmente lo reduce al caso fallido más sencillo. La idea la popularizó la biblioteca QuickCheck (Koen Claessen y John Hughes, 2000).

Dicho de forma sencilla

No preguntas «¿cuánto es 7 + 5?», sino «¿se cumple que a + b siempre es igual que b + a?», y dejas que el ordenador pruebe mil pares aleatorios. Encontrará casos en los que no habrías pensado.

Una propiedad es un enunciado con el cuantificador «para todo». Para la función zkrat del experimento, la prueba la escribe así:

∀ t, m ≥ 1: |v| ≤ m ∧ ( |t| ≤ m ⇒ v = t ) ∧ ( |t| > m ⇒ v = t[0, |v| − 1) + «…» ),  donde v = zkrat(t, m)

∀ t, m ≥ 1
para todo texto y toda longitud; por eso se puede comprobar con entradas aleatorias sin necesidad de conocer de antemano el resultado correcto
|v| ≤ m
la promesa principal de la función: el resultado cabe; atrapa al mutante que olvida contar los «…» (slice(0, max))
|t| ≤ m ⇒ v = t
un texto corto no cambia; sin esto pasaría una función que recorta siempre
v = t[0, |v| − 1) + «…»
el resultado recortado es el comienzo del texto original y termina en puntos suspensivos; sin esto pasaría una función que devuelve, por ejemplo, un texto vacío
t[0, k) son los primeros k caracteres del texto t. La propiedad no dice cuántos caracteres exactamente quedan, solo lo que debe cumplirse; por eso es más general que un único ejemplo concreto.

Una herramienta de pruebas basadas en propiedades hace entonces más o menos esto:

para i = 1 … N:                      # N suele ir de 100 a 1 000
    x = genera una entrada aleatoria
    si propiedad(x, f(x)) no se cumple:
        # reducción: busca una entrada más simple con la que siga fallando
        mientras exista y = simplifica(x) con la que la propiedad no se cumple:
            x = y                    # texto más corto, número menor, lista más corta
        informa del contraejemplo x
informa de «pasaron N entradas»      # no es una demostración, solo una buena noticia

La última línea es esencial. Las pruebas, ni siquiera las aleatorias, demuestran la corrección: solo buscan contraejemplos. Edsger Dijkstra lo expresó en 1969 diciendo que las pruebas pueden mostrar la presencia de errores, nunca su ausencia. La probabilidad de que un error raro se cuele la hemos calculado más arriba.

5. A qué prestar atención

Los agentes cometen errores distintos de los de las personas. Los más frecuentes, que vale la pena buscar al revisar:

  • «Hecho» sin comprobación. El agente anuncia el éxito sin haber ejecutado las pruebas, o tras ejecutarlas solo en parte. Pide la salida de las pruebas.
  • Modificar las pruebas en lugar del código. Cuando una prueba falla, el «arreglo» más fácil es cambiar el valor esperado o borrar la prueba. En las diferencias, busca cambios en las pruebas.
  • Errores silenciados. Un catch vacío, un valor por defecto en lugar de un error, un resultado escrito a mano para la entrada de prueba.
  • Trabajo de más. Un archivo reescrito que no tiene que ver con la tarea, una biblioteca nueva, un formato de datos «mejorado». Las restricciones del encargo y los pasos pequeños lo mantienen a raya.
  • Secretos y pasos irreversibles. Contraseñas en el código, borrado de archivos, escritura en la base de datos de producción, envío al repositorio remoto. Haz que estas acciones requieran tu aprobación.

6. No perder la visión de conjunto

El agente no recuerda nada entre sesiones. Lo que debe saber siempre (cómo se ejecuta y se prueba el proyecto, qué convenciones tiene, qué evitar) va en un archivo de instrucciones en la raíz del proyecto que el agente lee al empezar. Escribe ahí lo que has tenido que advertirle al agente más de una vez. Y lo que solo sabes tú (por qué algo está hecho así, qué se probó y no funcionó) va en los commits y en la documentación, para el agente y para ti mismo dentro de medio año.

El último consejo es el más sencillo: no te desentiendas. No necesariamente delante del ordenador: el agente puede trabajar solo y tú echas un vistazo cuando necesite una decisión. Para eso nació coden: el encargo, la aprobación de los pasos y la revisión del resultado los tienes en el móvil, mientras el agente trabaja en tu ordenador.

7. Resumen

  • Un buen encargo: objetivo, contexto (archivos, modelos a seguir), restricciones y criterios de aceptación verificables.
  • Pasos pequeños, un commit tras cada uno, y revisar las diferencias en el código y la salida de las pruebas, no el resumen.
  • En el experimento, las pruebas del camino feliz detectaron el 52 % de los errores; con casos límite, el 90 %; con propiedades, el 97 %.
  • Escribe (o aprueba) las pruebas según el encargo y, a ser posible, antes del código; si no, solo repetirán los errores de la implementación.
  • Vigila las modificaciones de las pruebas, los errores silenciados, el trabajo de más y los pasos irreversibles.

¿Quieres probarlo tú mismo? Pídele a un agente de IA que ejecute en tu proyecto una herramienta de pruebas de mutación (Stryker para JavaScript, mutmut para Python) y que te enseñe los mutantes que han sobrevivido. Cada uno de ellos es una prueba que falta.

¿Lo ves de otra manera? Escribe a support@coden-app.com.

Un Agente en tu ordenador, controlado desde el móvil