Por qué Clawdbot se hizo viral - Y cómo crear agentes de larga duración listos para la producción con LangGraph y Milvus
Clawdbot (ahora OpenClaw) se hizo viral
Clawdbot, ahora rebautizado como OpenClaw, arrasó en Internet la semana pasada. El asistente de inteligencia artificial de código abierto creado por Peter Steinberger alcanzó más de 110.000 estrellas en GitHub en pocos días. Los usuarios publicaron vídeos en los que aparecía registrando vuelos, gestionando correos electrónicos y controlando dispositivos domésticos inteligentes de forma autónoma. Andrej Karpathy, ingeniero fundador de OpenAI, lo elogió. David Sacks, fundador e inversor de Tech, tuiteó sobre él. La gente lo llamó "Jarvis, pero real".
Luego llegaron las advertencias de seguridad.
Los investigadores encontraron cientos de paneles de administración expuestos. El bot se ejecuta con acceso root por defecto. No hay sandboxing. Las vulnerabilidades de inyección de prompt podrían permitir a los atacantes secuestrar el agente. Una pesadilla para la seguridad.
Clawdbot se hizo viral por una razón
Clawdbot se hizo viral por una razón. Se ejecuta localmente o en tu propio servidor. Se conecta a aplicaciones de mensajería que la gente ya utiliza: WhatsApp, Slack, Telegram, iMessage. Recuerda el contexto a lo largo del tiempo en lugar de olvidarlo todo después de cada respuesta. Gestiona calendarios, resume correos electrónicos y automatiza tareas entre aplicaciones.
Los usuarios tienen la sensación de que se trata de una IA personal que no interviene y está siempre activa, no solo de una herramienta de respuesta inmediata. Su modelo de código abierto y autoalojamiento atrae a los desarrolladores que desean control y personalización. Y la facilidad de integración con los flujos de trabajo existentes hace que sea fácil de compartir y recomendar.
Dos retos para crear agentes de larga duración
La popularidad de Clawdbot demuestra que la gente quiere IA que actúe, no sólo que responda. Pero cualquier agente que funcione durante largos periodos de tiempo y realice tareas reales, ya sea Clawdbot o algo creado por uno mismo, tiene que resolver dos problemas técnicos: la memoria y la verificación.
El problema de la memoria se manifiesta de múltiples formas:
Los agentes agotan su ventana de contexto a mitad de la tarea y dejan el trabajo a medias.
Pierden de vista la lista completa de tareas y declaran "terminado" demasiado pronto.
No pueden transferir el contexto entre sesiones, por lo que cada nueva sesión empieza de cero.
Todos estos problemas tienen la misma raíz: los agentes no tienen memoria persistente. Las ventanas de contexto son finitas, la recuperación entre sesiones es limitada y el progreso no se controla de forma que los agentes puedan acceder a él.
El problema de la verificación es diferente. Incluso cuando la memoria funciona, los agentes marcan las tareas como completadas tras una rápida prueba unitaria, sin comprobar si la función funciona realmente de extremo a extremo.
Clawdbot aborda ambos problemas. Almacena la memoria localmente en todas las sesiones y utiliza "habilidades" modulares para automatizar navegadores, archivos y servicios externos. El planteamiento funciona. Pero no está listo para la producción. Para uso empresarial, se necesita una estructura, capacidad de auditoría y seguridad que Clawdbot no proporciona.
Este artículo cubre los mismos problemas con soluciones listas para producción.
Para la memoria, utilizamos una arquitectura de dos agentes basada en la investigación de Anthropic: un agente inicializador que divide los proyectos en características verificables, y un agente de codificación que trabaja a través de ellos de uno en uno con traspasos limpios. Para la recuperación semántica entre sesiones, utilizamos Milvus, una base de datos vectorial que permite a los agentes buscar por significado, no por palabras clave.
Para la verificación, utilizamos la automatización del navegador. En lugar de confiar en las pruebas unitarias, el agente prueba las funciones como lo haría un usuario real.
Repasaremos los conceptos y mostraremos una implementación práctica utilizando LangGraph y Milvus.
Cómo la arquitectura de dos agentes previene el agotamiento del contexto
Cada LLM tiene una ventana de contexto: un límite sobre la cantidad de texto que puede procesar a la vez. Cuando un agente trabaja en una tarea compleja, esta ventana se llena de código, mensajes de error, historial de conversaciones y documentación. Cuando la ventana está llena, el agente se detiene o empieza a olvidar el contexto anterior. En el caso de las tareas de larga duración, esto es inevitable.
Pensemos en un agente al que se le pide lo siguiente "Construye un clon de claude.ai". El proyecto requiere autenticación, interfaces de chat, historial de conversaciones, transmisión de respuestas y docenas de funciones más. Un solo agente intentará abordarlo todo a la vez. A mitad de la implementación de la interfaz de chat, la ventana de contexto se llena. La sesión termina con código a medio escribir, sin documentación de lo que se intentó, y sin indicación de lo que funciona y lo que no. La siguiente sesión hereda un desastre. Incluso con la compactación del contexto, el nuevo agente tiene que adivinar lo que estaba haciendo la sesión anterior, depurar el código que no escribió y averiguar dónde reanudar. Se pierden horas antes de que se produzca ningún nuevo progreso.
La solución del agente doble
La solución de Anthropic, descrita en su post de ingeniería "Effective harnesses for long-running agents", consiste en utilizar dos modos diferentes de avisos: un aviso de inicialización para la primera sesión y un aviso de codificación para las sesiones posteriores.
Técnicamente, ambos modos utilizan el mismo agente subyacente, el mismo prompt de sistema, las mismas herramientas y el mismo arnés. La única diferencia es el aviso inicial al usuario. Sin embargo, dado que desempeñan funciones distintas, resulta útil pensar en ellos como dos agentes separados. A esto lo llamamos arquitectura de dos agentes.
El inicializador prepara el entorno para un progreso incremental. Toma una petición vaga y hace tres cosas:
Divide el proyecto en características específicas y verificables. No requisitos vagos como "crear una interfaz de chat", sino pasos concretos y comprobables: "el usuario hace clic en el botón Nuevo chat → aparece una nueva conversación en la barra lateral → el área de chat muestra el estado de bienvenida". El ejemplo del clon de claude.ai de Anthropic tenía más de 200 de estas características.
Crea un archivo de seguimiento del progreso. Este archivo registra el estado de finalización de cada característica, por lo que cualquier sesión puede ver lo que está hecho y lo que queda.
Escribe scripts de configuración y realiza un commit git inicial. Scripts como
init.shpermiten a las futuras sesiones poner en marcha rápidamente el entorno de desarrollo. El git commit establece una línea de base limpia.
El inicializador no sólo planifica. Crea una infraestructura que permite a las futuras sesiones empezar a trabajar inmediatamente.
El agente de codificación se encarga de cada sesión posterior. Lo hace:
Lee el archivo de progreso y los registros de git para comprender el estado actual.
Ejecuta una prueba básica de extremo a extremo para confirmar que la aplicación sigue funcionando.
Elige una característica en la que trabajar
Implementa la función, la prueba a fondo, la envía a git con un mensaje descriptivo y actualiza el archivo de progreso.
Cuando la sesión termina, el código base está en un estado fusionable: sin errores importantes, código ordenado, documentación clara. No hay trabajo a medio terminar ni ningún misterio sobre lo que se ha hecho. La siguiente sesión continúa exactamente donde se detuvo la anterior.
Utilice JSON para el seguimiento de características, no Markdown
Un detalle de implementación digno de mención: la lista de características debe ser JSON, no Markdown.
Al editar JSON, los modelos de IA tienden a modificar quirúrgicamente campos específicos. Al editar Markdown, suelen reescribir secciones enteras. Con una lista de más de 200 características, las ediciones Markdown pueden corromper accidentalmente su seguimiento del progreso.
Una entrada JSON tiene este aspecto:
json
{
"category": "functional",
"description": "New chat button creates a fresh conversation",
"steps": [
"Navigate to main interface",
"Click the 'New Chat' button",
"Verify a new conversation is created",
"Check that chat area shows welcome state",
"Verify conversation appears in sidebar"
],
"passes": false
}
Cada característica tiene pasos de verificación claros. El campo passes registra la finalización. También se recomiendan instrucciones enérgicas como "Es inaceptable eliminar o editar pruebas, ya que esto podría dar lugar a una funcionalidad ausente o defectuosa" para evitar que el agente juegue con el sistema eliminando funciones difíciles.
Cómo Milvus proporciona a los agentes memoria semántica entre sesiones
La arquitectura de dos agentes resuelve el agotamiento del contexto, pero no el olvido. Incluso con traspasos limpios entre sesiones, el agente pierde la pista de lo que ha aprendido. No puede recordar que "JWT refresh tokens" está relacionado con "user authentication" a menos que esas palabras exactas aparezcan en el archivo de progreso. A medida que el proyecto crece, la búsqueda a través de cientos de commits de git se vuelve lenta. La concordancia de palabras clave pasa por alto conexiones que serían obvias para un humano.
Aquí es donde entran en juego las bases de datos vectoriales. En lugar de almacenar texto y buscar palabras clave, una base de datos vectorial convierte el texto en representaciones numéricas del significado. Cuando buscas "autenticación de usuario", encuentra entradas sobre "tokens de actualización JWT" y "gestión de sesiones de inicio de sesión". No porque las palabras coincidan, sino porque los conceptos son semánticamente cercanos. El agente puede preguntar "¿he visto algo así antes?" y obtener una respuesta útil.
En la práctica, esto funciona incrustando registros de progreso y commits de git en la base de datos como vectores. Cuando comienza una sesión de codificación, el agente consulta la base de datos con su tarea actual. La base de datos devuelve la historia relevante en milisegundos: qué se intentó antes, qué funcionó, qué falló. El agente no parte de cero. Empieza con el contexto.
Milvus se adapta bien a este caso de uso. Es de código abierto y está diseñado para la búsqueda de vectores a escala de producción, manejando miles de millones de vectores sin sudar la gota gorda. Para proyectos más pequeños o desarrollo local, Milvus Lite puede integrarse directamente en una aplicación como SQLite. No requiere configuración de clúster. Cuando el proyecto crece, puede migrar a Milvus distribuido sin cambiar su código. Para generar incrustaciones, puede utilizar modelos externos como SentenceTransformer para un control preciso, o hacer referencia a estas funciones de incrustación incorporadas para configuraciones más sencillas. Milvus también admite la búsqueda híbrida, que combina la similitud vectorial con el filtrado tradicional, por lo que puede consultar "encontrar problemas de autenticación similares de la última semana" en una sola llamada.
Esto también resuelve el problema de la transferencia. La base de datos vectorial persiste fuera de cualquier sesión, por lo que el conocimiento se acumula con el tiempo. La sesión 50 tiene acceso a todo lo aprendido en las sesiones 1 a 49. El proyecto desarrolla la memoria institucional.
Verificación de la finalización con pruebas automatizadas
Incluso con la arquitectura de dos agentes y la memoria a largo plazo, los agentes pueden declarar la victoria demasiado pronto. Éste es el problema de la verificación.
He aquí un modo de fallo común: Una sesión de codificación termina una característica, ejecuta una prueba de unidad rápida, ve que pasa, y cambia "passes": false a "passes": true. Pero una prueba de unidad que pasa no significa que la característica funcione realmente. La API puede devolver datos correctos mientras que la interfaz de usuario no muestra nada debido a un error de CSS. El archivo de progreso dice "completo" mientras que los usuarios no ven nada.
La solución es hacer que el agente pruebe como un usuario real. Cada función de la lista de funciones tiene pasos de verificación concretos: "el usuario hace clic en el botón Nuevo chat → aparece una nueva conversación en la barra lateral → el área de chat muestra el estado de bienvenida". El agente debe verificar estos pasos literalmente. En lugar de ejecutar únicamente pruebas a nivel de código, utiliza herramientas de automatización del navegador como Puppeteer para simular el uso real. Abre la página, pulsa botones, rellena formularios y comprueba que aparecen en pantalla los elementos correctos. Sólo cuando el flujo completo pasa, el agente marca la función como completa.
Así se detectan problemas que las pruebas unitarias pasan por alto. Una función de chat puede tener una lógica backend perfecta y respuestas API correctas. Pero si el frontend no muestra la respuesta, los usuarios no ven nada. La automatización del navegador puede capturar el resultado y verificar que lo que aparece en pantalla coincide con lo que debería aparecer. El campo passes sólo se convierte en true cuando la función funciona realmente de extremo a extremo.
Sin embargo, existen limitaciones. Algunas funciones nativas del navegador no pueden automatizarse con herramientas como Puppeteer. Los selectores de archivos y los diálogos de confirmación del sistema son ejemplos comunes. Anthropic observó que las características que dependen de los modales de alerta nativos del navegador tienden a tener más errores porque el agente no puede verlos a través de Puppeteer. La solución práctica es diseñar teniendo en cuenta estas limitaciones. Utilice componentes de interfaz de usuario personalizados en lugar de diálogos nativos siempre que sea posible, para que el agente pueda probar cada paso de verificación en la lista de características.
Poniéndolo todo junto: LangGraph para el estado de sesión, Milvus para la memoria a largo plazo
Los conceptos anteriores se unen en un sistema de trabajo utilizando dos herramientas: LangGraph para el estado de sesión y Milvus para la memoria a largo plazo. LangGraph gestiona lo que ocurre dentro de una sesión: en qué función se está trabajando, qué se ha completado, qué es lo siguiente. Milvus almacena el historial de búsqueda entre sesiones: qué se ha hecho antes, qué problemas se han encontrado y qué soluciones han funcionado. Juntos, proporcionan a los agentes memoria a corto y largo plazo.
Una nota sobre esta implementación: El código que sigue es una demostración simplificada. Muestra los patrones básicos en un único script, pero no reproduce completamente la separación de sesiones descrita anteriormente. En una configuración de producción, cada sesión de codificación sería una invocación separada, potencialmente en diferentes máquinas o en diferentes momentos. MemorySaver y thread_id en LangGraph permiten esto persistiendo el estado entre invocaciones. Para ver claramente el comportamiento de reanudación, ejecute el script una vez, deténgalo y vuelva a ejecutarlo con el mismo thread_id. La segunda ejecución continuaría donde la primera lo dejó.
Python
from sentence_transformers import SentenceTransformer
from pymilvus import MilvusClient
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
import subprocess
import json
# ==================== Initialization ====================
embedding_model = SentenceTransformer(‘all-MiniLM-L6-v2’)
milvus_client = MilvusClient(“./milvus_agent_memory.db”)
# Create collection
if not milvus_client.has_collection(“agent_history”):
milvus_client.create_collection(
collection_name=“agent_history”,
dimension=384,
auto_id=True
)
# ==================== Milvus Operations ====================
def retrieve_context(query: str, top_k: int = 3):
"""Retrieve relevant history from Milvus (core element: semantic retrieval)“"”
query_vec = embedding_model.encode(query).tolist()
results = milvus_client.search(
collection_name=“agent_history”,
data=[query_vec],
limit=top_k,
output_fields=[“content”]
)
if results and results[0]:
return [hit[“entity”][“content”] for hit in results[0]]
return []
def save_progress(content: str):
"""Save progress to Milvus (long-term memory)“"”
embedding = embedding_model.encode(content).tolist()
milvus_client.insert(
collection_name=“agent_history”,
data=[{“vector”: embedding, “content”: content}]
)
# ==================== Core Element 1: Git Commit ====================
def git_commit(message: str):
"""Git commit (core element from the article)“"”
try:
# In a real project, actual Git commands would be executed
# subprocess.run(["git", "add", “.”], check=True)
# subprocess.run([“git", “commit", "-m", message], check=True)
print(f”[Git Commit] {message}")
return True
except Exception as e:
print(f”[Git Commit Failed] {e}")
return False
# ==================== Core Element 2: Test Verification ====================
def run_tests(feature: str):
“""Run tests (end-to-end testing emphasized in the article)“"”
try:
# In a real project, testing tools like Puppeteer would be called
# Simplified to simulated testing here
print(f”[Test Verification] Testing feature: {feature}")
# Simulated test result
test_passed = True # In practice, this would return actual test results
if test_passed:
print(f"[Test Passed] {feature}")
return test_passed
except Exception as e:
print(f"[Test Failed] {e}")
return False
# ==================== State Definition ====================
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
features: list # All features list
completed_features: list # Completed features
current_feature: str # Currently processing feature
session_count: int # Session counter
# ==================== Two-Agent Nodes ====================
def initialize_node(state: AgentState):
“""Initializer Agent: Generate feature list and set up work environment""”
print(“\n========== Initializer Agent Started ==========”)
<span class="hljs-comment"># Generate feature list (in practice, a detailed feature list would be generated based on requirements)</span>
features = [
<span class="hljs-string">"Implement user registration"</span>,
<span class="hljs-string">"Implement user login"</span>,
<span class="hljs-string">"Implement password reset"</span>,
<span class="hljs-string">"Implement user profile editing"</span>,
<span class="hljs-string">"Implement session management"</span>
]
<span class="hljs-comment"># Save initialization info to Milvus</span>
init_summary = <span class="hljs-string">f"Project initialized with <span class="hljs-subst">{<span class="hljs-built_in">len</span>(features)}</span> features"</span>
save_progress(init_summary)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Initialization Complete] Feature list: <span class="hljs-subst">{features}</span>"</span>)
<span class="hljs-keyword">return</span> {
**state,
<span class="hljs-string">"features"</span>: features,
<span class="hljs-string">"completed_features"</span>: [],
<span class="hljs-string">"current_feature"</span>: features[<span class="hljs-number">0</span>] <span class="hljs-keyword">if</span> features <span class="hljs-keyword">else</span> <span class="hljs-string">""</span>,
<span class="hljs-string">"session_count"</span>: <span class="hljs-number">0</span>,
<span class="hljs-string">"messages"</span>: [init_summary]
}
def code_node(state: AgentState):
“""Coding Agent: Implement, test, commit (core loop node)“"”
print(f"\n========== Coding Agent Session #{state[‘session_count’] + 1} ==========”)
current_feature = state[<span class="hljs-string">"current_feature"</span>]
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Current Task] <span class="hljs-subst">{current_feature}</span>"</span>)
<span class="hljs-comment"># ===== Core Element 3: Retrieve history from Milvus (cross-session memory) =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Retrieving History] Querying experiences related to '<span class="hljs-subst">{current_feature}</span>'..."</span>)
context = retrieve_context(current_feature)
<span class="hljs-keyword">if</span> context:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Retrieval Results] Found <span class="hljs-subst">{<span class="hljs-built_in">len</span>(context)}</span> relevant records:"</span>)
<span class="hljs-keyword">for</span> i, ctx <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(context, <span class="hljs-number">1</span>):
<span class="hljs-built_in">print</span>(<span class="hljs-string">f" <span class="hljs-subst">{i}</span>. <span class="hljs-subst">{ctx[:<span class="hljs-number">60</span>]}</span>..."</span>)
<span class="hljs-keyword">else</span>:
<span class="hljs-built_in">print</span>(<span class="hljs-string">"[Retrieval Results] No relevant history (first time implementing this type of feature)"</span>)
<span class="hljs-comment"># ===== Step 1: Implement feature =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Starting Implementation] <span class="hljs-subst">{current_feature}</span>"</span>)
<span class="hljs-comment"># In practice, an LLM would be called to generate code</span>
implementation_result = <span class="hljs-string">f"Implemented feature: <span class="hljs-subst">{current_feature}</span>"</span>
<span class="hljs-comment"># ===== Step 2: Test verification (core element) =====</span>
test_passed = run_tests(current_feature)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> test_passed:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Session End] Tests did not pass, fixes needed"</span>)
<span class="hljs-keyword">return</span> state <span class="hljs-comment"># Don't proceed if tests fail</span>
<span class="hljs-comment"># ===== Step 3: Git commit (core element) =====</span>
commit_message = <span class="hljs-string">f"feat: <span class="hljs-subst">{current_feature}</span>"</span>
git_commit(commit_message)
<span class="hljs-comment"># ===== Step 4: Update progress file =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Updating Progress] Marking feature as complete"</span>)
<span class="hljs-comment"># ===== Step 5: Save to Milvus long-term memory =====</span>
progress_record = <span class="hljs-string">f"Completed feature: <span class="hljs-subst">{current_feature}</span> | Commit message: <span class="hljs-subst">{commit_message}</span> | Test status: passed"</span>
save_progress(progress_record)
<span class="hljs-comment"># ===== Step 6: Update state and prepare for next feature =====</span>
new_completed = state[<span class="hljs-string">"completed_features"</span>] + [current_feature]
remaining_features = [f <span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> state[<span class="hljs-string">"features"</span>] <span class="hljs-keyword">if</span> f <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> new_completed]
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Progress] Completed: <span class="hljs-subst">{<span class="hljs-built_in">len</span>(new_completed)}</span>/<span class="hljs-subst">{<span class="hljs-built_in">len</span>(state[<span class="hljs-string">'features'</span>])}</span>"</span>)
<span class="hljs-comment"># ===== Core Element 4: Session end (clear session boundary) =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[Session End] Codebase is in clean state, safe to interrupt\n"</span>)
<span class="hljs-keyword">return</span> {
**state,
<span class="hljs-string">"completed_features"</span>: new_completed,
<span class="hljs-string">"current_feature"</span>: remaining_features[<span class="hljs-number">0</span>] <span class="hljs-keyword">if</span> remaining_features <span class="hljs-keyword">else</span> <span class="hljs-string">""</span>,
<span class="hljs-string">"session_count"</span>: state[<span class="hljs-string">"session_count"</span>] + <span class="hljs-number">1</span>,
<span class="hljs-string">"messages"</span>: [implementation_result]
}
# ==================== Core Element 3: Loop Control ====================
def should_continue(state: AgentState):
"""Determine whether to continue to next feature (incremental loop development)“"”
if state[“current_feature”] and state[“current_feature”] != “”:
return “code” # Continue to next feature
else:
print(“\n========== All Features Complete ==========”)
return END
# ==================== Build Workflow ====================
workflow = StateGraph(AgentState)
# Add nodes
workflow.add_node(“initialize”, initialize_node)
workflow.add_node(“code”, code_node)
# Add edges
workflow.add_edge(START, “initialize”)
workflow.add_edge(“initialize”, “code”)
# Add conditional edges (implement loop)
workflow.add_conditional_edges(
“code”,
should_continue,
{
“code”: “code”, # Continue loop
END: END # End
}
)
# Compile workflow (using MemorySaver as checkpointer)
app = workflow.compile(checkpointer=MemorySaver())
# ==================== Usage Example: Demonstrating Cross-Session Recovery ====================
if name == "main":
print(“=” * 60)
print(“Demo Scenario: Multi-Session Development for Long-Running Agents”)
print(“=” * 60)
<span class="hljs-comment"># ===== Session 1: Initialize + complete first 2 features =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"\n[Scenario 1] First launch: Complete first 2 features"</span>)
config = {<span class="hljs-string">"configurable"</span>: {<span class="hljs-string">"thread_id"</span>: <span class="hljs-string">"project_001"</span>}}
result = app.invoke({
<span class="hljs-string">"messages"</span>: [],
<span class="hljs-string">"features"</span>: [],
<span class="hljs-string">"completed_features"</span>: [],
<span class="hljs-string">"current_feature"</span>: <span class="hljs-string">""</span>,
<span class="hljs-string">"session_count"</span>: <span class="hljs-number">0</span>
}, config)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"\n"</span> + <span class="hljs-string">"="</span> * <span class="hljs-number">60</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"[Simulated Scenario] Developer manually interrupts (Ctrl+C) or context window exhausted"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"="</span> * <span class="hljs-number">60</span>)
<span class="hljs-comment"># ===== Session 2: Restore state from checkpoint =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"\n[Scenario 2] New session starts: Continue from last interruption"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Using the same thread_id, LangGraph automatically restores from checkpoint..."</span>)
<span class="hljs-comment"># Using the same thread_id, LangGraph will automatically restore state from checkpoint</span>
result = app.invoke({
<span class="hljs-string">"messages"</span>: [],
<span class="hljs-string">"features"</span>: [],
<span class="hljs-string">"completed_features"</span>: [],
<span class="hljs-string">"current_feature"</span>: <span class="hljs-string">""</span>,
<span class="hljs-string">"session_count"</span>: <span class="hljs-number">0</span>
}, config)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"\n"</span> + <span class="hljs-string">"="</span> * <span class="hljs-number">60</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Demo Complete!"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"="</span> * <span class="hljs-number">60</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"\nKey Takeaways:"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"1. ✅ Two-Agent Architecture (initialize + code)"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"2. ✅ Incremental Loop Development (conditional edges control loop)"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"3. ✅ Git Commits (commit after each feature)"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"4. ✅ Test Verification (end-to-end testing)"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"5. ✅ Session Management (clear session boundaries)"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"6. ✅ Cross-Session Recovery (thread_id + checkpoint)"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"7. ✅ Semantic Retrieval (Milvus long-term memory)"</span>)
The key insight is in the last part. By using the same thread_id, LangGraph automatically restores the checkpoint from the previous session. Session 2 picks up exactly where session 1 stopped — no manual state transfer, no lost progress.
Conclusión
Los agentes de IA fracasan en tareas de larga duración porque carecen de memoria persistente y verificación adecuada. Clawdbot se hizo viral por resolver estos problemas, pero su enfoque no está listo para la producción.
Este artículo cubre tres soluciones que sí lo están:
Arquitectura de dos agentes: Un inicializador divide los proyectos en características verificables; un agente de codificación trabaja en ellas de una en una con traspasos limpios. Así se evita el agotamiento del contexto y se hace un seguimiento del progreso.
Base de datos vectorial para la memoria semántica: Milvus almacena los registros de progreso y los commits de git como incrustaciones, de modo que los agentes pueden buscar por significado, no por palabras clave. La sesión 50 recuerda lo que aprendió la sesión 1.
Automatización del navegador para una verificación real: Las pruebas unitarias verifican que el código se ejecuta. Puppeteer comprueba si las características funcionan realmente probando lo que los usuarios ven en pantalla.
Estos patrones no se limitan al desarrollo de software. La investigación científica, la modelización financiera, la revisión de documentos jurídicos... cualquier tarea que abarque varias sesiones y requiera transferencias fiables puede beneficiarse de ello.
Principios básicos:
Utilizar un inicializador para dividir el trabajo en partes verificables.
Seguimiento del progreso en un formato estructurado y legible por máquina.
Almacenar la experiencia en una base de datos vectorial para su recuperación semántica.
Verificar la finalización con pruebas reales, no sólo pruebas unitarias.
Diseñe límites de sesión claros para que el trabajo pueda detenerse y reanudarse de forma segura.
Las herramientas existen. Los patrones están probados. Lo que falta es aplicarlos.
¿Listo para empezar?
Explore Milvus y Milvus Lite para añadir memoria semántica a sus agentes.
Eche un vistazo a LangGraph para gestionar el estado de la sesión
Lee la investigación completa de Anthropic sobre arneses de agentes de larga duración
¿Tiene preguntas o quiere compartir lo que está construyendo?
Únase a la comunidad Milvus Slack para conectarse con otros desarrolladores
Asista a las horas de oficina de Milvus para preguntas y respuestas en directo con el equipo
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



