Milvus
Zilliz
  • Home
  • Blog
  • لماذا انتشر Clawdbot على نطاق واسع - وكيفية بناء عملاء جاهزين للإنتاج طويل الأمد مع لانجغراف وميلفوس

لماذا انتشر Clawdbot على نطاق واسع - وكيفية بناء عملاء جاهزين للإنتاج طويل الأمد مع لانجغراف وميلفوس

  • Tutorials
February 03, 2026
Min Yin

انتشر Clawdbot (الآن OpenClaw) على نطاق واسع

استحوذClawdbot، الذي أعيدت تسميته الآن إلى OpenClaw، على الإنترنت الأسبوع الماضي. فقد حقق مساعد الذكاء الاصطناعي المفتوح المصدر الذي أنشأه بيتر شتاينبرغر أكثر من 110,000 نجمة على GitHub في غضون أيام قليلة. وقد نشر المستخدمون مقاطع فيديو له وهو يتفقدهم بشكل مستقل في رحلات الطيران وإدارة رسائل البريد الإلكتروني والتحكم في الأجهزة المنزلية الذكية. وأشاد به أندريه كارباثي، المهندس المؤسس في OpenAI. وقام ديفيد ساكس، مؤسس ومستثمر في مجال التكنولوجيا، بالتغريد عنها. أطلق عليه الناس اسم "جارفيس، ولكن حقيقي".

ثم جاءت التحذيرات الأمنية.

وجد الباحثون مئات من لوحات الإدارة المكشوفة. يعمل الروبوت مع وصول الجذر بشكل افتراضي. لا يوجد وضع حماية. يمكن أن تسمح ثغرات الحقن الموجه للمهاجمين باختطاف الوكيل. كابوس أمني.

انتشر Clawdbot لسبب ما

انتشر Clawdbot لسبب ما. يعمل محلياً أو على خادمك الخاص. يتصل بتطبيقات المراسلة التي يستخدمها الناس بالفعل - واتس آب، سلاك، تيليجرام، آي مسج. يتذكر السياق بمرور الوقت بدلاً من نسيان كل شيء بعد كل رد. يدير التقويمات، ويلخص رسائل البريد الإلكتروني، ويقوم بأتمتة المهام عبر التطبيقات.

ويشعر المستخدمون بأن الذكاء الاصطناعي الشخصي لا يحتاج إلى مساعدة شخصية، فهو ليس مجرد أداة مطالبة واستجابة. ويروق نموذجها المفتوح المصدر والمستضاف ذاتياً للمطورين الذين يرغبون في التحكم والتخصيص. كما أن سهولة التكامل مع تدفقات العمل الحالية تجعل من السهل مشاركتها والتوصية بها.

تحديان لبناء وكلاء طويل الأمد

تثبت شعبية Clawdbot أن شعبية Clawdbot تثبت أن الناس يريدون ذكاءً اصطناعيًا يعمل، وليس فقط يجيب. ولكن أي وكيل يعمل على فترات طويلة ويكمل مهام حقيقية - سواء كان Clawdbot أو أي شيء تقوم ببنائه بنفسك - يجب أن يحل تحديين تقنيين: الذاكرة والتحقق.

تظهرمشكلة الذاكرة بطرق متعددة:

  • يستنفد الوكلاء نافذة السياق الخاصة بهم في منتصف المهمة ويتركون وراءهم عملاً نصف منتهٍ

  • يفقدون رؤية قائمة المهام الكاملة ويعلنون "تم" في وقت مبكر جدًا

  • لا يستطيعون تسليم السياق بين الجلسات، لذا تبدأ كل جلسة جديدة من الصفر

كل هذه المشاكل تنبع من نفس الجذر: الوكلاء ليس لديهم ذاكرة ثابتة. نوافذ السياق محدودة، والاسترجاع بين الجلسات محدود، ولا يتم تتبع التقدم بطريقة يمكن للوكلاء الوصول إليها.

مشكلة التحقق مختلفة. فحتى عندما تعمل الذاكرة، لا يزال الوكلاء يضعون علامة على المهام على أنها مكتملة بعد اختبار سريع للوحدة - دون التحقق مما إذا كانت الميزة تعمل بالفعل من البداية إلى النهاية.

يعالج Clawdbot كلا الأمرين. فهو يخزن الذاكرة محليًا عبر الجلسات ويستخدم "مهارات" معيارية لأتمتة المتصفحات والملفات والخدمات الخارجية. النهج يعمل. ولكنه ليس جاهزاً للإنتاج. للاستخدام المؤسسي، تحتاج إلى بنية وقابلية تدقيق وأمان لا يوفرها Clawdbot خارج الصندوق.

تغطي هذه المقالة نفس المشاكل مع الحلول الجاهزة للإنتاج.

بالنسبة للذاكرة، نستخدم بنية مكونة من عميلين استنادًا إلى أبحاث أنثروبيك: عامل تهيئة يقسم المشاريع إلى ميزات يمكن التحقق منها، وعامل ترميز يعمل من خلالها واحدًا تلو الآخر مع عمليات تسليم نظيفة. وللاستدعاء الدلالي عبر الجلسات، نستخدم Milvus، وهي قاعدة بيانات متجهة تتيح للوكلاء البحث حسب المعنى وليس الكلمات الرئيسية.

للتحقق، نستخدم أتمتة المتصفح. بدلاً من الوثوق باختبارات الوحدة، يختبر الوكيل الميزات بالطريقة التي يختبر بها المستخدم الحقيقي.

سنستعرض المفاهيم، ثم نعرض تطبيقًا عمليًا باستخدام LangGraph و Milvus.

كيف تمنع البنية ثنائية الوكيلين استنفاد السياق

يحتوي كل برنامج LLM على نافذة للسياق: حد لمقدار النص الذي يمكنه معالجته في وقت واحد. عندما يعمل الوكيل على مهمة معقدة، تمتلئ هذه النافذة بالرمز ورسائل الخطأ وسجل المحادثة والوثائق. وبمجرد امتلاء النافذة، يتوقف الوكيل أو يبدأ في نسيان السياق السابق. بالنسبة للمهام طويلة الأمد، هذا أمر لا مفر منه.

فكّر في وكيلٍ أُعطيَ مطالبة بسيطة: "إنشاء نسخة من claude.ai." يتطلب المشروع المصادقة، وواجهات الدردشة، وسجل المحادثات، وتدفق الردود، وعشرات الميزات الأخرى. سيحاول وكيل واحد معالجة كل شيء دفعة واحدة. في منتصف تنفيذ واجهة الدردشة، تمتلئ نافذة السياق. تنتهي الجلسة بشيفرة نصف مكتوبة، ولا يوجد توثيق لما تمت تجربته، ولا إشارة إلى ما يعمل وما لا يعمل. ترث الجلسة التالية فوضى. حتى مع ضغط السياق، يجب على الوكيل الجديد تخمين ما كانت تفعله الجلسة السابقة، وتصحيح التعليمات البرمجية التي لم يكتبها، ومعرفة أين يستأنف. تضيع ساعات قبل إحراز أي تقدم جديد.

حل الوكيل ذو الشقين

يتمثل حل أنثروبيك الموصوف في منشورهم الهندسي "التسخير الفعال للوكلاء الذين يعملون لفترة طويلة" في استخدام وضعين مختلفين للمطالبات: مطالبة تهيئة للجلسة الأولى ومطالبة ترميز للجلسات اللاحقة.

من الناحية الفنية، كلا الوضعين يستخدمان نفس الوكيل الأساسي وموجه النظام والأدوات والتسخير. الفرق الوحيد هو موجه المستخدم الأولي. ولكن نظرًا لأنهما يخدمان أدوارًا مختلفة، فإن التفكير فيهما كعاملين منفصلين هو نموذج ذهني مفيد. نسمي هذه البنية ذات الوكيلين.

يقوم المُهيئ بإعداد البيئة للتقدم التدريجي. يأخذ طلبًا غامضًا ويقوم بثلاثة أشياء:

  • يقسم المشروع إلى ميزات محددة يمكن التحقق منها. ليس متطلبات غامضة مثل "إنشاء واجهة دردشة"، بل خطوات ملموسة قابلة للاختبار: "ينقر المستخدم على زر دردشة جديدة ← تظهر محادثة جديدة في الشريط الجانبي ← تظهر منطقة الدردشة حالة ترحيب." يحتوي مثال claude.ai المستنسخ من أنثروبيك على أكثر من 200 من هذه الميزات.

  • ينشئ ملف تتبع التقدم. يسجل هذا الملف حالة اكتمال كل ميزة، بحيث يمكن لأي جلسة أن ترى ما تم إنجازه وما تبقى.

  • يكتب البرامج النصية للإعداد ويجعل التزام git الأولي. تتيح البرامج النصية مثل init.sh للجلسات المستقبلية تشغيل بيئة التطوير بسرعة. يؤسس التزام git خط أساس نظيف.

لا يقوم المُهيئ بالتخطيط فقط. إنه ينشئ بنية تحتية تتيح للجلسات المستقبلية بدء العمل على الفور.

يتعاملعامل الترميز مع كل جلسة لاحقة. يقوم بـ

  • يقرأ ملف التقدم وسجلات git لفهم الحالة الحالية

  • يقوم بإجراء اختبار أساسي من طرف إلى طرف للتأكد من أن التطبيق لا يزال يعمل

  • يختار ميزة واحدة للعمل عليها

  • ينفذ الميزة ويختبرها بدقة ويرسلها إلى git مع رسالة وصفية ويحدّث ملف التقدم

عندما تنتهي الجلسة، تكون قاعدة البرمجة في حالة قابلة للدمج: لا توجد أخطاء كبيرة، كود منظم، وثائق واضحة. لا يوجد عمل غير مكتمل ولا غموض حول ما تم إنجازه. تلتقط الجلسة التالية بالضبط من حيث توقفت هذه الجلسة.

استخدم JSON لتتبع الميزات، وليس Markdown

أحد تفاصيل التنفيذ الجديرة بالملاحظة: يجب أن تكون قائمة الميزات JSON وليس Markdown.

عند تحرير JSON، تميل نماذج الذكاء الاصطناعي إلى تعديل حقول محددة بشكل جراحي. عند تحرير Markdown، فإنها غالبًا ما تعيد كتابة أقسام كاملة. مع وجود قائمة تضم أكثر من 200 ميزة، يمكن أن تؤدي عمليات تحرير Markdown إلى إفساد تتبع تقدمك عن طريق الخطأ.

يبدو إدخال JSON هكذا:

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
}

لكل ميزة خطوات تحقق واضحة. يتتبع الحقل passes الإكمال. يوصى أيضًا بتعليمات شديدة اللهجة مثل "من غير المقبول إزالة الاختبارات أو تعديلها لأن ذلك قد يؤدي إلى فقدان وظائف أو أخطاء" لمنع الوكيل من التلاعب بالنظام عن طريق حذف الميزات الصعبة.

كيف يمنح ميلفوس الوكلاء ذاكرة دلالية عبر الجلسات

تحل بنية الوكيلين مشكلة استنفاد السياق، لكنها لا تحل مشكلة النسيان. فحتى مع عمليات التسليم النظيفة بين الجلسات، يفقد الوكيل مسار ما تعلمه. لا يمكن أن يتذكر أن "رموز تحديث JWT" تتعلق ب "مصادقة المستخدم" ما لم تظهر هذه الكلمات بالضبط في ملف التقدم. مع نمو المشروع، يصبح البحث في مئات التزامات git بطيئًا. تفوت مطابقة الكلمات الرئيسية الروابط التي قد تكون واضحة للإنسان.

هنا يأتي دور قواعد البيانات المتجهة. فبدلاً من تخزين النص والبحث عن الكلمات المفتاحية، تقوم قاعدة بيانات المتجهات بتحويل النص إلى تمثيلات رقمية للمعنى. عندما تبحث عن "مصادقة المستخدم"، تجد إدخالات حول "رموز تحديث JWT" و"معالجة جلسة تسجيل الدخول". ليس لأن الكلمات متطابقة، ولكن لأن المفاهيم متقاربة من الناحية الدلالية. يمكن للوكيل أن يسأل "هل رأيت شيئًا كهذا من قبل؟" ويحصل على إجابة مفيدة.

في الممارسة العملية، يعمل هذا من خلال تضمين سجلات التقدم والتزامات git في قاعدة البيانات كمتجهات. عندما تبدأ جلسة الترميز، يستفسر الوكيل عن قاعدة البيانات بمهمته الحالية. تقوم قاعدة البيانات بإرجاع السجل ذي الصلة بالمللي ثانية: ما تمت تجربته من قبل، وما الذي نجح وما الذي فشل. لا يبدأ الوكيل من الصفر. بل يبدأ بالسياق.

ميلفوس مناسب تمامًا لحالة الاستخدام هذه. فهو مفتوح المصدر ومصمّم للبحث عن المتجهات على نطاق الإنتاج، ويتعامل مع مليارات المتجهات دون عناء. بالنسبة للمشاريع الأصغر أو التطوير المحلي، يمكن تضمين Milvus Lite مباشرةً في تطبيق مثل SQLite. لا حاجة لإعداد المجموعة. عندما ينمو المشروع، يمكنك الانتقال إلى Milvus الموزعة دون تغيير الكود الخاص بك. لتوليد التضمينات، يمكنك استخدام نماذج خارجية مثل SentenceTransformer للتحكم الدقيق، أو الرجوع إلى وظائف التضمين المدمجة هذه لإعدادات أبسط. يدعم Milvus أيضًا البحث الهجين، حيث يجمع بين التشابه المتجه والتصفية التقليدية، بحيث يمكنك الاستعلام عن "البحث عن مشكلات مصادقة مماثلة من الأسبوع الماضي" في مكالمة واحدة.

هذا يحل أيضًا مشكلة النقل. تستمر قاعدة بيانات المتجهات خارج أي جلسة واحدة، لذلك تتراكم المعرفة بمرور الوقت. تتمتع الجلسة 50 بإمكانية الوصول إلى كل ما تم تعلمه في الجلسات من 1 إلى 49. يطور المشروع الذاكرة المؤسسية.

التحقق من الاكتمال بالاختبار الآلي

حتى مع البنية المكونة من عميلين والذاكرة طويلة الأمد، لا يزال بإمكان الوكلاء إعلان النصر مبكرًا جدًا. هذه هي مشكلة التحقق.

إليك وضع فشل شائع: تنتهي جلسة البرمجة من ميزة ما، وتقوم بتشغيل اختبار وحدة سريع، وترى أنها نجحت، وتنقلب "passes": false إلى "passes": true. لكن اجتياز اختبار الوحدة لا يعني أن الميزة تعمل بالفعل. قد تُرجع واجهة برمجة التطبيقات بيانات صحيحة بينما لا تعرض واجهة المستخدم أي شيء بسبب خطأ في CSS. يقول ملف التقدم "مكتمل" بينما لا يرى المستخدمون شيئًا.

الحل هو جعل الوكيل يختبر مثل المستخدم الحقيقي. لكل ميزة في قائمة الميزات خطوات تحقق ملموسة: "ينقر المستخدم على زر دردشة جديدة ← تظهر محادثة جديدة في الشريط الجانبي ← تظهر منطقة الدردشة حالة الترحيب." يجب على الوكيل التحقق من هذه الخطوات حرفيًا. بدلاً من إجراء اختبارات على مستوى التعليمات البرمجية فقط، فإنه يستخدم أدوات أتمتة المتصفح مثل Puppeteer لمحاكاة الاستخدام الفعلي. فهو يفتح الصفحة، وينقر على الأزرار، ويملأ النماذج، ويتحقق من ظهور العناصر الصحيحة على الشاشة. فقط عندما يمر التدفق الكامل، يقوم الوكيل بوضع علامة على اكتمال الميزة.

هذا يكتشف المشاكل التي تفوتها اختبارات الوحدة. قد تحتوي ميزة الدردشة على منطق خلفي مثالي واستجابات صحيحة لواجهة برمجة التطبيقات. ولكن إذا لم تعرض الواجهة الأمامية الرد، فلن يرى المستخدمون شيئًا. يمكن لأتمتة المتصفح تصوير النتيجة والتحقق من أن ما يظهر على الشاشة يطابق ما يجب أن يظهر. لا يصبح الحقل passes true إلا عندما تعمل الميزة بشكل حقيقي من البداية إلى النهاية.

ومع ذلك، هناك قيود. لا يمكن أتمتة بعض الميزات الأصلية للمتصفح بواسطة أدوات مثل Puppeteer. منتقي الملفات ومربعات حوار تأكيد النظام هي أمثلة شائعة. لاحظت أنثروبيك أن الميزات التي تعتمد على نماذج التنبيهات الأصلية في المتصفح تميل إلى أن تكون أكثر خطأ لأن الوكيل لا يستطيع رؤيتها من خلال Puppeteer. الحل العملي هو التصميم حول هذه القيود. استخدم مكونات واجهة المستخدم المخصصة بدلاً من مربعات الحوار الأصلية حيثما أمكن، حتى يتمكن الوكيل من اختبار كل خطوة تحقق في قائمة الميزات.

تجميعها معًا: لانجغراف لحالة الجلسة، وميلفوس للذاكرة طويلة الأجل

تجتمع المفاهيم أعلاه معًا في نظام عمل باستخدام أداتين: LangGraph لحالة الجلسة و Milvus للذاكرة طويلة المدى. يدير LangGraph ما يحدث داخل جلسة عمل واحدة: ما هي الميزة التي يتم العمل عليها، وما الذي اكتمل، وما هو التالي. يخزن Milvus السجل القابل للبحث عبر الجلسات: ما تم إنجازه من قبل، وما هي المشاكل التي تمت مواجهتها، وما هي الحلول التي نجحت. وهما معاً يعطيان العملاء ذاكرة قصيرة الأجل وذاكرة طويلة الأجل.

ملاحظة حول هذا التنفيذ: الكود أدناه هو عرض توضيحي مبسط. إنه يعرض الأنماط الأساسية في برنامج نصي واحد، لكنه لا يكرر فصل الجلسات الموضح سابقًا بشكل كامل. في إعدادات الإنتاج، ستكون كل جلسة ترميز عبارة عن استدعاء منفصل، ربما على أجهزة مختلفة أو في أوقات مختلفة. MemorySaver و thread_id في LangGraph يتيح ذلك من خلال استمرار الحالة بين عمليات الاستدعاء. لرؤية سلوك الاستئناف بوضوح، يمكنك تشغيل البرنامج النصي مرة واحدة، ثم إيقافه، ثم تشغيله مرة أخرى بنفس thread_id. سيبدأ التشغيل الثاني من حيث توقف الأول.

بايثون

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.

خاتمة

يفشل وكلاء الذكاء الاصطناعي في المهام طويلة الأمد لأنهم يفتقرون إلى الذاكرة الدائمة والتحقق المناسب. انتشر Clawdbot على نطاق واسع من خلال حل هذه المشاكل، لكن نهجه ليس جاهزاً للإنتاج.

غطت هذه المقالة ثلاثة حلول هي

  • بنية ذات عميلين: يقوم عامل التهيئة بتقسيم المشاريع إلى ميزات قابلة للتحقق؛ ويعمل وكيل الترميز من خلالهما واحدًا تلو الآخر مع عمليات تسليم نظيفة. هذا يمنع استنفاد السياق ويجعل التقدم قابلاً للتتبع.

  • قاعدة بيانات متجهة للذاكرة الدلالية: يقوم ميلفوس بتخزين سجلات التقدم والتزامات git على شكل تضمينات، بحيث يمكن للوكلاء البحث حسب المعنى وليس الكلمات الرئيسية. تتذكر الجلسة 50 ما تعلمته الجلسة 1.

  • أتمتة المتصفح للتحقق الحقيقي: تتحقق اختبارات الوحدة من تشغيل الشيفرة البرمجية. يتحقق Puppeteer مما إذا كانت الميزات تعمل بالفعل من خلال اختبار ما يراه المستخدمون على الشاشة.

لا تقتصر هذه الأنماط على تطوير البرمجيات. يمكن الاستفادة من البحث العلمي والنمذجة المالية ومراجعة المستندات القانونية - أي مهمة تمتد على عدة جلسات وتتطلب عمليات تسليم موثوقة.

المبادئ الأساسية:

  • استخدم أداة تهيئة لتقسيم العمل إلى أجزاء يمكن التحقق منها

  • تتبع التقدم بتنسيق منظم وقابل للقراءة آليًا

  • تخزين الخبرة في قاعدة بيانات متجهة للاسترجاع الدلالي

  • التحقق من الإكمال باختبارات واقعية، وليس فقط اختبارات الوحدة

  • تصميم حدود واضحة للجلسات بحيث يمكن إيقاف العمل واستئنافه بأمان

الأدوات موجودة. تم إثبات الأنماط. ما تبقى هو تطبيقها.

هل أنت مستعد للبدء؟

هل لديك أسئلة أو تريد مشاركة ما تقوم ببنائه؟

    Try Managed Milvus for Free

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

    Get Started

    Like the article? Spread the word

    استمر في القراءة