Milvus
Zilliz
  • Home
  • Blog
  • Perché Clawdbot è diventato virale - e come costruire agenti di lunga durata pronti per la produzione con LangGraph e Milvus

Perché Clawdbot è diventato virale - e come costruire agenti di lunga durata pronti per la produzione con LangGraph e Milvus

  • Tutorials
February 03, 2026
Min Yin

Clawdbot (ora OpenClaw) è diventato virale

Clawdbot, ora ribattezzato OpenClaw, ha fatto il giro di Internet la scorsa settimana. L'assistente AI open-source costruito da Peter Steinberger ha raggiunto le 110.000 stelle GitHub in pochi giorni. Gli utenti hanno postato video in cui l'assistente effettuava autonomamente il check-in dei voli, gestiva le e-mail e controllava i dispositivi della casa intelligente. Andrej Karpathy, ingegnere fondatore di OpenAI, ne ha tessuto le lodi. David Sacks, fondatore e investitore di Tech, ne ha parlato su Twitter. La gente lo ha definito "Jarvis, ma reale".

Poi sono arrivati gli avvertimenti sulla sicurezza.

I ricercatori hanno trovato centinaia di pannelli di amministrazione esposti. Il bot funziona con accesso root per impostazione predefinita. Non c'è sandboxing. Le vulnerabilità di iniezione del prompt potrebbero consentire agli aggressori di dirottare l'agente. Un incubo per la sicurezza.

Clawdbot è diventato virale per un motivo

Clawdbot è diventato virale per un motivo. Viene eseguito localmente o sul proprio server. Si collega alle app di messaggistica che le persone già utilizzano: WhatsApp, Slack, Telegram, iMessage. Ricorda il contesto nel tempo invece di dimenticare tutto dopo ogni risposta. Gestisce i calendari, riassume le e-mail e automatizza le attività tra le varie app.

Gli utenti hanno la sensazione di un'intelligenza artificiale personale, sempre attiva, non solo di uno strumento di risposta immediata. Il suo modello open-source e self-hosted piace agli sviluppatori che desiderano controllo e personalizzazione. Inoltre, la facilità di integrazione con i flussi di lavoro esistenti ne facilita la condivisione e la raccomandazione.

Due sfide per la creazione di agenti di lunga durata

La popolarità di Clawdbot dimostra che le persone vogliono un'intelligenza artificiale che agisca, non solo che risponda. Ma qualsiasi agente che funzioni per lunghi periodi e porti a termine compiti reali, sia che si tratti di Clawdbot o di qualcosa che si costruisce da soli, deve risolvere due problemi tecnici: la memoria e la verifica.

Il problema della memoria si manifesta in diversi modi:

  • Gli agenti esauriscono la loro finestra di contesto nel bel mezzo di un'attività e si lasciano dietro un lavoro incompleto.

  • perdono di vista l'elenco completo dei compiti e dichiarano "finito" troppo presto

  • Non possono passare il contesto tra una sessione e l'altra, quindi ogni nuova sessione inizia da zero.

Tutti questi problemi hanno la stessa origine: gli agenti non hanno una memoria persistente. Le finestre di contesto sono limitate, il recupero tra le sessioni è limitato e i progressi non sono tracciati in modo accessibile agli agenti.

Il problema della verifica è diverso. Anche quando la memoria funziona, gli agenti segnano i compiti come completati dopo un rapido test dell'unità, senza verificare se la funzione funziona effettivamente da un capo all'altro.

Clawdbot affronta entrambi i problemi. Memorizza la memoria localmente tra le sessioni e utilizza "abilità" modulari per automatizzare browser, file e servizi esterni. L'approccio funziona. Ma non è pronto per la produzione. Per l'uso aziendale, occorrono struttura, verificabilità e sicurezza che Clawdbot non è in grado di fornire.

Questo articolo affronta gli stessi problemi delle soluzioni pronte per la produzione.

Per la memoria, utilizziamo un'architettura a due agenti basata sulla ricerca di Anthropic: un agente inizializzatore che suddivide i progetti in caratteristiche verificabili e un agente di codifica che li elabora uno alla volta con passaggi puliti. Per il richiamo semantico tra le sessioni, utilizziamo Milvus, un database vettoriale che consente agli agenti di effettuare ricerche in base al significato, non alle parole chiave.

Per la verifica, utilizziamo l'automazione del browser. Invece di affidarsi ai test unitari, l'agente testa le funzionalità come farebbe un utente reale.

Illustreremo i concetti e mostreremo un'implementazione funzionante utilizzando LangGraph e Milvus.

Come l'architettura a due agenti previene l'esaurimento del contesto

Ogni LLM ha una finestra di contesto: un limite alla quantità di testo che può elaborare contemporaneamente. Quando un agente lavora su un compito complesso, questa finestra si riempie di codice, messaggi di errore, cronologia delle conversazioni e documentazione. Quando la finestra è piena, l'agente si ferma o inizia a dimenticare il contesto precedente. Per le attività di lunga durata, questo è inevitabile.

Si consideri un agente che riceve una semplice richiesta: "Costruisci un clone di claude.ai". Il progetto richiede autenticazione, interfacce di chat, cronologia delle conversazioni, risposte in streaming e decine di altre funzionalità. Un singolo agente cercherà di affrontare tutto in una volta. A metà dell'implementazione dell'interfaccia di chat, la finestra di contesto si riempie. La sessione termina con codice scritto a metà, nessuna documentazione di ciò che è stato tentato e nessuna indicazione di ciò che funziona e di ciò che non funziona. La sessione successiva eredita un pasticcio. Anche con la compattazione del contesto, il nuovo agente deve indovinare cosa stava facendo la sessione precedente, eseguire il debug del codice che non ha scritto e capire dove riprendere. Si perdono ore prima di fare qualsiasi progresso.

La soluzione a due agenti

La soluzione di Anthropic, descritta nel post "Effective harnesses for long-running agents", consiste nell'utilizzare due diverse modalità di prompt: un prompt di inizializzazione per la prima sessione e un prompt di codifica per le sessioni successive.

Tecnicamente, entrambe le modalità utilizzano lo stesso agente sottostante, lo stesso prompt di sistema, gli stessi strumenti e lo stesso harness. L'unica differenza è il prompt iniziale dell'utente. Tuttavia, poiché svolgono ruoli distinti, è utile pensare a due agenti separati. Questa è l'architettura a due agenti.

L'inizializzatore imposta l'ambiente per un progresso incrementale. Prende una richiesta vaga e fa tre cose:

  • Suddivide il progetto in caratteristiche specifiche e verificabili. Non requisiti vaghi come "creare un'interfaccia di chat", ma passi concreti e testabili: "l'utente fa clic sul pulsante Nuova chat → la nuova conversazione appare nella barra laterale → l'area della chat mostra lo stato di benvenuto". L'esempio del clone claude.ai di Anthropic aveva oltre 200 funzioni di questo tipo.

  • Crea un file di monitoraggio dei progressi. Questo file registra lo stato di completamento di ogni funzionalità, in modo che ogni sessione possa vedere cosa è stato fatto e cosa rimane.

  • Scrive gli script di configurazione e fa un commit git iniziale. Gli script come init.sh consentono alle sessioni future di avviare rapidamente l'ambiente di sviluppo. Il commit git stabilisce una linea di base pulita.

L'inizializzatore non si limita a pianificare. Crea un'infrastruttura che consente alle sessioni future di iniziare a lavorare immediatamente.

L'agente di codifica gestisce ogni sessione successiva. Esso:

  • Legge il file di avanzamento e i log di git per capire lo stato attuale.

  • Esegue un test end-to-end di base per confermare che l'applicazione funziona ancora.

  • Sceglie una funzionalità su cui lavorare

  • Implementa la funzionalità, la testa accuratamente, effettua il commit su git con un messaggio descrittivo e aggiorna il file di avanzamento.

Quando la sessione termina, la base di codice è in uno stato mergeable: nessun bug importante, codice ordinato, documentazione chiara. Non c'è lavoro a metà e non c'è alcun mistero su ciò che è stato fatto. La sessione successiva riprende esattamente da dove si è interrotta quella precedente.

Usare JSON per la tracciabilità delle caratteristiche, non Markdown

Un dettaglio di implementazione degno di nota: l'elenco delle funzionalità deve essere JSON, non Markdown.

Quando si modifica JSON, i modelli AI tendono a modificare chirurgicamente campi specifici. Quando modificano Markdown, spesso riscrivono intere sezioni. Con un elenco di oltre 200 funzionalità, le modifiche in Markdown possono danneggiare accidentalmente il monitoraggio dei progressi.

Una voce JSON ha questo aspetto:

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
}

Ogni caratteristica ha fasi di verifica chiare. Il campo passes tiene traccia del completamento. Per evitare che l'agente giochi con il sistema eliminando le funzioni più difficili, si raccomanda anche di scrivere in modo deciso istruzioni come "È inaccettabile rimuovere o modificare i test, perché ciò potrebbe portare a funzionalità mancanti o difettose".

Come Milvus fornisce agli agenti una memoria semantica attraverso le sessioni

L'architettura a due agenti risolve l'esaurimento del contesto, ma non risolve la dimenticanza. Anche con passaggi puliti tra le sessioni, l'agente perde traccia di ciò che ha imparato. Non può ricordare che "i token di aggiornamento JWT" sono collegati all'"autenticazione dell'utente", a meno che queste parole esatte non compaiano nel file di avanzamento. Quando il progetto cresce, la ricerca tra centinaia di commit git diventa lenta. La corrispondenza tra le parole chiave non permette di individuare le connessioni che sarebbero ovvie per un essere umano.

È qui che entrano in gioco i database vettoriali. Invece di memorizzare il testo e cercare le parole chiave, un database vettoriale converte il testo in rappresentazioni numeriche del significato. Quando si cerca "autenticazione utente", si trovano voci su "token di aggiornamento JWT" e "gestione della sessione di login". Non perché le parole corrispondano, ma perché i concetti sono semanticamente vicini. L'agente può chiedere "ho già visto qualcosa di simile?" e ottenere una risposta utile.

In pratica, questo funziona incorporando i record di avanzamento e i commit di git nel database come vettori. Quando inizia una sessione di codifica, l'agente interroga il database con il suo compito corrente. Il database restituisce la cronologia rilevante in millisecondi: cosa è stato provato in precedenza, cosa ha funzionato, cosa è fallito. L'agente non parte da zero. Inizia con il contesto.

Milvus è adatto a questo caso d'uso. È open source e progettato per la ricerca vettoriale su scala di produzione, in grado di gestire miliardi di vettori senza sudare. Per progetti più piccoli o per lo sviluppo locale, Milvus Lite può essere integrato direttamente in un'applicazione come SQLite. Non è necessaria la configurazione di un cluster. Quando il progetto cresce, è possibile migrare a Milvus distribuito senza modificare il codice. Per la generazione di incorporazioni, è possibile utilizzare modelli esterni come SentenceTransformer per un controllo a grana fine, oppure fare riferimento alle funzioni di incorporamento integrate per configurazioni più semplici. Milvus supporta anche la ricerca ibrida, combinando la similarità vettoriale con il filtraggio tradizionale, in modo da poter chiedere "trova problemi di autenticazione simili dell'ultima settimana" con un'unica chiamata.

Questo risolve anche il problema del trasferimento. Il database dei vettori persiste al di fuori di ogni singola sessione, quindi le conoscenze si accumulano nel tempo. La sessione 50 ha accesso a tutto ciò che è stato appreso nelle sessioni dalla 1 alla 49. Il progetto sviluppa una memoria istituzionale.

Verifica del completamento con test automatici

Anche con l'architettura a due agenti e la memoria a lungo termine, gli agenti possono dichiarare la vittoria troppo presto. Questo è il problema della verifica.

Ecco una modalità di fallimento comune: Una sessione di codifica termina una funzione, esegue un rapido test unitario, lo vede passare e gira "passes": false su "passes": true. Ma il superamento del test unitario non significa che la funzione funzioni davvero. L'API potrebbe restituire dati corretti mentre l'interfaccia utente non visualizza nulla a causa di un bug dei CSS. Il file di avanzamento dice "completo", mentre gli utenti non vedono nulla.

La soluzione è fare in modo che l'agente esegua i test come un utente reale. Ogni funzione dell'elenco delle funzioni ha delle fasi di verifica concrete: "l'utente fa clic sul pulsante Nuova chat → la nuova conversazione appare nella barra laterale → l'area della chat mostra lo stato di benvenuto". L'agente deve verificare questi passaggi alla lettera. Invece di eseguire solo test a livello di codice, utilizza strumenti di automazione del browser come Puppeteer per simulare l'uso effettivo. Apre la pagina, clicca sui pulsanti, compila i moduli e controlla che sullo schermo appaiano gli elementi giusti. Solo quando l'intero flusso viene superato, l'agente contrassegna il completamento della funzione.

In questo modo si possono individuare i problemi che i test unitari non rilevano. Una funzione di chat può avere una logica di backend perfetta e risposte API corrette. Ma se il frontend non rende la risposta, gli utenti non vedono nulla. L'automazione del browser può fare uno screenshot del risultato e verificare che ciò che appare sullo schermo corrisponda a ciò che dovrebbe apparire. Il campo passes diventa true solo quando la funzione funziona davvero end-to-end.

Esistono tuttavia delle limitazioni. Alcune funzioni native del browser non possono essere automatizzate da strumenti come Puppeteer. I selezionatori di file e le finestre di conferma del sistema sono esempi comuni. Anthropic ha notato che le funzionalità che si basano su modali di avviso nativi del browser tendono a essere più difettose perché l'agente non può vederle attraverso Puppeteer. La soluzione pratica consiste nel progettare intorno a queste limitazioni. Usare componenti dell'interfaccia utente personalizzati invece di finestre di dialogo native, quando possibile, in modo che l'agente possa testare ogni fase di verifica nell'elenco delle funzionalità.

Mettere tutto insieme: LangGraph per lo stato di sessione, Milvus per la memoria a lungo termine

I concetti di cui sopra confluiscono in un sistema funzionante grazie a due strumenti: LangGraph per lo stato di sessione e Milvus per la memoria a lungo termine. LangGraph gestisce ciò che accade all'interno di una singola sessione: quale funzione è in corso di lavorazione, cosa è stato completato, cosa è in programma. Milvus memorizza la cronologia delle sessioni: cosa è stato fatto prima, quali problemi sono stati incontrati e quali soluzioni hanno funzionato. Insieme, forniscono agli agenti una memoria a breve e a lungo termine.

Una nota su questa implementazione: Il codice che segue è una dimostrazione semplificata. Mostra gli schemi principali in un singolo script, ma non replica completamente la separazione delle sessioni descritta in precedenza. In una configurazione di produzione, ogni sessione di codifica sarebbe un'invocazione separata, potenzialmente su macchine diverse o in momenti diversi. I servizi MemorySaver e thread_id di LangGraph consentono di mantenere lo stato tra un'invocazione e l'altra. Per vedere chiaramente il comportamento di ripresa, si esegue lo script una volta, lo si interrompe e poi lo si esegue di nuovo con lo stesso thread_id. La seconda esecuzione riprende da dove si era interrotta la prima.

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">&quot;Implement user registration&quot;</span>,
    <span class="hljs-string">&quot;Implement user login&quot;</span>,
    <span class="hljs-string">&quot;Implement password reset&quot;</span>,
    <span class="hljs-string">&quot;Implement user profile editing&quot;</span>,
    <span class="hljs-string">&quot;Implement session management&quot;</span>
]

<span class="hljs-comment"># Save initialization info to Milvus</span>
init_summary = <span class="hljs-string">f&quot;Project initialized with <span class="hljs-subst">{<span class="hljs-built_in">len</span>(features)}</span> features&quot;</span>
save_progress(init_summary)

<span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;[Initialization Complete] Feature list: <span class="hljs-subst">{features}</span>&quot;</span>)

<span class="hljs-keyword">return</span> {
    **state,
    <span class="hljs-string">&quot;features&quot;</span>: features,
    <span class="hljs-string">&quot;completed_features&quot;</span>: [],
    <span class="hljs-string">&quot;current_feature&quot;</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">&quot;&quot;</span>,
    <span class="hljs-string">&quot;session_count&quot;</span>: <span class="hljs-number">0</span>,
    <span class="hljs-string">&quot;messages&quot;</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">&quot;current_feature&quot;</span>]
<span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;[Current Task] <span class="hljs-subst">{current_feature}</span>&quot;</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&quot;[Retrieving History] Querying experiences related to &#x27;<span class="hljs-subst">{current_feature}</span>&#x27;...&quot;</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&quot;[Retrieval Results] Found <span class="hljs-subst">{<span class="hljs-built_in">len</span>(context)}</span> relevant records:&quot;</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&quot;  <span class="hljs-subst">{i}</span>. <span class="hljs-subst">{ctx[:<span class="hljs-number">60</span>]}</span>...&quot;</span>)
<span class="hljs-keyword">else</span>:
    <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;[Retrieval Results] No relevant history (first time implementing this type of feature)&quot;</span>)

<span class="hljs-comment"># ===== Step 1: Implement feature =====</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;[Starting Implementation] <span class="hljs-subst">{current_feature}</span>&quot;</span>)
<span class="hljs-comment"># In practice, an LLM would be called to generate code</span>
implementation_result = <span class="hljs-string">f&quot;Implemented feature: <span class="hljs-subst">{current_feature}</span>&quot;</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&quot;[Session End] Tests did not pass, fixes needed&quot;</span>)
    <span class="hljs-keyword">return</span> state  <span class="hljs-comment"># Don&#x27;t proceed if tests fail</span>

<span class="hljs-comment"># ===== Step 3: Git commit (core element) =====</span>
commit_message = <span class="hljs-string">f&quot;feat: <span class="hljs-subst">{current_feature}</span>&quot;</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&quot;[Updating Progress] Marking feature as complete&quot;</span>)

<span class="hljs-comment"># ===== Step 5: Save to Milvus long-term memory =====</span>
progress_record = <span class="hljs-string">f&quot;Completed feature: <span class="hljs-subst">{current_feature}</span> | Commit message: <span class="hljs-subst">{commit_message}</span> | Test status: passed&quot;</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">&quot;completed_features&quot;</span>] + [current_feature]
remaining_features = [f <span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> state[<span class="hljs-string">&quot;features&quot;</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&quot;[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">&#x27;features&#x27;</span>])}</span>&quot;</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&quot;[Session End] Codebase is in clean state, safe to interrupt\n&quot;</span>)

<span class="hljs-keyword">return</span> {
    **state,
    <span class="hljs-string">&quot;completed_features&quot;</span>: new_completed,
    <span class="hljs-string">&quot;current_feature&quot;</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">&quot;&quot;</span>,
    <span class="hljs-string">&quot;session_count&quot;</span>: state[<span class="hljs-string">&quot;session_count&quot;</span>] + <span class="hljs-number">1</span>,
    <span class="hljs-string">&quot;messages&quot;</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">&quot;\n[Scenario 1] First launch: Complete first 2 features&quot;</span>)
config = {<span class="hljs-string">&quot;configurable&quot;</span>: {<span class="hljs-string">&quot;thread_id&quot;</span>: <span class="hljs-string">&quot;project_001&quot;</span>}}

result = app.invoke({
    <span class="hljs-string">&quot;messages&quot;</span>: [],
    <span class="hljs-string">&quot;features&quot;</span>: [],
    <span class="hljs-string">&quot;completed_features&quot;</span>: [],
    <span class="hljs-string">&quot;current_feature&quot;</span>: <span class="hljs-string">&quot;&quot;</span>,
    <span class="hljs-string">&quot;session_count&quot;</span>: <span class="hljs-number">0</span>
}, config)

<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;\n&quot;</span> + <span class="hljs-string">&quot;=&quot;</span> * <span class="hljs-number">60</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;[Simulated Scenario] Developer manually interrupts (Ctrl+C) or context window exhausted&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;=&quot;</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">&quot;\n[Scenario 2] New session starts: Continue from last interruption&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Using the same thread_id, LangGraph automatically restores from checkpoint...&quot;</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">&quot;messages&quot;</span>: [],
    <span class="hljs-string">&quot;features&quot;</span>: [],
    <span class="hljs-string">&quot;completed_features&quot;</span>: [],
    <span class="hljs-string">&quot;current_feature&quot;</span>: <span class="hljs-string">&quot;&quot;</span>,
    <span class="hljs-string">&quot;session_count&quot;</span>: <span class="hljs-number">0</span>
}, config)

<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;\n&quot;</span> + <span class="hljs-string">&quot;=&quot;</span> * <span class="hljs-number">60</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Demo Complete!&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;=&quot;</span> * <span class="hljs-number">60</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;\nKey Takeaways:&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;1. ✅ Two-Agent Architecture (initialize + code)&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;2. ✅ Incremental Loop Development (conditional edges control loop)&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;3. ✅ Git Commits (commit after each feature)&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;4. ✅ Test Verification (end-to-end testing)&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;5. ✅ Session Management (clear session boundaries)&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;6. ✅ Cross-Session Recovery (thread_id + checkpoint)&quot;</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;7. ✅ Semantic Retrieval (Milvus long-term memory)&quot;</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.

Conclusione

Gli agenti di intelligenza artificiale falliscono nei compiti di lunga durata perché non dispongono di una memoria persistente e di una verifica adeguata. Clawdbot è diventato virale risolvendo questi problemi, ma il suo approccio non è pronto per la produzione.

Questo articolo ha trattato tre soluzioni che lo sono:

  • Architettura a due agenti: Un inizializzatore suddivide i progetti in caratteristiche verificabili; un agente di codifica li elabora uno alla volta con passaggi puliti. Questo previene l'esaurimento del contesto e rende tracciabili i progressi.

  • Database vettoriale per la memoria semantica: Milvus memorizza i record di progresso e i commit di git come embeddings, in modo che gli agenti possano cercare in base al significato, non alle parole chiave. La sessione 50 ricorda ciò che la sessione 1 ha imparato.

  • Automazione del browser per una verifica reale: I test unitari verificano che il codice funzioni. Puppeteer verifica se le funzionalità funzionano davvero, testando ciò che gli utenti vedono sullo schermo.

Questi modelli non sono limitati allo sviluppo del software. La ricerca scientifica, la modellazione finanziaria, la revisione di documenti legali: qualsiasi attività che si estende su più sessioni e richiede passaggi affidabili può trarne beneficio.

I principi fondamentali:

  • Utilizzare un inizializzatore per suddividere il lavoro in parti verificabili.

  • Tracciare i progressi in un formato strutturato e leggibile dalla macchina.

  • Memorizzare l'esperienza in un database vettoriale per il recupero semantico.

  • Verificare il completamento con test reali, non solo con test unitari.

  • Progettare per confini di sessione netti, in modo che il lavoro possa essere interrotto e ripreso in modo sicuro.

Gli strumenti esistono. I modelli sono collaudati. Quello che resta da fare è applicarli.

Pronti per iniziare?

Avete domande o volete condividere ciò che state costruendo?

    Try Managed Milvus for Free

    Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

    Get Started

    Like the article? Spread the word

    Continua a Leggere