Por que o Clawdbot se tornou viral - e como criar agentes de longa duração prontos para produção com LangGraph e Milvus
O Clawdbot (agora OpenClaw) tornou-se viral
O Clawdbot, agora renomeado para OpenClaw, tomou a Internet de assalto na semana passada. O assistente de IA de código aberto criado por Peter Steinberger atingiu mais de 110.000 estrelas no GitHub em apenas alguns dias. Os utilizadores publicaram vídeos em que o assistente fazia o check-in dos seus voos de forma autónoma, geria e-mails e controlava dispositivos domésticos inteligentes. Andrej Karpathy, o engenheiro fundador da OpenAI, elogiou-o. David Sacks, um fundador e investidor da Tech, tweetou sobre ele. As pessoas chamaram-lhe "Jarvis, mas real".
Depois vieram os avisos de segurança.
Os investigadores encontraram centenas de painéis de administração expostos. O bot funciona com acesso root por defeito. Não existe uma caixa de proteção. As vulnerabilidades de injeção de prompts podem permitir que os atacantes sequestrem o agente. Um pesadelo de segurança.
O Clawdbot tornou-se viral por uma razão
O Clawdbot tornou-se viral por uma razão. É executado localmente ou no seu próprio servidor. Liga-se a aplicações de mensagens que as pessoas já utilizam - WhatsApp, Slack, Telegram, iMessage. Lembra-se do contexto ao longo do tempo em vez de se esquecer de tudo após cada resposta. Gere calendários, resume e-mails e automatiza tarefas entre aplicações.
Os utilizadores têm a sensação de uma IA pessoal, sem intervenção e sempre ativa, e não apenas de uma ferramenta de resposta rápida. O seu modelo de código aberto e auto-hospedado atrai os programadores que pretendem controlo e personalização. E a facilidade de integração com fluxos de trabalho existentes facilita a partilha e a recomendação.
Dois desafios para a criação de agentes de longa duração
A popularidade do Clawdbot prova que as pessoas querem uma IA que actue, não apenas que responda. Mas qualquer agente que funcione durante longos períodos e conclua tarefas reais - quer seja o Clawdbot ou algo construído por si - tem de resolver dois desafios técnicos: memória e verificação.
O problema da memória aparece de várias maneiras:
Os agentes esgotam a sua janela de contexto a meio da tarefa e deixam para trás trabalho meio acabado
Perdem de vista a lista completa de tarefas e declaram "feito" demasiado cedo
Não podem transferir o contexto entre sessões, pelo que cada nova sessão começa do zero
Todos estes problemas têm a mesma origem: os agentes não têm memória persistente. As janelas de contexto são finitas, a recuperação entre sessões é limitada e o progresso não é registado de uma forma a que os agentes possam aceder.
O problema da verificação é diferente. Mesmo quando a memória funciona, os agentes ainda marcam as tarefas como concluídas após um rápido teste unitário - sem verificar se o recurso realmente funciona de ponta a ponta.
O Clawdbot resolve ambos. Ele armazena memória localmente em todas as sessões e usa "habilidades" modulares para automatizar navegadores, arquivos e serviços externos. A abordagem funciona. Mas não está pronta para produção. Para uso corporativo, é preciso estrutura, auditabilidade e segurança que o Clawdbot não fornece imediatamente.
Este artigo aborda os mesmos problemas com soluções prontas para produção.
Para a memória, usamos uma arquitetura de dois agentes baseada na pesquisa da Anthropic: um agente inicializador que divide os projetos em recursos verificáveis e um agente codificador que trabalha com eles um de cada vez com transferências limpas. Para a recuperação semântica entre sessões, utilizamos o Milvus, uma base de dados vetorial que permite aos agentes pesquisar por significado e não por palavras-chave.
Para a verificação, usamos a automação do navegador. Em vez de confiar nos testes unitários, o agente testa as funcionalidades da forma como um utilizador real o faria.
Iremos apresentar os conceitos e, em seguida, mostraremos uma implementação funcional usando LangGraph e Milvus.
Como a arquitetura de dois agentes evita a exaustão de contexto
Cada LLM tem uma janela de contexto: um limite para a quantidade de texto que pode processar de uma só vez. Quando um agente trabalha numa tarefa complexa, esta janela enche-se de código, mensagens de erro, histórico de conversação e documentação. Quando a janela fica cheia, o agente pára ou começa a esquecer o contexto anterior. Para tarefas de longa duração, isto é inevitável.
Considere um agente que recebe uma solicitação simples: "Construir um clone de claude.ai." O projeto requer autenticação, interfaces de conversação, histórico de conversação, respostas em fluxo contínuo e dezenas de outras funcionalidades. Um único agente tentará lidar com tudo de uma vez. A meio da implementação da interface de conversação, a janela de contexto enche-se. A sessão termina com código meio escrito, sem documentação do que foi tentado e sem indicação do que funciona e do que não funciona. A próxima sessão herda uma confusão. Mesmo com a compactação de contexto, o novo agente tem que adivinhar o que a sessão anterior estava fazendo, depurar o código que não foi escrito e descobrir onde retomar. Horas são desperdiçadas antes que qualquer novo progresso seja feito.
A solução do agente duplo
A solução da Anthropic, descrita em seu post de engenharia "Effective harnesses for long-running agents", é usar dois modos diferentes de prompt: um prompt inicializador para a primeira sessão e um prompt de codificação para as sessões seguintes.
Tecnicamente, ambos os modos usam o mesmo agente subjacente, prompt do sistema, ferramentas e chicote. A única diferença é a pergunta inicial do utilizador. Mas como eles têm funções distintas, pensar neles como dois agentes separados é um modelo mental útil. Chamamos a isto a arquitetura de dois agentes.
O inicializador configura o ambiente para o progresso incremental. Ele recebe um pedido vago e faz três coisas:
Divide o projeto em caraterísticas específicas e verificáveis. Não são requisitos vagos como "criar uma interface de chat", mas passos concretos e testáveis: "o utilizador clica no botão Novo Chat → aparece uma nova conversa na barra lateral → a área de chat mostra o estado de boas-vindas". O exemplo do clone claude.ai da Anthropic tinha mais de 200 caraterísticas desse tipo.
Cria um ficheiro de acompanhamento do progresso. Este ficheiro regista o estado de conclusão de cada funcionalidade, para que qualquer sessão possa ver o que está feito e o que falta fazer.
Escreve scripts de configuração e faz um git commit inicial. Scripts como
init.shpermitem que sessões futuras iniciem o ambiente de desenvolvimento rapidamente. O git commit estabelece uma linha de base limpa.
O inicializador não planeia apenas. Ele cria uma infraestrutura que permite que as sessões futuras comecem a trabalhar imediatamente.
O agente de codificação lida com cada sessão subsequente. Ele:
Lê o arquivo de progresso e os logs do git para entender o estado atual
Executa um teste básico de ponta a ponta para confirmar que o aplicativo ainda funciona
Escolhe um recurso para trabalhar
Implementa a funcionalidade, testa-a minuciosamente, faz o commit no git com uma mensagem descritiva e actualiza o ficheiro de progresso
Quando a sessão termina, a base de código está num estado que pode ser fundido: sem grandes bugs, código ordenado, documentação clara. Não há nenhum trabalho meio-terminado e nenhum mistério sobre o que foi feito. A próxima sessão começa exatamente onde esta parou.
Use JSON para rastreamento de funcionalidades, não Markdown
Um detalhe de implementação que vale a pena notar: a lista de recursos deve ser JSON, não Markdown.
Ao editar JSON, os modelos de IA tendem a modificar cirurgicamente campos específicos. Ao editar Markdown, eles geralmente reescrevem seções inteiras. Com uma lista de mais de 200 recursos, as edições de Markdown podem acidentalmente corromper seu acompanhamento de progresso.
Uma entrada JSON tem este aspeto:
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 recurso tem etapas de verificação claras. O campo passes regista a conclusão. Instruções com palavras fortes como "É inaceitável remover ou editar testes porque isso pode levar a funcionalidades ausentes ou com erros" também são recomendadas para evitar que o agente manipule o sistema excluindo recursos difíceis.
Como é que o Milvus dá aos agentes memória semântica ao longo das sessões
A arquitetura de dois agentes resolve o esgotamento do contexto, mas não resolve o esquecimento. Mesmo com transferências limpas entre sessões, o agente perde a noção do que aprendeu. Ele não consegue lembrar que "tokens de atualização JWT" está relacionado à "autenticação de usuário", a menos que essas palavras exatas apareçam no arquivo de progresso. À medida que o projeto cresce, a pesquisa em centenas de commits do git torna-se lenta. A correspondência de palavras-chave perde ligações que seriam óbvias para um humano.
É aqui que entram as bases de dados vectoriais. Em vez de armazenar texto e procurar palavras-chave, uma base de dados vetorial converte texto em representações numéricas de significado. Quando pesquisa "autenticação de utilizador", encontra entradas sobre "tokens de atualização JWT" e "tratamento de sessões de início de sessão". Não porque as palavras coincidem, mas porque os conceitos são semanticamente próximos. O agente pode perguntar "já vi algo parecido com isto antes?" e obter uma resposta útil.
Na prática, isto funciona através da incorporação de registos de progresso e commits git na base de dados como vectores. Quando uma sessão de programação começa, o agente consulta a base de dados com a sua tarefa atual. A base de dados devolve o histórico relevante em milissegundos: o que foi tentado antes, o que funcionou, o que falhou. O agente não começa do zero. Começa com o contexto.
O Milvus é uma boa opção para este caso de utilização. É de código aberto e foi concebido para pesquisa de vectores em escala de produção, lidando com milhares de milhões de vectores sem esforço. Para projectos mais pequenos ou desenvolvimento local, o Milvus Lite pode ser incorporado diretamente numa aplicação como o SQLite. Não é necessária a configuração de um cluster. Quando o projeto crescer, pode migrar para o Milvus distribuído sem alterar o seu código. Para gerar embeddings, pode utilizar modelos externos como o SentenceTransformer para um controlo mais preciso, ou fazer referência a estas funções de embedding incorporadas para configurações mais simples. O Milvus também suporta a pesquisa híbrida, combinando a semelhança vetorial com a filtragem tradicional, para que possa consultar "encontrar problemas de autenticação semelhantes da última semana" numa única chamada.
Isto também resolve o problema da transferência. A base de dados vetorial persiste fora de uma única sessão, pelo que o conhecimento se acumula ao longo do tempo. A sessão 50 tem acesso a tudo o que foi aprendido nas sessões 1 a 49. O projeto desenvolve a memória institucional.
Verificar a conclusão com testes automatizados
Mesmo com a arquitetura de dois agentes e a memória de longo prazo, os agentes podem declarar vitória demasiado cedo. Este é o problema da verificação.
Aqui está um modo de falha comum: Uma sessão de codificação termina uma funcionalidade, executa um teste unitário rápido, vê que passou e vira "passes": false para "passes": true. Mas um teste unitário aprovado não significa que a funcionalidade realmente funciona. A API pode retornar dados corretos enquanto a interface do usuário não exibe nada por causa de um bug de CSS. O ficheiro de progresso diz "completo" enquanto os utilizadores não vêem nada.
A solução é fazer com que o agente teste como um utilizador real. Cada funcionalidade da lista de funcionalidades tem etapas de verificação concretas: "o utilizador clica no botão Novo chat → aparece uma nova conversa na barra lateral → a área de chat mostra o estado de boas-vindas". O agente deve verificar estes passos literalmente. Em vez de executar apenas testes de nível de código, ele usa ferramentas de automação do navegador, como o Puppeteer, para simular o uso real. Abre a página, clica em botões, preenche formulários e verifica se os elementos certos aparecem no ecrã. Só quando o fluxo completo passa é que o agente marca a funcionalidade como concluída.
Isto detecta problemas que os testes unitários não detectam. Uma funcionalidade de chat pode ter uma lógica de backend perfeita e respostas de API corretas. Mas se o frontend não renderizar a resposta, os utilizadores não vêem nada. A automação do navegador pode fazer uma captura de ecrã do resultado e verificar se o que aparece no ecrã corresponde ao que deveria aparecer. O campo passes só se torna true quando o recurso realmente funciona de ponta a ponta.
No entanto, existem limitações. Alguns recursos nativos do navegador não podem ser automatizados por ferramentas como o Puppeteer. Seletores de arquivos e diálogos de confirmação do sistema são exemplos comuns. O Anthropic observou que os recursos que dependem de modais de alerta nativos do navegador tendem a ser mais problemáticos porque o agente não pode vê-los através do Puppeteer. A solução prática é projetar em torno dessas limitações. Use componentes de IU personalizados em vez de caixas de diálogo nativas sempre que possível, para que o agente possa testar cada etapa de verificação na lista de recursos.
Juntando tudo: LangGraph para estado de sessão, Milvus para memória de longo prazo
Os conceitos acima são reunidos num sistema funcional usando duas ferramentas: LangGraph para o estado da sessão e Milvus para a memória de longo prazo. O LangGraph gere o que está a acontecer numa única sessão: que funcionalidade está a ser trabalhada, o que foi concluído, o que se segue. O Milvus armazena o histórico pesquisável das sessões: o que foi feito antes, quais problemas foram encontrados e quais soluções funcionaram. Juntos, eles dão aos agentes memória de curto e longo prazo.
Uma nota sobre esta implementação: O código abaixo é uma demonstração simplificada. Ele mostra os padrões principais em um único script, mas não replica totalmente a separação de sessões descrita anteriormente. Em uma configuração de produção, cada sessão de codificação seria uma invocação separada, potencialmente em máquinas diferentes ou em momentos diferentes. Os sites MemorySaver e thread_id no LangGraph permitem isso, persistindo o estado entre as invocações. Para ver claramente o comportamento de retomada, execute o script uma vez, pare-o e, em seguida, execute-o novamente com o mesmo thread_id. A segunda execução continuará de onde a primeira parou.
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.
Conclusão
Os agentes de IA falham em tarefas de longa duração porque lhes falta memória persistente e verificação adequada. O Clawdbot tornou-se viral ao resolver estes problemas, mas a sua abordagem não está pronta para a produção.
Este artigo cobriu três soluções que estão:
Arquitetura de dois agentes: Um inicializador divide os projetos em recursos verificáveis; um agente de codificação trabalha com eles um de cada vez com transferências limpas. Isso evita a exaustão do contexto e torna o progresso rastreável.
Base de dados vetorial para memória semântica: O Milvus armazena registos de progresso e commits do git como embeddings, para que os agentes possam pesquisar por significado e não por palavras-chave. A sessão 50 lembra-se do que a sessão 1 aprendeu.
Automação do navegador para verificação real: Testes unitários verificam se o código é executado. O Puppeteer verifica se as funcionalidades funcionam realmente, testando o que os utilizadores vêem no ecrã.
Estes padrões não se limitam ao desenvolvimento de software. A investigação científica, a modelação financeira, a revisão de documentos jurídicos - qualquer tarefa que abranja várias sessões e exija transferências fiáveis pode beneficiar.
Os princípios fundamentais:
Utilizar um inicializador para dividir o trabalho em partes verificáveis
Acompanhar o progresso num formato estruturado e legível por máquina
Armazenar a experiência numa base de dados vetorial para recuperação semântica
Verificar a conclusão com testes reais, não apenas com testes unitários
Conceber limites de sessão limpos para que o trabalho possa ser interrompido e retomado em segurança
As ferramentas existem. Os padrões estão comprovados. O que resta é aplicá-los.
Pronto para começar?
Explore o Milvus e o Milvus Lite para adicionar memória semântica aos seus agentes
Confira o LangGraph para gerenciar o estado da sessão
Leia a pesquisa completa da Anthropic sobre arreios de agentes de longa duração
Tem dúvidas ou quer partilhar o que está a construir?
Junte-se à comunidade Milvus Slack para se conectar com outros desenvolvedores
Participe no Milvus Office Hours para obter perguntas e respostas ao vivo com a equipa
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



