Почему Clawdbot стал вирусным - и как создать готовые к производству долго работающие агенты с помощью LangGraph и Milvus
Clawdbot (теперь OpenClaw) стал вирусным
Clawdbot, теперь переименованный в OpenClaw, на прошлой неделе захватил интернет. ИИ-ассистент с открытым исходным кодом, созданный Питером Стейнбергером, набрал 110 000+ звезд на GitHub всего за несколько дней. Пользователи публиковали видео, на которых он автономно регистрирует их на рейсы, управляет электронной почтой и контролирует устройства "умного дома". Андрей Карпати, инженер-основатель OpenAI, похвалил его. Дэвид Сакс, основатель и инвестор Tech, написал о нем в Твиттере. Люди называли его "Джарвис, но настоящий".
Затем последовали предупреждения о безопасности.
Исследователи обнаружили сотни открытых панелей администратора. По умолчанию бот работает с root-доступом. Песочница отсутствует. Уязвимости в инъекциях могут позволить злоумышленникам захватить агента. Кошмар безопасности.
Clawdbot стал вирусным не просто так.
Clawdbot стал вирусным не просто так. Он работает локально или на вашем собственном сервере. Он подключается к приложениям для обмена сообщениями, которые люди уже используют - WhatsApp, Slack, Telegram, iMessage. Он запоминает контекст на протяжении долгого времени, вместо того чтобы забывать все после каждого ответа. Он управляет календарями, обобщает электронные письма и автоматизирует задачи в разных приложениях.
У пользователей создается ощущение, что это персональный ИИ, не требующий внимания, а просто инструмент подсказки и ответа. Модель с открытым исходным кодом и самостоятельным хостингом привлекает разработчиков, которые хотят контролировать и настраивать систему. А простота интеграции с существующими рабочими процессами делает его удобным для распространения и рекомендации.
Две проблемы при создании долго работающих агентов
Популярность Clawdbot доказывает, что людям нужен ИИ, который действует, а не просто отвечает. Но любой агент, работающий в течение длительного времени и выполняющий реальные задачи - будь то Clawdbot или что-то, что вы создадите сами, - должен решить две технические проблемы: памяти и верификации.
Проблема памяти проявляется по-разному:
Агенты исчерпывают свое контекстное окно в середине задачи и оставляют незаконченную работу.
Они теряют из виду весь список задач и слишком рано объявляют "готово".
Они не могут передавать контекст между сессиями, поэтому каждая новая сессия начинается с нуля.
Все эти проблемы имеют один и тот же корень: у агентов нет постоянной памяти. Контекстные окна конечны, межсессионный поиск ограничен, а прогресс не отслеживается в доступном для агентов виде.
Проблема верификации заключается в другом. Даже когда память работает, агенты все равно помечают задачи как выполненные после быстрого юнит-теста, не проверяя, действительно ли функция работает из конца в конец.
Clawdbot решает обе проблемы. Он хранит память локально во всех сессиях и использует модульные "навыки" для автоматизации работы с браузерами, файлами и внешними сервисами. Подход работает. Но он не подходит для производства. Для корпоративного использования вам нужна структура, возможность аудита и безопасность, которые Clawdbot не обеспечивает из коробки.
В этой статье рассматриваются те же проблемы, что и в готовых к производству решениях.
Для запоминания мы используем двухагентную архитектуру, основанную на исследованиях Anthropic: агент-инициализатор, который разбивает проекты на проверяемые функции, и агент-кодировщик, который работает над ними по очереди с чистыми передачами. Для запоминания семантики по сессиям мы используем Milvus, векторную базу данных, которая позволяет агентам искать по смыслу, а не по ключевым словам.
Для проверки мы используем автоматизацию браузера. Вместо того чтобы доверять модульным тестам, агент тестирует функции так, как это сделал бы реальный пользователь.
Мы расскажем о концепциях, а затем покажем рабочую реализацию с использованием LangGraph и Milvus.
Как двухагентная архитектура предотвращает исчерпание контекста
У каждого LLM есть контекстное окно: предел того, сколько текста он может обработать за один раз. Когда агент работает над сложной задачей, это окно заполняется кодом, сообщениями об ошибках, историей разговоров и документацией. Как только окно заполняется, агент либо останавливается, либо начинает забывать предыдущий контекст. Для длительных задач это неизбежно.
Рассмотрим агента, получившего простой запрос: "Создать клон claude.ai". Проект требует аутентификации, чат-интерфейса, истории разговоров, потоковых ответов и десятков других функций. Один агент попытается справиться со всем сразу. На полпути к реализации интерфейса чата контекстное окно заполняется. Сессия заканчивается с наполовину написанным кодом, без документации о том, что было сделано, и без указаний на то, что работает, а что нет. Следующая сессия наследует беспорядок. Даже при уплотнении контекста новому агенту приходится догадываться, чем занимался предыдущий сеанс, отлаживать код, который он не писал, и выяснять, где можно возобновить работу. Час проходит впустую, прежде чем будет достигнут хоть какой-то новый прогресс.
Решение с двумя агентами
Решение Anthropic, описанное в инженерном посте "Эффективные харнессы для долго работающих агентов", заключается в использовании двух различных режимов подсказок: подсказка-инициализатор для первой сессии и подсказка-код для последующих сессий.
Технически оба режима используют один и тот же базовый агент, системные подсказки, инструменты и харнес. Единственное различие - это начальная подсказка для пользователя. Но поскольку они выполняют разные роли, полезно рассматривать их как два отдельных агента. Мы называем это двухагентной архитектурой.
Инициализатор создает среду для постепенного прогресса. Он принимает расплывчатый запрос и делает три вещи:
Разбивает проект на конкретные, поддающиеся проверке функции. Не расплывчатые требования вроде "сделать интерфейс чата", а конкретные, проверяемые шаги: "пользователь нажимает кнопку "Новый чат" → новая беседа появляется в боковой панели → область чата показывает состояние приветствия". Пример клона claude.ai от Anthropic содержит более 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 отслеживает завершение. Инструкции с жесткими формулировками вроде "Недопустимо удалять или редактировать тесты, так как это может привести к отсутствию или ошибкам в функциональности" также рекомендуются, чтобы агент не смог обмануть систему, удалив сложные функции.
Как Milvus обеспечивает агентам семантическую память на все сеансы
Двухагентная архитектура решает проблему исчерпания контекста, но не решает проблему забывания. Даже при чистом переходе от одной сессии к другой агент теряет память о том, чему он научился. Он не может вспомнить, что "маркеры обновления JWT" относятся к "аутентификации пользователей", если только эти слова не появятся в файле выполнения. По мере роста проекта поиск в сотнях git-коммитов становится медленным. При подборе ключевых слов упускаются связи, которые были бы очевидны для человека.
Именно здесь на помощь приходят векторные базы данных. Вместо того чтобы хранить текст и искать по ключевым словам, векторная база данных преобразует текст в числовые представления смысла. При поиске по запросу "аутентификация пользователей" в ней будут найдены записи о "маркерах обновления JWT" и "обработке сеансов входа". Не потому, что слова совпадают, а потому, что эти понятия семантически близки. Агент может спросить: "Видел ли я что-то подобное раньше?" и получить полезный ответ.
На практике это работает за счет встраивания записей о прогрессе и коммитов git в базу данных в виде векторов. Когда начинается сессия кодирования, агент запрашивает базу данных с текущей задачей. База данных за миллисекунды возвращает соответствующую историю: что уже пытались сделать, что получилось, что не получилось. Агент не начинает с нуля. Он начинает с контекста.
Milvus хорошо подходит для этого случая использования. Он имеет открытый исходный код и предназначен для векторного поиска в производственных масштабах, обрабатывая миллиарды векторов без особых усилий. Для небольших проектов или локальной разработки Milvus Lite может быть встроен непосредственно в приложение, например SQLite. Настройка кластера не требуется. Когда проект вырастет, вы сможете перейти на распределенный Milvus без изменения кода. Для генерации вкраплений вы можете использовать внешние модели, такие как SentenceTransformer, для более тонкого контроля или ссылаться на встроенные функции вкраплений для более простых настроек. Milvus также поддерживает гибридный поиск, сочетающий векторное сходство с традиционной фильтрацией, так что вы можете запросить "найти похожие проблемы аутентификации за последнюю неделю" за один вызов.
Это также решает проблему переноса. Векторная база данных сохраняется вне отдельной сессии, поэтому знания накапливаются со временем. На сеансе 50 можно получить доступ ко всему, что было изучено на сеансах с 1 по 49. Проект развивает институциональную память.
Проверка завершенности с помощью автоматизированного тестирования
Даже при двухагентной архитектуре и долговременной памяти агенты могут объявить о победе слишком рано. Это и есть проблема верификации.
Вот распространенный вариант отказа: Сессия кодинга завершает работу над функцией, запускает быстрый модульный тест, видит, что он прошел, и переключает "passes": false на "passes": true. Но прохождение модульного теста не означает, что функция действительно работает. API может возвращать корректные данные, а пользовательский интерфейс ничего не отображает из-за ошибки CSS. В файле выполнения написано "завершено", а пользователи ничего не видят.
Решение заключается в том, чтобы заставить агента тестировать как реальный пользователь. Каждая функция в списке функций имеет конкретные шаги проверки: "пользователь нажимает кнопку "Новый чат" → новая беседа появляется в боковой панели → область чата показывает состояние приветствия". Агент должен проверить эти шаги буквально. Вместо того чтобы выполнять только тесты на уровне кода, он использует инструменты автоматизации браузера, такие как Puppeteer, для имитации реального использования. Он открывает страницу, нажимает кнопки, заполняет формы и проверяет, появляются ли на экране нужные элементы. Только когда весь поток проходит, агент отмечает функцию как завершенную.
Это позволяет выявить проблемы, которые не замечают модульные тесты. Функция чата может иметь идеальную логику бэкенда и правильные ответы API. Но если фронтенд не отображает ответ, пользователи ничего не видят. Автоматизация браузера может сделать скриншот и проверить, соответствует ли то, что появляется на экране, тому, что должно появиться. Поле passes становится true только тогда, когда функция действительно работает из конца в конец.
Однако здесь есть свои ограничения. Некоторые функции, присущие браузерам, не могут быть автоматизированы с помощью таких инструментов, как Puppeteer. В качестве примера можно привести подборщики файлов и диалоги подтверждения системы. В Anthropic отметили, что функции, полагающиеся на модальные оповещения, как правило, имеют больше ошибок, поскольку агент не может увидеть их через Puppeteer. Практическим выходом из ситуации является проектирование с учетом этих ограничений. По возможности используйте пользовательские компоненты пользовательского интерфейса вместо нативных диалогов, чтобы агент мог протестировать каждый шаг проверки в списке функций.
Собираем все вместе: LangGraph для состояния сеанса, Milvus для долговременной памяти
Концепции, описанные выше, объединяются в рабочую систему с помощью двух инструментов: LangGraph для состояния сеанса и Milvus для долговременной памяти. LangGraph управляет тем, что происходит в рамках одной сессии: над какой функцией ведется работа, что завершено, что дальше. Milvus хранит историю с возможностью поиска по всем сессиям: что было сделано ранее, какие проблемы возникли и какие решения сработали. Вместе они дают агентам как кратковременную, так и долговременную память.
Замечание по реализации: Приведенный ниже код - это упрощенная демонстрация. Он показывает основные паттерны в одном сценарии, но не полностью повторяет разделение сессий, описанное ранее. В производственной установке каждый сеанс кодирования будет отдельным вызовом, возможно, на разных машинах или в разное время. В LangGraph для этого используются MemorySaver и thread_id, сохраняющие состояние между вызовами. Чтобы наглядно увидеть поведение возобновления, запустите скрипт один раз, остановите его, а затем запустите снова с тем же thread_id. Второй запуск продолжит работу с того места, на котором остановился первый.
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.
Заключение
ИИ-агенты не справляются с длительными задачами, потому что им не хватает постоянной памяти и надлежащей проверки. Clawdbot получил широкую известность благодаря решению этих проблем, но его подход не подходит для производства.
В этой статье мы рассмотрели три решения:
Двухагентная архитектура: Инициализатор разбивает проекты на проверяемые функции; агент кодирования работает над ними по очереди с чистыми передачами. Это предотвращает истощение контекста и позволяет отслеживать прогресс.
Векторная база данных для семантической памяти: Milvus хранит записи о прогрессе и коммиты git в виде вкраплений, поэтому агенты могут искать по смыслу, а не по ключевым словам. Сессия 50 запоминает то, что узнала сессия 1.
Автоматизация браузера для реальной проверки: Юнит-тесты проверяют, работает ли код. Puppeteer проверяет, работают ли функции на самом деле, тестируя то, что пользователи видят на экране.
Эти паттерны не ограничиваются разработкой программного обеспечения. Научные исследования, финансовое моделирование, анализ юридических документов - любая задача, которая охватывает несколько сессий и требует надежной передачи данных, может принести пользу.
Основные принципы:
Используйте инициализатор, чтобы разбить работу на поддающиеся проверке фрагменты.
Отслеживайте прогресс в структурированном, машиночитаемом формате
Хранить опыт в векторной базе данных для семантического поиска
Проверяйте завершение работы с помощью реальных тестов, а не только юнит-тестов.
Создавайте четкие границы сессий, чтобы работа могла безопасно приостанавливаться и возобновляться.
Инструменты существуют. Паттерны проверены. Осталось только применить их.
Готовы приступить к работе?
Изучите Milvus и Milvus Lite для добавления семантической памяти в ваши агенты.
Ознакомьтесь с LangGraph для управления состоянием сессии
Ознакомьтесь с полным исследованием Anthropic, посвященным долговременной работе агентов.
У вас есть вопросы или вы хотите поделиться тем, что вы создаете?
Присоединяйтесь к сообществу Milvus Slack, чтобы общаться с другими разработчиками.
Посещайте Milvus Office Hours для живых вопросов и ответов с командой
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



