20°
Portada del artículo: Un agente que lee mis propios posts y verifica que las cifras no se inventen
AgentesSDKVerificación

Un agente que lee mis propios posts y verifica que las cifras no se inventen

Google publicó la versión 2.14 de su kit de agentes el 4 de noviembre de 2026 y entre otras cosas sacó de experimental las sandboxes de ejecución local. Escribí un agente que lee un post técnico del blog, extrae cada cifra que aparece en el texto y abre el JSON crudo del experimento para comprobar que esa cifra existe. Sobre el post que publiqué ayer, detectó 46 números, verificó los 9 que venían de las mediciones, marcó 1 que no estaba en el JSON por un campo que dejé sin registrar, y clasificó los otros 36 como contexto o configuración. Tardó un minuto y dieciséis segundos. Una regex trivial clasifica mal: dice que verifica 45 cifras porque 50 == 50 por azar.

Efrain Garay 7 de noviembre de 2026

Reproduciendo el resumen
En 58 segundos y con narración: el agente ADK 2.14 lee un post del blog, extrae 46 cifras, verifica 9 contra results.json, encuentra 1 olvido y clasifica 36 como contexto. La regex trivial dice 45 pero son falsos positivos. Sin sonido por defecto: actívalo en los controles.Verlo en el visualizador de reels →

Google publicó el 4 de noviembre de 2026 la versión 2.14.0 del Agent Development Kit, su SDK para armar agentes en Python. El cambio grueso de esta versión es que las sandboxes de ejecución salen de experimental: ahora un agente puede correr código Python en un LocalEnvironment (tu máquina), en un E2BEnvironment (una micro-VM remota), en un DaytonaEnvironment (un contenedor efímero) o en un AgentEngineEnvironment (el runtime gestionado de Google Cloud). Antes, cada uno de esos cuatro backends vivía detrás de un subject to change en la documentación.

Un agente que puede ejecutar código es un agente que puede, por fin, auditar algo que yo escribí.

Este post es la prueba de concepto más corta posible: un agente de treinta líneas que lee un .mdx del blog, abre el results.json crudo del mismo experimento, y verifica que cada cifra del texto corresponde a una entrada del JSON.

Por qué este agente, y no otro

La regla número uno de este blog es: ningún número sin haberlo medido. Cada cifra del texto debe mapear a archivo:clave de los datos crudos. Lo digo en la receta y lo repito en los gates del build.

Hasta hoy, esa regla la cumplía a ojo. Yo mismo redactaba el número, yo mismo revisaba que el results.json lo tuviera, yo mismo firmaba al pie. La única barrera entre un 56,0 % honesto y un 56,0 % inventado era la memoria.

Con las sandboxes salidas de experimental, es trivial poner un agente delante que haga lo mismo que yo haría: mirar el JSON.

Arquitectura de VerificadorCifrasEl runner de google-adk 2.14 orquesta tres herramientas: leer el post en Markdown, leer la forma del JSON de resultados, y ejecutar fragmentos de Python en una sandbox local. El modelo decide qué llamada hacer en cada paso.ADK Runnerv2.14.0Claude 4.5read_posttexto del MDXread_results_keysforma del JSONpython_lookupsandbox localresults.jsond = ...
Tres herramientas simples, un runner con sandbox, un modelo que decide cuál llamar.

El agente recibe la ruta al post y la ruta al results.json. Tiene tres herramientas:

  • read_post() le devuelve el MDX entero.
  • read_results_keys() le devuelve la forma del JSON, dos niveles de profundidad, para que pueda planificar las búsquedas sin bajar todo el árbol.
  • python_lookup(code) ejecuta un fragmento de Python en la sandbox local, con la variable d ya cargada con el JSON. Lo que el código asigne a result vuelve al agente como respuesta.

El prompt del agente le dice que escanee el post, saque cada número con su unidad y su contexto, y por cada uno escriba un one-liner de la forma result = d["agg"]["cola2_acc"]. Cada vez que la sandbox devuelve un valor, el agente compara, tolera conversiones de unidades (14,5 GB = 14 518 MB), y marca la cifra como verificada, no encontrada o contexto.

La ejecución

Lo probé contra el post que publiqué ayer, el de Cola2 en 16 GB. Son dieciséis mil caracteres de texto medido y un results.json de cincuenta ítems con su agregado.

El agente corrió en 76,8 segundos, hizo 11 llamadas a herramientas, y emitió esta lista:

Cifras detectadas vs verificadasBarras horizontales sobre el post Cola2. La regex trivial detecta 69 cifras y clasifica 45 como verificadas, pero muchas son coincidencias numéricas falsas. El agente VerificadorCifras detecta 46 cifras, verifica 9 como medidas reales, marca 1 como no encontrada por olvido en el JSON, y clasifica 36 como contexto que no deben aparecer en el JSON.Cifras en el post Cola2 (16 KB)regex trivial69 detectadas45 "verificadas"** muchas por coincidencia numérica (4==n_error 4, 50==n)agente ADK46 detectadas9 verificadas1 no encontrada (olvido)36 contexto
El agente gasta un minuto y dieciséis segundos pero no miente. La regex es gratis y clasifica mal.

46 cifras detectadas. Son todas las que el modelo consideró claims numéricos: no cuenta los años, las versiones de software ni los identificadores.

9 verificadas contra results.json. Las claves son las obvias: 56.0 (acierto Cola2), 22.0 (acierto línea base), 28, 11, 50 (N de muestras), 10.54 (latencia media), 72.86 (tokens por segundo), 14518 (VRAM MB, también citada como 14,5 GB), y el margen 34 que el agente supo calcular como 56 − 22.

1 cifra no encontrada. El tiempo de carga 26,6 s aparece en el texto pero el campo load_s del results.json quedó en null. Es un olvido mío al registrar la medición, y el agente lo encuentra sin que yo lo pida.

36 cifras clasificadas como contexto. Fechas de release (4 de noviembre, 30 de octubre), dimensiones del modelo (4 mil millones de parámetros, 34 capas, 2 560 de hidden, 346 mil millones de tokens de entrenamiento), configuraciones (sample_seed=42, max_prompt_length=512, MAX_NEW_TOKENS=32), citas del paper (60,10 de media), cálculos derivados (3 tokens por segundo = 32/10,54), estimaciones hipotéticas (“debería estar en 30-40 tokens por segundo”). Ninguna de estas debe mapear al JSON, y el agente lo sabe.

Por qué no sirve una regex

Reescribí el agente como una regex de catorce líneas: extrae todo número con unidad opcional, aplánalo el JSON a {path: valor}, cruza.

El bucle del VerificadorCifrasPor cada cifra detectada, el agente escribe un one-liner de Python, la sandbox lo ejecuta, el resultado vuelve al agente, que clasifica la cifra como verificada, no encontrada o contexto.cifra"56,0 %"one-linerd.agg.cola2_accveredictoverificada46 veces sobre el post Cola2
Un ciclo por cifra: detectar, proponer lookup, ejecutar en la sandbox, clasificar.

La regex detecta 69 cifras y dice que verifica 45. Eso suena mejor que el agente, hasta que uno mira los matches: “verificó” el número 4 contra n_error: 4, el 2 contra cuatro paths distintos, el 50 contra el n del agregado. La regex no distingue entre “la latencia fue de 10,54 segundos” (medida) y “max_prompt_length=512” (configuración); ambas son números, y ambas aparecen en el JSON por coincidencia estructural, no porque la cifra del texto hable del campo del JSON.

El agente hace una cosa que la regex no puede: lee el contexto de la frase. “La latencia media fue de 10,54 segundos” apunta claramente a agg.mean_latency_s; “corrí con --max-samples 100” no habla de ningún campo medido, es un hiperparámetro.

Es exactamente el tipo de distinción que convertiría una auditoría a ojo en un gate de build. La regex inflada da falsa confianza; el agente, aunque más caro, da información accionable.

Lo que cuesta

Una ejecución sobre un post de 16 KB:

  • 76,8 s de wall time.
  • 11 llamadas a herramientas (1 read_post, 1 read_results_keys, 9 python_lookup).
  • Una entrada / salida al modelo que no mido en tokens porque LiteLLM no las expone de vuelta al agente en el SDK que probé.
  • Cero recursos de GPU. Todo corre en el Mac.
  • La sandbox local es un subprocess.run con timeout; para producción, convendría cambiar a E2BEnvironment para aislar.

Comparado con un gate de build de un segundo, es un orden de magnitud lento. Pero es el único modo honesto de comprobar que lo que publico cuadra con lo que medí.

Lo que falta

Que el agente sepa cuándo null es un olvido y cuándo es un diseño. En mi ejecución marcó load_s como “no encontrada”. Es un olvido, correcto, pero no supo decirme eso: habría necesitado leer el script de medición para ver que ahí sí se registra el campo pero yo no lo capturé.

Un segundo juez. Un agente que se audita a sí mismo deja fuera el sesgo del autor pero no el de la escuela de pensamiento. Lo próximo útil sería correr el mismo verificador con Gemini 2.5 Flash en paralelo, comparar las dos listas de cifras, y publicar solo las que ambos marcan igual.

Tokens de costo. El SDK no los expone por run; para usarlo como gate hay que contar fuera.

Preguntas frecuentes

¿Qué es Google ADK?

Agent Development Kit. Un SDK de Python que Google publicó para armar agentes con herramientas, memorias, sandboxes de ejecución y evaluadores. La versión 2.14.0 salió el 4 de noviembre de 2026 y sacó de experimental los `BaseEnvironment`, `LocalEnvironment`, `E2BEnvironment` y `DaytonaEnvironment`, el `EnvironmentToolset`, el runtime de evaluación multi-ejecución y el A2A.

¿Qué hace el agente VerificadorCifras?

Lee un archivo `.mdx` de este blog y el `results.json` de su propio experimento, extrae cada número que aparece en el texto (porcentajes, latencias, VRAM, aciertos), y para cada uno escribe un `one-liner` de Python que busca ese valor dentro del JSON. Lo ejecuta en la sandbox local, mira el resultado, y clasifica cada cifra como *verificada*, *no encontrada*, o *contexto*. Al final emite un veredicto con los tres contadores.

¿Para qué sirve?

Para auditar yo solo lo que publico. La regla del blog es que cada cifra del texto debe mapear a `archivo:clave` de los datos crudos. Hasta ahora esa regla se cumplía a ojo: el autor la escribe, el revisor la cree. Con el agente delante, la fiscalización sucede antes de publicar, y el 'me quedé sin registrar `load_s`' aparece como un hallazgo marcable en vez de un olvido silencioso.

¿Cuánto detectó y verificó?

Sobre el post de Cola2 que publiqué ayer, detectó 46 cifras, verificó 9 contra `results.json`, marcó 1 como no encontrada (el tiempo de carga que dejé sin registrar en el JSON) y clasificó 36 como contexto o configuración (fechas, dimensiones del modelo, hiperparámetros del paper). Gastó 11 llamadas de herramienta y tardó 76,8 segundos corriendo sobre Claude Sonnet 4.5 por LiteLLM.

¿Y una regex trivial haría lo mismo?

No. Una regex que extrae todo número decimal y lo busca en el JSON encontró 69 'cifras' en el mismo post (muchas son `2`, `4`, `8`, `32`: parámetros secundarios que no son claims) y marcó 45 como 'verificadas' porque el número `50` o el `4` aparecen en el JSON por mera coincidencia. Sin el juicio del modelo sobre qué es *claim* y qué es *parámetro*, la regex miente con mucha confianza.

¿Qué modelo está detrás?

`anthropic/claude-sonnet-4-5` servido por la API de Anthropic, enchufado al ADK con el adaptador `LiteLlm`. Fue el que tenía más barato para una prueba con pocas llamadas. ADK acepta Gemini, OpenAI, OpenRouter, Z.ai, MiniMax y cualquiera que LiteLLM exponga.

Fuentes

7 de noviembre de 2026 · Mac M2 · google-adk 2.14.0 · litellm con anthropic/claude-sonnet-4-5 · LocalEnvironment (subprocess) · post Cola2 de 16 kB, results.json de 65 ítems · ejecución única, temperature por defecto del SDK.

BOLETÍN · MEDIDO

Una medición nueva, cuando la hay.

Cada post con sus números, sus fallos y los scripts para repetirlo. Entre uno y tres correos al mes. Te das de baja con un clic.

LO ÚLTIMO QUE LLEGÓ

Uso Listmonk en mi propio servidor; tu correo no sale de ahí. Confirmas por correo antes de recibir nada.

Comentarios

Todavía no hay comentarios. El primero es tuyo.

Se revisa antes de publicarse. El correo no se guarda ni aparece en ninguna parte.