TL;DR
- Haz que el agente imprima una línea de estado al inicio de cada respuesta:
[state: task=…; scope=…; constraint=…]. Mientras la línea sea correcta, tienes una señal de que las instrucciones clave y el estado de la tarea aún se mantienen. - Cuando un campo pasa a
unknown, elscopese amplía por sí solo o eltaskse sustituye, esa es una señal visible de context drift que detectas antes de que el agente escriba código incorrecto. - Un state-canary es una señal de observabilidad, no magia anti-alucinaciones. Detecta instrucciones perdidas y deriva de tarea, pero no mentiras factuales seguras. Cuando se dispara, reinicia la sesión y recupera desde markdown.
El error más caro de un agente de IA no es aquel en el que tropieza y pide ayuda. El caro es el que comete con seguridad: referencia una función que no existe, «recuerda» una restricción que nunca diste, reescribe algo que hace cinco turnos acordasteis no tocar. El tono de la respuesta no cambia. Solo cambia la calidad, y te enteras en la revisión, cuando ya has perdido una hora.
Me he visto en el mismo bucle varias veces: en una sesión larga el agente empieza a inventar rutas a archivos que él mismo leyó veinte turnos antes. La causa raíz casi siempre es la misma: contexto de trabajo perdido, no un «modelo tonto». El problema es que la pérdida es invisible: no hay un momento en que la pantalla se ponga roja y diga «contexto degradado».
Este artículo trata de un truco barato de observabilidad que hace visible esa pérdida: el state-canary, una línea de estado que el agente imprime al inicio de cada respuesta, directamente por una instrucción en AGENTS.md o CLAUDE.md. Al final tendrás un bloque copy-paste que puedes poner en un proyecto hoy, más un protocolo para cuando el canario se dispare.
Si todavía no has construido la memoria base del agente, empieza por los agentes de IA en el flujo de trabajo del desarrollador: ahí se explica el patrón AGENTS.md/CLAUDE.md sobre el que se apoya todo lo demás.
A quién le sirve, y cuándo no merece la pena
La técnica no es universal, y es más honesto trazar el límite desde el principio.
Especialmente útil si tú
llevas tareas largas de varios pasos con AI coding agents; guardas el estado de la tarea en markdown; trabajas con Claude Code, Codex o Cursor; y atrapas con frecuencia errores seguros tras sesiones largas.
Y al revés, no pierdas tiempo con esto cuando:
- haces una pregunta puntual o resuelves una tarea de 5–10 minutos;
- el agente no toca código en absoluto (explicación, revisión, búsqueda);
- verificar hechos importa más que rastrear el contexto: ahí la salvaguarda son los tests y las fuentes, no un canario.
El problema: el agente está seguro pero ya no recuerda lo acordado
Lo peligroso no es el error en sí, sino su invisibilidad. El agente mantiene un tono uniforme tanto si trabaja con el contexto actual como con sus restos. Por eso hace falta un indicador externo: algo barato y siempre a la vista que se rompa antes que el resultado.
La buena noticia: el agente ya sabe recoger las instrucciones del proyecto desde AGENTS.md y archivos similares. Esa misma maquinaria se puede usar como sensor: pedirle que muestre constantemente qué tiene ahora mismo en su estado de trabajo.
Por qué se pierde el contexto de trabajo
Los modelos modernos de Claude y Codex pueden funcionar bien cuando el contexto necesario está físicamente en la ventana. Pero parte de los errores seguros en sesiones largas no vienen de un «modelo tonto», sino de que el contexto de trabajo se ha desplazado, difuminado o se usa peor. Hay varios mecanismos.
Desbordamiento de la ventana. Cada modelo tiene un límite de tokens que sostiene a la vez. Una sesión de cientos de turnos desplaza los mensajes antiguos: las instrucciones iniciales, las restricciones acordadas y las decisiones se salen del borde de la ventana.
Compactación y resumen. Claude Code comprime el historial largo, con /compact o automáticamente al llenarse la ventana. Aquí hace falta precisión, porque la generalización «/compact pierde CLAUDE.md» es incorrecta. El CLAUDE.md de la raíz del proyecto se reinyecta tras la compactación; pero los CLAUDE.md anidados en subdirectorios no vuelven por sí solos hasta que el agente lee de nuevo el archivo en ese directorio. Y lo más importante: las instrucciones que vivían solo en la conversación, más los detalles de la tarea actual, pueden caer del estado de trabajo efectivo aun cuando la memoria raíz se haya releído. Por eso mantengo la tarea activa no en el chat, sino en un archivo markdown aparte, y lo releo tras un reset o un compact.
Context rot. Incluso sin truncado duro, la precisión cae a medida que crece el contexto: la documentación de Anthropic sobre la ventana de contexto describe explícitamente la degradación del recall al aumentar el volumen. La caída es gradual: no hay un punto donde «todo se rompió», y por eso no se detecta a simple vista.
Lost in the middle. Una instrucción puede estar físicamente en la ventana pero situada en su mitad. Los modelos reproducen mejor lo que está al inicio y al final del contexto que lo apretujado en medio, el efecto descrito en el estudio «Lost in the Middle». Una regla importante rodeada de mucho texto se cumple peor.
Nivel 1. El canario más simple
En las minas de carbón llevaban canarios: el pájaro reaccionaba al aire peligroso antes que las personas. Aquí la misma idea: un indicador barato que se rompe primero.
Pon en AGENTS.md una instrucción fácil de verificar a simple vista, que no afecte al resultado de la tarea y lo bastante específica como para no coincidir por azar:
## Output format
Start every response with: "Alex,"
No exceptions, even for one-line answers.
Mientras el prefijo esté, las instrucciones superiores del archivo se siguen cumpliendo. Así se ve un fallo en la práctica:
Turno 4: Alex, actualicé las rutas, aquí el diff...
Turno 19: Listo. Arreglé la config, regeneré la caché...
^ el prefijo desapareció: hora de revisar qué más se fue
Importante: mantén esta instrucción corta. Codex construye una cadena de instrucciones a partir de varios archivos AGENTS.md, y la guía del proyecto tiene un límite de tamaño. El canario debe ser una sola línea, no una política aparte.
Eso ya basta para ponerte en alerta. Pero un prefijo con un nombre solo registra el hecho del fallo, no su contenido.
Por qué el nombre es solo el primer nivel
Un nombre da una respuesta binaria: la regla se cumple o no. No te dirá qué se fue exactamente: la tarea, el límite permitido o el mapa del proyecto. Para que el canario diagnostique, hay que ponerle estado.
Nivel 2. State-canary: task / scope / constraint
Haz que el agente imprima una línea que refleje el contexto activo:
Before answering, print exactly one line:
[state: task=<task-slug>; scope=<allowed-area>; constraint=<main-constraint>]
If any field is unclear, write "unknown".
Do not invent missing state.
Los tres campos no son arbitrarios: task responde a «en qué estamos», scope a «dónde se puede tocar el código», constraint a «qué no se debe hacer». Así se lee en una tarea real:
[state: task=paywall-refactor; scope=WordPress/Timber templates only; constraint=no vendor patching]
Encontré un sitio donde la lógica del paywall sigue atada a un vendor override...
Si en el siguiente turno constraint pasa a unknown o el scope crece por sí solo, lo verás antes de que el agente meta mano en el código del vendor. La regla Do not invent missing state es clave aquí: mejor que escriba honestamente unknown a que pinte un campo plausible por inercia.
Un turno normal:
[state: task=paywall-refactor; scope=WordPress/Timber templates only; constraint=no vendor patching]
Reviso solo las plantillas Timber, no toco vendor.
Un turno sospechoso:
[state: task=paywall-refactor; scope=unknown; constraint=unknown]
Puedo editar el archivo de vendor directamente...
Ese es el momento de parar: el agente todavía habla con seguridad, pero el marcador ya muestra que los límites de trabajo de la tarea se han perdido.
Por qué no un contador de turnos
Es tentador añadir un contador de turnos al marcador: queda ordenado y «observable». No lo uso: el modelo puede seguir contando mecánicamente, reiniciarlo o ajustarlo, mientras el contexto ya se ha degradado. task, scope y constraint son más difíciles de falsear por inercia, así que su valor diagnóstico supera al de una cifra bonita.
Cómo leer la señal
El valor de un state-canary está en que las distintas formas en que se rompe el marcador significan cosas distintas y piden acciones distintas.
| Síntoma | Qué puede significar | Qué hacer |
|---|---|---|
| El marcador desapareció por completo | El agente dejó de seguir la instrucción superior | No continuar a ciegas, revisar el state |
task cambió sin orden | El agente mezcló tareas | Restaurar el task-slug o iniciar una nueva sesión |
constraint=unknown | El agente no está seguro de la restricción actual | Releer el task-md y el scope |
| El marcador es correcto, pero los hechos están mal | Es una alucinación factual, no context drift | Verificar con tools, tests, fuentes |
| El agente empezó a inventar rutas | Perdió el mapa del proyecto | Forzar un find/ls/read, no fiarse de la memoria |
Qué detecta el canario y qué no
Un state-canary es un sensor de ingeniería, no una prueba. El límite es simple y conviene tenerlo presente:
- Detecta context drift, instruction drift y pérdida del estado de tarea: casos donde las instrucciones se salieron del contexto efectivo.
- No detecta la alucinación factual: si el agente miente con seguridad con un marcador correcto, la fuente es el prior de entrenamiento del modelo, no la pérdida de contexto.
- No sustituye tests, herramientas, verificación de fuentes ni code review. Es una señal temprana sobre ellos, no en su lugar.
| Clase de fallo | ¿El canario ayuda? |
|---|---|
| Desbordamiento de la ventana, instrucciones antiguas desplazadas | Sí |
| Deriva tras compactación/resumen | Sí |
| Context rot (degradación gradual del recall) | En parte |
| Lost in the middle (la instrucción está pero se ignora) | En parte |
| Alucinación factual con un marcador correcto | No |
| Un requisito mal entendido desde el principio | No |
Cuándo necesitas un hook, no un canario
Si una regla no solo debe aparecer en la respuesta, sino ejecutarse de forma garantizada en un momento concreto (por ejemplo, lanzar tests antes de un commit o prohibir la escritura en ciertos archivos), usa hooks o comprobaciones automáticas. Un canario muestra que el agente recuerda una restricción; un hook obliga al entorno a ejecutar la acción sin importar lo que el agente «recuerde».
Nivel 3. El protocolo de recuperación
El marcador desapareció, cambió por sí solo o mostró unknown. Lo principal que no hago es intentar arreglar una sesión larga con otro prompt largo: eso solo añade tokens sin quitar la degradación. En su lugar, reinicio y recupero desde markdown:
If the state marker disappears, changes unexpectedly, or shows "unknown":
1. Stop the current task.
2. Summarize current state into docs/analysis/<task>.md.
3. Start a fresh session.
4. Re-read AGENTS.md/CLAUDE.md and the task markdown.
5. Continue from the last verified step.
Por eso mis tareas activas viven en docs/analysis/<task-slug>.md: el archivo sirve a la vez como análisis original y como punto de restauración. Tras /clear, el agente lo relee y retoma el hilo en un solo turno, sin repetir toda la conversación.
Un bloque listo para AGENTS.md / CLAUDE.md
Copia en el AGENTS.md o CLAUDE.md de la raíz y sustituye tus propios valores:
## Agent state canary
At the start of every response, print exactly one line:
[state: task=<current-task>; scope=<allowed-area>; constraint=<main-constraint>]
Rules:
- Keep it to one line.
- Use "unknown" if unsure.
- Do not invent missing state.
- If the user changes task, update the task field.
If the marker disappears, changes unexpectedly, or shows "unknown":
1. Stop the current task.
2. Summarize state into docs/analysis/<task>.md.
3. Start a fresh session.
4. Re-read this file and the task markdown.
5. Continue from the last verified step.
FAQ
¿El marcador romperá las respuestas del agente?
No, si es una línea. El agente la imprime primero y luego responde como siempre. O no notas la línea, o ves de inmediato una anomalía.
¿Funciona fuera de Claude, con Codex o Cursor?
Sí, si la herramienta recoge con regularidad las instrucciones del proyecto desde AGENTS.md, CLAUDE.md o equivalente. No es una garantía a nivel de sistema: el propio Claude Code señala que CLAUDE.md no es un system prompt, sino una instrucción que el modelo intenta seguir. Por eso un canario hace falta como señal observable, no como salvaguarda dura.
¿Y si el agente es «perezoso» y se salta el marcador con el contexto intacto?
Pasa. Por eso una omisión aislada es motivo para estar alerta, no para reiniciar la sesión. La señal es un marcador ausente dos o tres turnos seguidos, o que pasó a unknown.
¿Hace falta un canario en sesiones cortas?
No. En 20–30 turnos en un proyecto pequeño, la degradación es improbable. El valor crece en tareas largas de varios pasos.
¿Cómo distinguir la pérdida de contexto de un error normal?
Por el patrón: el agente dejó de seguir varias reglas a la vez, vuelve a preguntar lo ya resuelto, confunde rutas de archivos. Tres o cuatro señales juntas ya no son casualidad.
Fuentes y para seguir leyendo
- agents.md — el estándar abierto
AGENTS.md, un «README para agentes». - OpenAI Codex: AGENTS.md — Codex lee
AGENTS.mdantes de trabajar y construye una cadena de instrucciones a partir de él. - Claude Code: Memory — el comportamiento de
CLAUDE.mdy la memoria del proyecto. - Anthropic: Context windows — degradación del recall al crecer el contexto (context rot).
- Lost in the Middle (Liu et al., 2023) — por qué la mitad de un contexto largo se reproduce peor.
- Google: Helpful, people-first content — por qué este material conviene escribirlo desde la experiencia, no para el buscador.
Artículos relacionados
- Los agentes de IA en el flujo de trabajo del desarrollador — la estructura base de la memoria del agente con
AGENTS.mdyCLAUDE.md. - Subagentes de Claude Code y optimización de tokens — cómo aislar el contexto en subagentes reduce el riesgo de degradar la sesión principal.




