Mengapa Clawdbot Menjadi Viral - Dan Cara Membangun Agen Jangka Panjang yang Siap Produksi dengan LangGraph dan Milvus
Clawdbot (sekarang menjadi OpenClaw) menjadi viral
Clawdbot, yang kini berganti nama menjadi OpenClaw, menggemparkan dunia maya minggu lalu. Asisten AI sumber terbuka yang dibangun oleh Peter Steinberger ini meraih 110.000+ bintang GitHub hanya dalam beberapa hari. Para pengguna mengunggah video-video yang menunjukkan asisten ini secara otonom memeriksa penerbangan, mengelola email, dan mengendalikan perangkat rumah pintar. Andrej Karpathy, insinyur pendiri OpenAI, memujinya. David Sacks, seorang pendiri dan investor teknologi, men-tweet tentang hal ini. Orang-orang menyebutnya "Jarvis, tetapi nyata."
Kemudian muncul peringatan keamanan.
Para peneliti menemukan ratusan panel admin yang terbuka. Bot berjalan dengan akses root secara default. Tidak ada kotak pasir. Kerentanan injeksi yang cepat dapat memungkinkan penyerang membajak agen. Mimpi buruk keamanan.
Clawdbot menjadi viral karena suatu alasan
Clawdbot menjadi viral karena suatu alasan. Ia berjalan secara lokal atau di server Anda sendiri. Terhubung ke aplikasi perpesanan yang sudah digunakan orang - WhatsApp, Slack, Telegram, iMessage. Aplikasi ini mengingat konteks dari waktu ke waktu alih-alih melupakan semuanya setelah setiap balasan. Mengelola kalender, meringkas email, dan mengotomatiskan tugas-tugas di seluruh aplikasi.
Pengguna mendapatkan rasa AI pribadi yang lepas tangan dan selalu aktif-bukan hanya alat yang cepat dan responsif. Modelnya yang open-source dan self-hosted menarik bagi para pengembang yang menginginkan kontrol dan penyesuaian. Dan kemudahan mengintegrasikan dengan alur kerja yang ada membuatnya mudah untuk dibagikan dan direkomendasikan.
Dua tantangan untuk membangun agen yang berjalan lama
Popularitas Clawdbot membuktikan bahwa orang menginginkan AI yang bertindak, bukan hanya menjawab. Namun, agen apa pun yang berjalan dalam jangka waktu yang lama dan menyelesaikan tugas-tugas nyata-apakah itu Clawdbot atau sesuatu yang Anda buat sendiri-harus menyelesaikan dua tantangan teknis: memori dan verifikasi.
Masalah memori muncul dalam berbagai cara:
Agen menghabiskan jendela konteks mereka di tengah-tengah tugas dan meninggalkan pekerjaan yang setengah jadi
Mereka melupakan daftar tugas lengkap dan menyatakan "selesai" terlalu dini
Mereka tidak dapat menyerahkan konteks di antara sesi, sehingga setiap sesi baru dimulai dari awal
Semua ini berasal dari akar yang sama: agen tidak memiliki memori yang persisten. Jendela konteks terbatas, pengambilan lintas sesi terbatas, dan kemajuan tidak dilacak dengan cara yang dapat diakses oleh agen.
Masalah verifikasi berbeda. Bahkan ketika memori bekerja, agen masih menandai tugas sebagai selesai setelah pengujian unit cepat-tanpa memeriksa apakah fitur tersebut benar-benar bekerja secara menyeluruh.
Clawdbot mengatasi keduanya. Ia menyimpan memori secara lokal di seluruh sesi dan menggunakan "keterampilan" modular untuk mengotomatiskan peramban, berkas, dan layanan eksternal. Pendekatan ini berhasil. Tetapi belum siap untuk produksi. Untuk penggunaan perusahaan, Anda membutuhkan struktur, kemampuan audit, dan keamanan yang tidak disediakan oleh Clawdbot.
Artikel ini membahas masalah yang sama dengan solusi siap-produksi.
Untuk memori, kami menggunakan arsitektur dua agen berdasarkan penelitian Anthropic: agen inisialisasi yang memecah proyek menjadi fitur-fitur yang dapat diverifikasi, dan agen pengkodean yang mengerjakannya satu per satu dengan handoff yang bersih. Untuk mengingat semantik di seluruh sesi, kami menggunakan Milvus, basis data vektor yang memungkinkan agen mencari berdasarkan makna, bukan kata kunci.
Untuk verifikasi, kami menggunakan otomatisasi peramban. Alih-alih mempercayai tes unit, agen menguji fitur-fitur seperti yang dilakukan oleh pengguna sebenarnya.
Kami akan membahas konsep-konsepnya, kemudian menunjukkan implementasi yang bekerja menggunakan LangGraph dan Milvus.
Bagaimana Arsitektur Dua-Agen Mencegah Kehabisan Konteks
Setiap LLM memiliki jendela konteks: sebuah batasan berapa banyak teks yang dapat diproses sekaligus. Ketika sebuah agen mengerjakan tugas yang kompleks, jendela ini akan terisi dengan kode, pesan kesalahan, riwayat percakapan, dan dokumentasi. Setelah jendela penuh, agen akan berhenti atau mulai melupakan konteks sebelumnya. Untuk tugas yang berjalan lama, hal ini tidak dapat dihindari.
Pertimbangkan agen yang diberi perintah sederhana: "Buatlah tiruan dari claude.ai." Proyek ini membutuhkan autentikasi, antarmuka obrolan, riwayat percakapan, respons streaming, dan lusinan fitur lainnya. Satu agen akan mencoba menangani semuanya sekaligus. Di tengah-tengah implementasi antarmuka obrolan, jendela konteks terisi penuh. Sesi ini berakhir dengan kode setengah tertulis, tidak ada dokumentasi tentang apa yang telah dicoba, dan tidak ada indikasi tentang apa yang berhasil dan apa yang tidak. Sesi berikutnya mewarisi kekacauan. Bahkan dengan pemadatan konteks, agen baru harus menebak apa yang dilakukan sesi sebelumnya, men-debug kode yang tidak ditulisnya, dan mencari tahu di mana harus melanjutkan. Berjam-jam terbuang sebelum ada kemajuan baru yang dibuat.
Solusi Agen Dua Kali Lipat
Solusi Anthropic, yang dijelaskan dalam posting teknik mereka "Pemanfaatan yang efektif untuk agen yang sudah berjalan lama", adalah dengan menggunakan dua mode permintaan yang berbeda: permintaan inisialisasi untuk sesi pertama dan permintaan pengkodean untuk sesi berikutnya.
Secara teknis, kedua mode tersebut menggunakan agen, prompt sistem, alat, dan harness yang sama. Satu-satunya perbedaan adalah prompt pengguna awal. Tetapi karena keduanya memiliki peran yang berbeda, menganggapnya sebagai dua agen yang terpisah adalah model mental yang berguna. Kami menyebutnya sebagai arsitektur dua agen.
Inisialisasi mengatur lingkungan untuk kemajuan bertahap. Inisialisasi menerima permintaan yang tidak jelas dan melakukan tiga hal:
Memecah proyek menjadi fitur-fitur yang spesifik dan dapat diverifikasi. Bukan persyaratan yang tidak jelas seperti "membuat antarmuka obrolan," tetapi langkah-langkah konkret yang dapat diuji: "pengguna mengklik tombol Obrolan Baru → percakapan baru muncul di bilah sisi → area obrolan menunjukkan status selamat datang." Contoh klon claude.ai milik Anthropic memiliki lebih dari 200 fitur seperti itu.
Membuat file pelacakan kemajuan. File ini mencatat status penyelesaian setiap fitur, sehingga setiap sesi dapat melihat apa yang sudah selesai dan apa yang tersisa.
Menulis skrip penyiapan dan melakukan komit git awal. Skrip seperti
init.shmemungkinkan sesi berikutnya menjalankan lingkungan pengembangan dengan cepat. Komit git menetapkan garis dasar yang bersih.
Inisialisasi tidak hanya merencanakan. Inisialisasi menciptakan infrastruktur yang memungkinkan sesi mendatang dapat segera bekerja.
Agen pengkodean menangani setiap sesi berikutnya. Ia
Membaca berkas kemajuan dan log git untuk memahami keadaan saat ini
Menjalankan pengujian dasar dari ujung ke ujung untuk mengonfirmasi bahwa aplikasi masih berfungsi
Memilih satu fitur untuk dikerjakan
Mengimplementasikan fitur tersebut, mengujinya secara menyeluruh, berkomitmen ke git dengan pesan deskriptif, dan memperbarui file kemajuan
Saat sesi berakhir, basis kode berada dalam kondisi yang dapat digabungkan: tidak ada bug besar, kode yang teratur, dokumentasi yang jelas. Tidak ada pekerjaan yang setengah jadi dan tidak ada misteri tentang apa yang telah dilakukan. Sesi berikutnya akan melanjutkan apa yang telah dilakukan pada sesi ini.
Gunakan JSON untuk Pelacakan Fitur, Bukan Penurunan Harga
Satu detail implementasi yang perlu diperhatikan: daftar fitur harus berupa JSON, bukan Markdown.
Ketika mengedit JSON, model AI cenderung memodifikasi bidang tertentu. Ketika mengedit Markdown, mereka sering menulis ulang seluruh bagian. Dengan daftar 200+ fitur, pengeditan Markdown dapat merusak pelacakan kemajuan Anda secara tidak sengaja.
Entri JSON terlihat seperti ini:
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
}
Setiap fitur memiliki langkah-langkah verifikasi yang jelas. Bidang passes melacak penyelesaian. Instruksi dengan kata-kata yang tegas seperti "Tidak dapat diterima untuk menghapus atau mengedit tes karena hal ini dapat menyebabkan fungsionalitas yang hilang atau bermasalah" juga disarankan untuk mencegah agen mempermainkan sistem dengan menghapus fitur-fitur yang sulit.
Bagaimana Milvus Memberikan Memori Semantik kepada Agen di Seluruh Sesi
Arsitektur dua agen menyelesaikan kelelahan konteks, tetapi tidak menyelesaikan masalah lupa. Bahkan dengan handoff yang bersih di antara sesi, agen kehilangan jejak apa yang telah dipelajarinya. Agen tidak dapat mengingat bahwa "token penyegaran JWT" berhubungan dengan "autentikasi pengguna" kecuali kata-kata tersebut muncul di dalam berkas kemajuan. Seiring dengan pertumbuhan proyek, pencarian melalui ratusan komit git menjadi lambat. Pencocokan kata kunci melewatkan koneksi yang akan terlihat jelas oleh manusia.
Di sinilah basis data vektor berperan. Alih-alih menyimpan teks dan mencari kata kunci, basis data vektor mengubah teks menjadi representasi numerik dari makna. Ketika Anda mencari "otentikasi pengguna", database ini akan menemukan entri tentang "token penyegaran JWT" dan "penanganan sesi login". Bukan karena kata-katanya cocok, tetapi karena konsepnya secara semantik dekat. Agen dapat bertanya "apakah saya pernah melihat hal seperti ini sebelumnya?" dan mendapatkan jawaban yang berguna.
Dalam praktiknya, ini bekerja dengan menyematkan catatan kemajuan dan komit git ke dalam basis data sebagai vektor. Ketika sesi pengkodean dimulai, agen menanyakan basis data dengan tugas saat ini. Basis data mengembalikan riwayat yang relevan dalam hitungan milidetik: apa yang telah dicoba sebelumnya, apa yang berhasil, apa yang gagal. Agen tidak memulai dari awal. Ini dimulai dengan konteks.
Milvus sangat cocok untuk kasus penggunaan ini. Milvus bersifat open source dan dirancang untuk pencarian vektor skala produksi, menangani miliaran vektor tanpa harus berkeringat. Untuk proyek-proyek yang lebih kecil atau pengembangan lokal, Milvus Lite dapat disematkan langsung ke dalam aplikasi seperti SQLite. Tidak perlu pengaturan cluster. Ketika proyek berkembang, Anda dapat bermigrasi ke Milvus terdistribusi tanpa mengubah kode Anda. Untuk menghasilkan penyematan, Anda dapat menggunakan model eksternal seperti SentenceTransformer untuk kontrol yang lebih halus, atau mereferensikan fungsi penyematan bawaan untuk pengaturan yang lebih sederhana. Milvus juga mendukung pencarian hibrida, menggabungkan kemiripan vektor dengan pemfilteran tradisional, sehingga Anda bisa melakukan kueri "temukan masalah autentikasi serupa dari minggu lalu" dalam satu panggilan.
Hal ini juga memecahkan masalah transfer. Basis data vektor tetap ada di luar satu sesi, sehingga pengetahuan terakumulasi dari waktu ke waktu. Sesi 50 memiliki akses ke semua yang dipelajari di sesi 1 sampai 49. Proyek ini mengembangkan memori institusional.
Memverifikasi Penyelesaian dengan Pengujian Otomatis
Bahkan dengan arsitektur dua agen dan memori jangka panjang, agen masih dapat menyatakan kemenangan terlalu dini. Ini adalah masalah verifikasi.
Berikut adalah mode kegagalan yang umum terjadi: Sesi pengkodean menyelesaikan sebuah fitur, menjalankan uji unit cepat, melihatnya lulus, dan membalik "passes": false ke "passes": true. Tetapi uji unit yang lulus tidak berarti fitur tersebut benar-benar berfungsi. API mungkin mengembalikan data yang benar sementara UI tidak menampilkan apa pun karena adanya bug CSS. File progres mengatakan "selesai" sementara pengguna tidak melihat apa-apa.
Solusinya adalah membuat agen menguji seperti pengguna sebenarnya. Setiap fitur dalam daftar fitur memiliki langkah-langkah verifikasi yang konkret: "pengguna mengklik tombol Obrolan Baru → percakapan baru muncul di bilah sisi → area obrolan menunjukkan status selamat datang." Agen harus memverifikasi langkah-langkah ini secara harfiah. Alih-alih hanya menjalankan pengujian tingkat kode, agen menggunakan alat otomatisasi peramban seperti Puppeteer untuk mensimulasikan penggunaan yang sebenarnya. Alat ini membuka halaman, mengklik tombol, mengisi formulir, dan memeriksa apakah elemen yang tepat muncul di layar. Hanya ketika aliran penuh berlalu, agen menandai fitur tersebut selesai.
Hal ini menangkap masalah yang terlewatkan oleh unit test. Sebuah fitur obrolan mungkin memiliki logika backend yang sempurna dan respons API yang benar. Tetapi jika frontend tidak merender balasan, pengguna tidak melihat apa-apa. Otomatisasi browser dapat mengambil screenshot dan memverifikasi bahwa apa yang muncul di layar sesuai dengan apa yang seharusnya muncul. Bidang passes hanya akan menjadi true ketika fitur tersebut benar-benar bekerja secara end-to-end.
Akan tetapi, ada beberapa keterbatasan. Beberapa fitur bawaan peramban tidak bisa diotomatisasi oleh alat seperti Puppeteer. Pemilih file dan dialog konfirmasi sistem adalah contoh yang umum. Anthropic mencatat bahwa fitur-fitur yang mengandalkan modal peringatan bawaan peramban cenderung bermasalah karena agen tidak dapat melihatnya melalui Puppeteer. Solusi praktisnya adalah mendesain di sekitar keterbatasan ini. Gunakan komponen UI khusus alih-alih dialog asli jika memungkinkan, sehingga agen dapat menguji setiap langkah verifikasi dalam daftar fitur.
Menyatukannya: LangGraph untuk Status Sesi, Milvus untuk Memori Jangka Panjang
Konsep-konsep di atas disatukan dalam sebuah sistem kerja dengan menggunakan dua alat: LangGraph untuk status sesi dan Milvus untuk memori jangka panjang. LangGraph mengelola apa yang terjadi dalam satu sesi: fitur mana yang sedang dikerjakan, apa yang sudah selesai, apa yang berikutnya. Milvus menyimpan riwayat yang dapat dicari di seluruh sesi: apa yang telah dilakukan sebelumnya, masalah apa yang dihadapi, dan solusi apa yang berhasil. Bersama-sama, keduanya memberikan memori jangka pendek dan jangka panjang kepada agen.
Sebuah catatan tentang implementasi ini: Kode di bawah ini adalah demonstrasi yang disederhanakan. Kode ini menunjukkan pola inti dalam satu skrip, tetapi tidak sepenuhnya meniru pemisahan sesi yang dijelaskan sebelumnya. Dalam pengaturan produksi, setiap sesi pengkodean akan menjadi pemanggilan yang terpisah, mungkin pada mesin yang berbeda atau pada waktu yang berbeda. MemorySaver dan thread_id di LangGraph memungkinkan hal ini dengan mempertahankan state di antara pemanggilan. Untuk melihat perilaku resume dengan jelas, Anda menjalankan skrip sekali, hentikan, lalu jalankan lagi dengan thread_id yang sama. Proses kedua akan melanjutkan apa yang ditinggalkan oleh proses pertama.
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.
Kesimpulan
Agen AI gagal dalam tugas-tugas jangka panjang karena mereka tidak memiliki memori yang tahan lama dan verifikasi yang tepat. Clawdbot menjadi viral dengan memecahkan masalah ini, tetapi pendekatannya belum siap untuk produksi.
Artikel ini membahas tiga solusi yaitu:
Arsitektur dua agen: Inisialisasi memecah proyek menjadi fitur-fitur yang dapat diverifikasi; agen pengkodean mengerjakannya satu per satu dengan handoff yang bersih. Hal ini mencegah kelelahan konteks dan membuat kemajuan dapat dilacak.
Basis data vektor untuk memori semantik: Milvus menyimpan catatan kemajuan dan komit git sebagai embeddings, sehingga agen dapat mencari berdasarkan makna, bukan kata kunci. Sesi 50 mengingat apa yang dipelajari di sesi 1.
Otomatisasi peramban untuk verifikasi nyata: Tes unit memverifikasi bahwa kode berjalan. Dalang memeriksa apakah fitur benar-benar berfungsi dengan menguji apa yang dilihat pengguna di layar.
Pola-pola ini tidak terbatas pada pengembangan perangkat lunak. Penelitian ilmiah, pemodelan keuangan, tinjauan dokumen hukum-tugas apa pun yang mencakup beberapa sesi dan membutuhkan handoff yang andal dapat memperoleh manfaat.
Prinsip-prinsip inti:
Gunakan inisialisasi untuk memecah pekerjaan menjadi bagian-bagian yang dapat diverifikasi
Melacak kemajuan dalam format yang terstruktur dan dapat dibaca oleh mesin
Menyimpan pengalaman dalam basis data vektor untuk pengambilan semantik
Verifikasi penyelesaian dengan pengujian dunia nyata, bukan hanya pengujian unit
Desain untuk batas sesi yang bersih sehingga pekerjaan dapat dijeda dan dilanjutkan dengan aman
Alat-alatnya ada. Pola-polanya sudah terbukti. Yang tersisa adalah menerapkannya.
Siap untuk memulai?
Jelajahi Milvus dan Milvus Lite untuk menambahkan memori semantik ke agen Anda
Lihat LangGraph untuk mengelola status sesi
Baca penelitian lengkap Anthropic tentang pemanfaatan agen yang sudah berjalan lama
Ada pertanyaan atau ingin berbagi tentang apa yang sedang Anda bangun?
Bergabunglah dengan komunitas Milvus Slack untuk terhubung dengan pengembang lain
Hadiri Jam Kantor Milvus untuk tanya jawab langsung dengan tim
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



