Tengo 58 crons corriendo agentes autónomos en una Raspberry Pi. Esta semana tres de ellos se rompieron por razones tan absurdas que ninguna me las enseñó un tutorial. Las aprendí a las 4 de la mañana, con logs en la cara.
Las comparto porque son el tipo de cosas que solo aprendes cuando tu sistema lleva semanas andando y de repente algo deja de funcionar sin razón aparente.
1. Tu parser de .env de 4 líneas está mintiendo
Escribí un script que lee contraseñas de Gmail desde un archivo .env. El parser era esto:
env = {}
for line in open(".env"):
k, v = line.strip().split("=", 1)
env[k.strip()] = v.strip()
Cuatro líneas. Inofensivo. Funcionó durante semanas.
Hasta que una contraseña de Gmail con espacios necesitó comillas en el .env:
GOOGLE_APP_PASSWORD="eeuq cvtr xxxx yyyy"
El parser guardó "eeuq cvtr xxxx yyyy" — con las comillas. IMAP autenticaba con comillas literales en la contraseña. Obviamente fallaba. Pero el mensaje de error era AUTHENTICATIONFAILED, así que perdí una hora rotando credenciales que estaban bien.
La solución es una línea:
env[k.strip()] = v.strip().strip('"').strip("'")
python-dotenv hace esto automáticamente. Los parsers manuales no. Si escribiste tu propio lector de .env, revisá que quite las comillas. No es un detalle estético: te va a romper autenticación sin decirte por qué.
2. DeepSeek ignora enable_thinking: false (en serio)
Esto me costó 707 documentos sin procesar.
Tengo un script que extrae conceptos del segundo cerebro usando la API de DeepSeek directa. Le paso esto:
{
"model": "deepseek-v4-pro",
"enable_thinking": false,
"messages": [...]
}
El parámetro se llama enable_thinking. La documentación lo menciona. Parece razonable.
DeepSeek lo ignora completamente.
El modelo entra en modo razonamiento igual. Genera 4096 tokens de reasoning interno, cero tokens de contenido útil, y devuelve finish_reason: "length". Mi script hacía json.loads() sobre un string vacío y fallaba en silencio.
La forma correcta es:
{
"model": "deepseek-v4-pro",
"thinking": {"type": "disabled"},
"messages": [...]
}
enable_thinking es un parámetro fantasma. Existe en la doc pero no hace nada. thinking.type: disabled es lo que realmente funciona.
Esto no está en la documentación principal. Lo encontré después de tres batches vacíos consecutivos a las 4:25 AM. Si estás llamando a DeepSeek por API directa y no querés reasoning tokens, usá thinking.type: disabled. No enable_thinking: false. Ese parámetro es decorativo.
3. grep -c falla cuando todo está bien
Tengo health checks que verifican que no haya duplicados en archivos de memoria:
grep -c "## WARM" MEMORY.md
Si hay 1 ocurrencia, grep -c devuelve 1 y exit code 0. Todo bien.
Si hay 0 ocurrencias — que es exactamente lo que quiero, significa que no hay duplicados — grep -c devuelve 0 y exit code 1.
Unix considera que "no encontré nada" es un error. Mi health check interpretaba exit code 1 como fallo y marcaba el cron como EXHAUSTED.
El fix: parsear el número en vez de confiar en el exit code. Si expect_max está definido y el output es un número, evaluar el número. Exit code 1 con 0 matches es exactamente el estado deseado.
Regla general: si tu health check usa grep -c para verificar ausencia de algo, el exit code te va a mentir. Parseá el número.
Lo que aprendí
Ninguna de estas trampas es un bug del modelo ni de la herramienta. Son comportamientos perfectamente lógicos una vez que los entendés. El problema es que no son obvios hasta que te rompen algo en producción a las 4 AM.
Si estás construyendo agentes que corren solos, asumí que estas cosas van a pasar. La diferencia entre un sistema que sobrevive y uno que se cae no está en evitar estos errores — está en detectarlos rápido y tener un patrón documentado para cada uno.
En mi caso, las tres quedaron registradas en el archivo de patrones de auto-reparación del sistema. La próxima vez que aparezcan, el agente ya sabe qué hacer. Esa es la parte que ningún tutorial te enseña: construir agentes no es escribir el prompt perfecto. Es construir la memoria de todo lo que salió mal.