Una ejecución de nanobot que parece un bucle infinito está limitada: en el código fuente distribuido de v0.3.5 cada turno del agente está limitado por agents.defaults.maxToolIterations, cuyo valor predeterminado es 200, y un mantenedor afirma públicamente que no es literalmente un bucle infinito. El uso de tokens es el problema real, porque una ejecución que alcanza ese límite registra 201 turnos del asistente y reenvía un prompt cada vez más grande en cada uno. Determinar cuál de esos casos tienes es un diagnóstico de cuatro preguntas, y la solución respaldada por evidencia no es un valor de configuración.
Empieza descartando la pista obvia. El informe original es la incidencia #5781, cuyo título apunta a dream.maxIterations, y la PR #5782 se titula «fix(dream): enforce configured iteration limit». Esa clave no existe en v0.3.5 y la PR se cerró sin fusionar el 16 de septiembre de 2026. Un tutorial basado en ella no configura nada.
Una aclaración, porque la consulta muestra el rastreador equivocado. Esta página trata sobre HKUDS/nanobot, MIT, instalado como el paquete de PyPI nanobot-ai — 0.3.5, Python 3.11 o posterior, subido el 15 de septiembre de 2026. obot-platform/nanobot es un proyecto de Go distinto cuyo README declara que está en modo de mantenimiento con las incidencias desactivadas; sus números de incidencia no son evidencia aquí. Y pip install nanobot, sin sufijo, instala un paquete de navegación robótica no relacionado.
Qué se informó y contra qué versión
Dream es el trabajo programado de consolidación de memoria de nanobot, ejecutado por el gateway como un trabajo cron llamado dream. No es un trabajo de embeddings ni de índices vectoriales: Kunavo no ofrece ningún modelo de embeddings, y Dream no necesita uno, porque la memoria persistente de nanobot son archivos normales dentro del espacio de trabajo y una ejecución de Dream es tráfico de chat ordinario.
El issue #5781 fue presentado el 15 de septiembre de 2026 por BrianMwangi21 contra nanobot v0.3.0, en Python 3.12 y con un modelo de razonamiento accedido mediante OpenRouter. El síntoma: el trabajo Dream alternaba read_file en memory/history.jsonl con read_file en un único SKILL.md, docenas de veces, mientras la traza de razonamiento volvía a deliberar dónde archivar un solo dato pequeño. Siete ejecuciones registradas durante unas 26 horas oscilaron entre aproximadamente 25 y 111 minutos; las cercanas a 200 llamadas de herramientas alcanzaban el límite global, y un punto de control de sesión mostraba runtime_checkpoint.iteration: 164, fase tools_completed, en la misma llamada read_file.
Hay tres límites de alcance que deben acompañar a esas cifras. Son registros del gateway declarados por un solo usuario, con un solo modelo, en v0.3.0 — ningún mantenedor los reprodujo y nadie ha vuelto a probarlos en v0.3.5. El issue está etiquetado como enhancement y priority: p2, no como bug, y sigue abierto. Además, esta forma ya había aparecido antes: el issue #3073, un bucle read_file casi idéntico en history.jsonl del 12 de abril de 2026, se cerró como not planned. Nada de esto respalda considerar nanobot generalmente caro; respalda comprobar si tu propia instalación está haciendo esto.
La clave obsoleta y el límite que realmente está activo
Toda la confusión es una diferencia de versión, y la fuente la resuelve. Leído en las dos etiquetas de versión el 21 de septiembre de 2026:
| Clave de configuración | En v0.3.0 | En v0.3.5 | Qué hacer ahora |
|---|---|---|---|
dream.maxIterations | Valor predeterminado 15, marcada # Deprecated: no longer used | Eliminada del esquema | No la escribas; no configura nada |
dream.maxBatchSize | Valor predeterminado 20, mismo comentario de obsolescencia | Eliminada | No la escribas |
dream.annotateLineAges | Valor predeterminado true, mismo comentario de obsolescencia | Eliminada | No la escribas |
dream.enabled | Valor predeterminado true | Valor predeterminado true | Editable en la configuración de ejecución de WebUI |
dream.intervalH | Valor predeterminado 2 | Valor predeterminado 2 | Edita config.json; no es una hoja de WebUI |
dream.cron | Null; anulación heredada | Null; anulación heredada | Tiene precedencia sobre intervalH cuando se establece |
dream.modelOverride | Declarada, comentada como pending implementation | Implementada | Solo nombres de presets, nunca un ID de modelo sin procesar |
agents.defaults.maxToolIterations | 200 | 200 | El único límite, y es para todo el proceso |
Dos cosas hacen que esa tabla sea fácil de interpretar mal. El 200 es doblemente cierto: es el valor de la configuración del informante y el valor predeterminado enviado en la línea 129 de nanobot/config/schema.py, idéntico en la etiqueta v0.3.5 y en main, así que un lector que nunca lo haya establecido sigue teniendo 200. Y no está documentado: una consulta de la propia referencia de configuración 0.3.5 de nanobot el 21 de septiembre de 2026 devolvió 488,608 bytes de HTML con cero apariciones de maxToolIterations, y el docs/configuration.md del repositorio en esa etiqueta tampoco contiene ninguna. El número puede demostrarse mediante el código fuente y un comentario de un mantenedor, no mediante una página de documentación; por eso debes mirar con sospecha cualquier tutorial que cite una cifra diferente.
modelOverride es el único remedio con una restricción de versión. En v0.3.0 el campo existía con el comentario pending implementation y sin ningún código que lo resolviera; el PR #5107, fusionado el 27 de julio de 2026, lo implementó para v0.3.5. La página oficial de memoria de 0.3.5 indica que selecciona una entrada con nombre de model_presets para Dream, acepta únicamente nombres de presets y no admite identificadores de modelos sin procesar. Esa página documenta tres claves de Dream y no menciona ningún límite de iteraciones. Esta es la superficie completa actual, junto con el bloque providers en el que se apoya, cubierto en la página de configuración de nanobot:
{
"modelPresets": {
"dream-cheap": {
"provider": "kunavo",
"model": "claude-haiku-4-5",
"maxTokens": 8192
}
},
"agents": {
"defaults": {
"maxToolIterations": 200,
"dream": {
"enabled": true,
"intervalH": 2,
"modelOverride": "dream-cheap"
}
}
}
}Observa lo que no aparece en ese archivo: ninguna forma de limitar Dream por separado. AgentLoop se construye una sola vez con max_iterations a partir de los valores predeterminados del proceso, y tanto la ruta cron de Dream como la ruta manual /dream llaman a process_direct(...) sin argumento de iteraciones, al igual que los subagentes. Reducir el límite lo reduce para el chat, Dream, heartbeat y los subagentes conjuntamente.
Diagnostica en este orden
Responde estas cuatro preguntas antes de cambiar nada, porque tres son gratuitas y la cuarta indica si las tres primeras importan. Las cadenas de registro son el texto literal de v0.3.5; las llaves representan valores de ejecución.
| Pregunta | Dónde buscar | Qué significa la respuesta |
|---|---|---|
| 1. ¿La ejecución se detuvo por sí sola o alcanzó el límite? | Registro del gateway: Max iterations (200) reached, de agent/loop.py | Su presencia significa una ejecución limitada, con aproximadamente 201 turnos del asistente. Su ausencia significa que la ejecución terminó antes del límite: el modelo convergió o la ejecución falló por completo y registró Dream cron job failed |
| 2. ¿Avanzó el cursor de Dream? | Tres líneas distintas en cli/gateway_runtime.py: Dream cron job completed, cursor advanced to …; … completed with no memory changes; cursor advanced to …; Dream cron job did not complete (…); cursor remains at … | La tercera línea es el mecanismo de seguridad de v0.3.5: una ejecución incompleta deja el lote para volver a intentarlo. También significa que el mismo lote vuelve en el siguiente ciclo |
| 3. ¿Se repite la misma herramienta con los mismos argumentos? | Las líneas Tool call: entre esos dos marcadores | Una herramienta y unos argumentos idénticos repetidos una y otra vez indican que el modelo no converge, que es la conclusión a la que llegó upstream. El único mecanismo de protección contra repeticiones de v0.3.5 coincide con web_fetch y web_search; una repetición de read_file no se detecta |
| 4. ¿Cuál fue el coste? | llm_usage.sqlite3 en el directorio de configuración, agregado por fecha y fuente; en el lado del gateway, por clave y por día en usage | Dream está etiquetado como dream. Heartbeat está etiquetado como cron, así que filtrar por "heartbeat" no encuentra nada |
No tienes que esperar dos horas al ciclo cron para reproducirlo. El comando /dream ejecuta el mismo trabajo bajo demanda e informa de la misma distinción en el chat: Dream completed in Ns. o Dream completed in Ns; no memory changes. si tiene éxito, frente a Dream did not complete after Ns (reason); memory cursor was not advanced. cuando no termina. Otro cambio de v0.3.5 importa mientras buscas pruebas: los archivos JSONL de sesión se trasladaron al árbol sessions/<workspace-id>/ del directorio de configuración, por lo que las rutas de v0.3.0 citadas en el hilo del issue no son donde se encuentra tu punto de control.
Cinco palancas y qué respalda realmente cada una
| Palanca | Evidencia que la respalda | Cuándo gana | Lo que te cuesta |
|---|---|---|---|
| Actualiza de v0.3.0 a v0.3.5 | El commit 4e2640f, en v0.3.5 y no en v0.3.0, hace que el cursor avance únicamente cuando el motivo de detención es completed | Tu síntoma es memoria omitida, no gasto | No acorta un bucle que no converge, y nadie ha vuelto a probar #5781 en 0.3.5 |
| Cambia el modelo para Dream | El único remedio con un antes y un después: en la auditoría del informante, un lote en el que un modelo pasó 91 minutos sin terminar fue completado por la siguiente ejecución con otro modelo en aproximadamente un minuto y con 6 llamadas de herramientas, usando el mismo prompt, el mismo historial y las mismas herramientas | La calidad del chat debe mantenerse en tu modelo caro | Requiere v0.3.5 y un preset definido; en v0.3.0 el campo no resuelve a nada |
Reduce maxToolIterations | Editable en la configuración de ejecución de WebUI, mínimo 1, por cada webui/settings_runtime.py | Quieres un límite máximo en el peor caso mientras diagnosticas | Una sola perilla para chat, Dream, heartbeat y subagentes; además, una ejecución limitada no avanza el cursor, por lo que el mismo lote se vuelve a intentar en el siguiente ciclo |
| Ralentiza o desactiva Dream | intervalH, o dream.enabled en la WebUI; el PR #5407 fusionado retira el trabajo persistido cuando se desactiva | La consolidación no compensa su coste en tu carga de trabajo | Pierdes la consolidación de memoria, que es precisamente la función |
| Configura la ruta para que el reenvío sea más barato | En v0.3.5 exactamente dos especificaciones de proveedor establecen supports_prompt_caching: anthropic y openrouter; el campo tiene false como valor predeterminado | Aceptas que haya ejecuciones largas y quieres que cuesten menos | Depende de la ruta de protocolo que hayas configurado y de que verifiques tú mismo el uso devuelto |
Los dos IDs de modelo del informante fueron deepseek/deepseek-v4-flash-0731 y openai/gpt-5.6-luna, tal como los escribió mediante OpenRouter; esos IDs y sus precios no se comprobaron aquí, así que interpreta la comparación como evidencia de que el modelo determina la convergencia, no como una recomendación de ninguno de los dos. Upstream llegó a la misma conclusión: al anunciar el cierre del PR #5782 en el issue #5781, chengyongru escribió que un límite fijo de iteraciones de Dream no aborda el problema subyacente de convergencia dependiente del modelo y puede hacer que ejecuciones que de otro modo serían viables se reintenten repetidamente.
Qué cuesta una ejecución limitada: aritmética ilustrativa
Esto es aritmética ilustrativa de tokens, no el coste medido de una tarea ni un límite de facturación. nanobot no publica ninguna cifra de tokens por ejecución para Dream, así que cada dato de entrada es una suposición que debes sustituir por tu propia medición. Supón una ejecución que alcanza el límite en 201 turnos del asistente — la cantidad de la auditoría del informante — y que cada uno reenvía un prompt mantenido constante en 25,000 tokens, su caracterización de su propio espacio de trabajo, no una cifra publicada. Eso equivale a 5.03M tokens de entrada. Los tokens de salida se excluyen, y un prompt real crece con cada resultado de herramienta, por lo que esto subestima una ejecución real en dos sentidos simultáneamente. Las tarifas son precios actuales del catálogo de Kunavo.
| Modelo | Entrada por 1M | Lectura de caché por 1M | Una ejecución limitada, sin caché | La misma ejecución, reenvíos leídos desde la caché |
|---|---|---|---|---|
| Claude Haiku 4.5 | $0.70 | $0.07 | $3.52 | $0.37 |
| GPT-5.6 Terra | $0.70 | $0.07 | $3.52 | $0.37 |
| Claude Sonnet 5 | $1.40 | $0.14 | $7.04 | $0.74 |
La última columna supone un caso ideal que nada de esto ha comprobado: el primer envío se factura a la tarifa de entrada normal y todos los 200 reenvíos se sirven como lecturas de caché. Una escritura de caché se factura a su propia tarifa, que en algunos modelos es superior a la tarifa de entrada normal; una entrada de caché caduca; y un prompt que crece se vuelve a escribir en lugar de releerse. Por eso, considera la diferencia entre las dos últimas columnas como el tamaño del beneficio, no como un presupuesto. El único punto es que, en una ejecución larga y repetitiva, el dinero se va en el reenvío, no en el modelo.
Que los marcadores de caché lleguen o no a la red depende de la configuración de nanobot y es visible en el código fuente distribuido. Una clave de proveedor inventada bajo providers se trata como un proveedor compatible con OpenAI normal y deja supports_prompt_caching en su valor predeterminado false, por lo que el cliente de nanobot no envía marcadores cache_control en esa ruta, ni siquiera para un ID de modelo con forma de Claude. Mantener el preset en el proveedor integrado anthropic y sobrescribir providers.anthropic.apiBase hace que se conserven. Esto describe lo que envía el cliente de nanobot, no lo que cualquier endpoint hace por su cuenta, y Kunavo no ha probado nanobot en ejecución. Lee el uso devuelto de una llamada real antes de presupuestar un trabajo recurrente como almacenado en caché: la documentación de caché muestra cómo se ve una caché funcional y la referencia de la URL base proporciona ambas convenciones de endpoint.
El importe del catálogo de Kunavo es un mínimo de facturación, no un límite: cuando upstream informa de su cargo, la factura es el mayor valor entre el coste del catálogo y el coste de upstream multiplicado por el margen aplicable. La recarga mínima es $10 en crédito prepago: un mínimo de financiación, no una tarifa por tarea ni una suscripción. Consulta detalles de facturación y optimización de costes para conocer el método de medición que sustituye las suposiciones anteriores.
Verifica la corrección en una ejecución
Cambia una sola cosa, ejecuta /dream una vez en lugar de esperar a la programación y comprueba los tres marcadores en orden: ninguna advertencia Max iterations (200) reached, una línea cursor advanced to … en lugar de cursor remains at …, y una cantidad plausible de llamadas de herramientas entre ambas. Después, lee el cargo que tu propia cuenta registró para ese intervalo, por clave y por día, en usage; el almacén de nanobot cuenta tokens, no dinero. Si estás configurando el endpoint por primera vez, la guía de inicio rápido y la página de configuración de nanobot cubren ambas rutas de protocolo, y crear una cuenta de Kunavo es el paso previo a financiar una clave. ¿Estás comparando tiempos de ejecución? nanobot frente a OpenClaw coloca las dos cadencias de fondo una junto a otra, y el directorio de APIs de agentes indexa los clientes por protocolo de red.
Preguntas frecuentes
¿nanobot está realmente atrapado en un bucle infinito?
No, y un mantenedor lo afirma públicamente. Al comentar la incidencia #5324 el 10 de agosto de 2026, chengyongru escribió que el ejecutor de agentes de nanobot está limitado por agents.defaults.maxToolIterations, cuyo valor predeterminado es 200, por lo que no se trata literalmente de un bucle infinito; añadió que un bucle largo pero limitado aún puede causar un uso acumulado de tokens muy elevado, así que el impacto práctico es real. El código fuente distribuido de v0.3.5 coincide: max_tool_iterations tiene el valor predeterminado 200 en nanobot/config/schema.py y agent/loop.py registra la advertencia «Max iterations (200) reached» cuando una ejecución alcanza ese límite. Lo que la gente llama un bucle infinito es una ejecución que alcanza ese techo; en los registros de un usuario eso significó 201 turnos del asistente volviendo a leer los mismos dos archivos.
¿Por qué no hace nada establecer dream.maxIterations?
Porque el campo ya no existe. En nanobot v0.3.0, la configuración de Dream incluía max_iterations, max_batch_size y annotate_line_ages, cada uno marcado en el código fuente con el comentario «Deprecated: no longer used»; en la etiqueta v0.3.5 los tres han desaparecido y la clase solo define enabled, intervalH, cron y modelOverride. El mantenedor chengyongru declaró el 15 de septiembre de 2026 que dream.maxIterations se marcó intencionadamente como obsoleto y sin uso cuando Dream se trasladó al bucle normal de agentes, y desde entonces se eliminó de main. La solicitud de cambios que habría podido restaurarlo, la #5782, se cerró sin fusionar al día siguiente. Escribir esa clave en una configuración de v0.3.5 escribe una clave que el esquema no define.
¿Cómo limito los tokens solo para la tarea Dream de nanobot?
No como presupuesto acumulado, en nanobot v0.3.5. Nada del código distribuido suma los tokens de una ejecución y la detiene al alcanzar una cifra, y agents.defaults.maxToolIterations es el único límite de iteraciones; se aplica a todo el proceso, a los turnos de chat normales, Dream, el heartbeat y los subagentes al mismo tiempo. Puedes limitar dos cosas exclusivamente a Dream mediante agents.defaults.dream.modelOverride: el modelo y los límites por llamada propios de ese preset, porque ModelPresetConfig contiene maxTokens y contextWindowTokens, y dream_runtime() resuelve el preset nombrado en el entorno de ejecución bajo el que se ejecuta Dream. Esos límites afectan a cada llamada individual, no al total de la ejecución, de modo que 200 llamadas con un maxTokens bajo siguen siendo 200 llamadas. También puedes limitar el calendario mediante intervalH. Un presupuesto acumulado es algo que el proyecto original ha rechazado añadir por ahora: en la incidencia #5781, el 16 de septiembre de 2026, el mismo día que cerró la PR #5782, chengyongru escribió que un presupuesto de tokens o recursos para tareas en segundo plano requiere un diseño más detallado que cubra la unidad y el alcance del presupuesto, la semántica de terminación, el comportamiento de reintentos y cursores, la observabilidad y la interacción con distintos modelos, y que no planean avanzar en ello por ahora.
¿Cómo detengo una ejecución Dream de nanobot que ya está en curso?
No con /stop, según la lectura del código fuente de v0.3.5. /stop está documentado como una cancelación del turno activo del agente para este chat y se implementa como una cancelación sobre las tareas registradas bajo la clave de sesión de ese chat, mientras que una ejecución Dream se crea bajo su propia clave efímera con el formato dream:YYYYMMDD-HHMMSS. Es una lectura de la ruta de código, no un resultado probado: aquí no se ejecutó /stop contra una ejecución Dream activa. Las palancas documentadas son /restart, desactivar agents.defaults.dream.enabled o detener el proceso del gateway. La PR fusionada #5407 en v0.3.5 es la que hace que desactivar la función retire realmente la tarea del sistema persistida, en lugar de dejarla programada.
¿Cómo veo cuántos tokens usaron las tareas en segundo plano de nanobot?
Lee el almacén de uso local, no un comando de barra. nanobot v0.3.5 registra cada llamada al modelo con un campo source tipado como user, api, cron, dream o system y las escribe en llm_usage.sqlite3 dentro del directorio de configuración — ~/.nanobot de forma predeterminada — con columnas de tokens de entrada, salida, lectura de caché y escritura de caché, y las agrega agrupadas por fecha y origen. Hay dos trampas. No existe ningún comando /insights ni /cost: las dos solicitudes de cambios que proponían uno, #3735 y #3921, se cerraron sin fusionar, y la lista integrada de v0.3.5 es /new, /compact, /stop, /restart, /status, /model, /history, /goal, /trigger, /dream, /dream-log, /dream-restore, /dream-prompt, /evaluator-prompt, /skill, /help y /pairing. Además, las etiquetas son asimétricas: el gasto de Dream lleva la etiqueta dream, pero el gasto del heartbeat lleva la etiqueta cron, porque la clave de sesión del heartbeat es literalmente «heartbeat». Son recuentos de tokens, no dinero, así que debes conciliarlos con el libro mayor del propio proveedor.
¿Actualizar a nanobot v0.3.5 corrige el bucle?
Nadie ha dicho que lo haga y esta página tampoco lo afirmará. La incidencia #5781 se presentó contra v0.3.0 y sigue abierta, etiquetada como enhancement y con prioridad p2; quien la reportó nunca volvió a probarla después de actualizar y ningún mantenedor la reprodujo. Lo que sí corrige v0.3.5 es más limitado y merece la pena: el commit 4e2640f impide que una ejecución incompleta avance el cursor de Dream y omita el historial silenciosamente, la PR fusionada #5442 hace que una ejecución incompleta informe de por qué no terminó y la PR fusionada #5325 hace que edit_file devuelva «Error: new_text must be different from old_text.» en lugar de informar de éxito cuando la edición no cambia nada. Esto último aborda el bucle de leer y editar de la incidencia #5324, no el bucle de solo lectura de la #5781. En v0.3.5 tampoco se incluye ningún bloqueo general para llamadas idénticas repetidas a herramientas. El único bloqueo de repetición del código distribuido, repeated_external_lookup_error en nanobot/utils/runtime.py, bloquea un web_fetch o web_search idéntico después de dos intentos y no coincide con ningún otro nombre de herramienta, por lo que un read_file repetido continúa hasta alcanzar el límite de iteraciones. Las cinco solicitudes de cambios que proponen un bloqueo general — #3077, #4522, #5344, #3701 y #4154 — siguen sin fusionarse.
Comprobado el 21 de septiembre de 2026: la API de GitHub para HKUDS/nanobot y para los issues #5781, #5324 y #3073, y los pull requests #5782, #5107, #5325, #5442, #5407, #3077, #4522, #5344, #3701, #4154, #3735, #3921 y #4622; el registro de PyPI para nanobot-ai 0.3.5; la documentación de memoria y configuración de nanobot 0.3.5; y el código fuente distribuido en la etiqueta v0.3.5 para cada valor predeterminado, cadena de registro y ruta de código citados arriba, comparado con v0.3.0 donde difieren. Kunavo no ha instalado ni ejecutado nanobot, ningún comportamiento descrito aquí ha sido probado por Kunavo y nadie ha vuelto a probar el bucle del issue #5781 en v0.3.5. Las tarifas de tokens proceden del catálogo actual y cada cifra en dólares es aritmética ilustrativa basada en las suposiciones indicadas.