Главная / Статьи

Файловая система вашего агента — это решение: объяснение бэкендов Deep Agents

Бэкенды LangChain Deep Agents определяют, какие файлы сохраняются: пять встроенных вариантов, механизм маршрутизации CompositeBackend, а также проблема пространства имён в многопользовательских хранилищах и песочницах.

Издатель: REACTAPP.TOP9 мин чтения2174 слов

Источники: LangChain Deep Agents: Your Agent’s Filesystem Is a Decision. Most People Make It by Accident. IV

Самый страшный баг агента — это не сбой. Он тихо забывает прошлое, выдаёт чужую память или записывает ценные знания в временное пространство, которое вы вовсе не собирались терять. В Deep Agents от LangChain местом, где определяются такие исходы, является бэкенд, и цель здесь — перестать рассматривать файловую систему как нечто волшебное и осознанно решать, что сохранится, что исчезнет и кто сможет это увидеть.

Каждый агент Deep Agents имеет собственную файловую систему, но место её хранения полностью зависит от вас: в памяти, на диске, в долговечном хранилище, охватывающем несколько потоков, или в комбинации этих вариантов. Неправильный выбор приводит к тому, что агент тихо забывает всё, что происходило между разговорами.

Баг, стоящий за исчезновением записей

Предположим, вы создали агента с включёнными инструментами файловой системы и увидели, как он сохраняет заметки в файл /memories/findings.md прямо во время чата. Позже в том же разговоре он снова их читает. Всё в порядке, поэтому вы выпускаете продукт. На следующий день возвращается пользователь и упоминает что-то, что, по предположению, агент должен был знать вчера, но агент ничего не помнит. Заметки исчезли. Никаких ошибок, никаких записей в журнале. Агент просто начал всё сначала, но об этом не сообщил.

Никто не пишет такой баг намеренно. Он возникает из-за стандартной настройки: файлы агента хранятся во временном пространстве, связанном с конкретным потоком, которое исчезает вместе с разговором. Если пропустить решение о месте хранения файлов, фреймворк сделает это за вас. Для временных заметок это подходит, но для хранения информации в памяти — это неправильный выбор.

Разрешения, память, сандбоксы и процесс развертывания — всё это зависит от этого одного выбора. Если бэкенд выбран правильно, всё остальное налаживается само собой, а при неправильном выборе приходится целые дни искать пропавшие данные.

Файловая система является виртуальной, а бэкенд определяет её структуру

Агент всегда видит обычную файловую систему с обычными путями. Бэкенд определяет, из чего состоит эта файловая система, и вы можете менять бэкенды без изменения поведения агента.

Предоставлено шесть инструментов для работы с файловой системой: ls, read_file, write_file, edit_file, glob и grep. Для модели они работают с обычной файловой системой. Модель записывает файл /workspace/plan.md, позже его читает и выполняет поиск в каталоге /repo, как будто у неё есть диск, причём всё это не меняется независимо от того, какой бэкенд используется в основе.

Бэкенд — это реализация, стоящая за этой абстракцией. Он отвечает на вопросы, о которых агент даже не задумывается. Если модель записывает /workspace/plan.md, куда исчезают эти байты? В память Python, которая уничтожается при завершении процесса? На реальный диск? В базу данных, сохраняющуюся на протяжении всех будущих разговоров пользователя? Каждый ответ соответствует определённому бэкенду.

Выбор осуществляется путём передачи бэкенда в функцию create_deep_agent(model=..., backend=...). Если ничего не передать, будет использоваться значение по умолчанию, что объясняет исчезновение записей выше.

Совет, чтобы ваш код соответствовал текущим версиям: предоставьте агенту готовый экземпляр, например StateBackend(). В более старых примерах использовался обёртка lambda rt:, которая теперь устарела и не нужна.

Пять бэкендов, отсортированных по тому, что сохраняется

Простой способ разделить пять встроенных решений — это опираться на срок их использования, а не на внутреннюю структуру: как долго остается файл, созданный с помощью каждого из них?

StateBackend является стандартным вариантом и представляет собой рабочую область, ограниченную одним потоком. Файлы хранятся в состоянии агента и сохраняются на протяжении всего диалога (одного потока), но не делятся между потоками. Это своего рода блокнот агента, идеальный для временных записей, которые снова читаются через два шага и удаляются по окончании разговора. Это подходящий стандартный вариант, но именно этот бэкенд приводит к неожиданному исчезновению записей, если его принимают за память.

StoreBackend обеспечивает надежное хранение данных между потоками. Файлы сохраняются в хранилище LangGraph и остаются доступными в течение нескольких разговоров, что и требуется для долгосрочной памяти. Однако существует серьезный недостаток: необходимо явно указать, чьи данные хранятся. Об этом речь пойдет ниже.

FilesystemBackend и LocalShellBackend используют реальный диск компьютера-хоста. Они читают и записывают файлы в указанной вами папке, причем LocalShellBackend также предоставляет агенту инструмент execute, позволяющий выполнять команды оболочки непосредственно на вашем компьютере без использования сандбокса. Такие решения подходят для локального помощника-программиста, разрабатываемого у вас за рабочим столом, но опасны в любых других ситуациях; именно поэтому в документации прямо предупреждается о недопустимости их использования в развернутых агентах. Прямой доступ к хост-системе в сочетании с динамическими данными от пользователя — это повод для возникновения инцидентов безопасности.

ContextHubBackend — это встроенный в LangSmith вариант долговременного хранения данных. Файлы хранятся в репозитории LangSmith Hub и сохраняются в виде коммитов, что обеспечивает сохранение информации между запусками и фиксацию каждого изменения в файловой системе, при этом не требуется создавать собственный хранилище. Этот вариант уместен, когда вы уже работаете в экосистеме LangSmith и хотите сохранять данные без настройки базы данных.

Если рассматривать все варианты вместе, они располагаются вдоль одной оси: ничего не сохраняется, сохраняется только контекст диалога, сохраняется всё, или сохраняется всё вместе с историей изменений. Выберите вариант, необходимый для ваших функций.

CompositeBackend: разные пути, разная продолжительность существования

Идея, благодаря которой бэкенды работают эффективно, заключается в том, что редко бывает необходима одна и та же политика сохранения для всей файловой системы. Рабочие файлы должны создаваться быстро, стоить недорого и быть временными, в то время как данные в памяти должны сохраняться долго. Это два разных типа бэкендов, и CompositeBackend позволяет объединить их в одной файловой системе путем маршрутизации по префиксам путей.

Укажите для большинства путей один стандартный бэкенд, а также таблицу маршрутизации, которая перенаправляет выбранные префиксы. Обычная структура предусматривает использование временного хранилища для всего, а постоянное хранилище находится за путем /memories/:

from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langgraph.store.memory import InMemoryStore

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    backend=CompositeBackend(  # ①
        default=StateBackend(),  # ②
        routes={  # ③
            "/memories/": StoreBackend(namespace=lambda _rt: ("memories",)),  # ④
        },
    ),
    store=InMemoryStore(),  # passed to create_deep_agent, not to the backend  ⑤
)

Рассмотрим пункты из этого списка:

  1. CompositeBackend предоставляет агенту одну файловую систему и перенаправляет каждую операцию к под-бэкенду, маршрут которого совпадает с указанным путем.
  2. default используется для тех случаев, когда ни один маршрут не соответствует запросу. В данном примере это потоково-ограниченный StateBackend.
  3. routes связывает префиксы путей с бэкендами. Если совпадают несколько префиксов, применяется более длинный, поэтому /memories/projects/ имеет приоритет перед /memories/.
  4. Записи в папке /memories/ сохраняются в надежном StoreBackend, где функция namespace возвращает кортеж, определяющий владельца и область применения данных.
  5. Объект store LangGraph передается функции create_deep_agent, а не бэкенду — это деталь архитектуры, которую часто упускают при первом ознакомлении.

В результате всё, что записывается под путем /memories/, сохраняется в постоянном хранилище и остаётся доступным во время последующих разговоров, тогда как всё остальное — будь то план в папке /workspace, временный файл или кэшированная информация — попадает в стандартный StateBackend и исчезает по окончании работы потока. Агент ни о чём этом не знает и ему всё равно; он просто записывает пути.

Стратегия файловой системы для настоящего агента

Возьмём, к примеру, агента для обслуживания кода под названием buggy-shop. Когда он работает с репозиторием, файлы репозитория представляют собой рабочее состояние. Их загружают, редактируют и тестируют, и нет причин, по которым они должны попасть в следующий разговор. То, что агент усваивает в процессе работы, — это уже другой вопрос, и именно это он должен запомнить при следующем запросе: нестабильные тесты в одном модуле, фикстуры, используемые несколькими тестовыми наборами, ранее обнаруженная ошибка пагинации типа «ошибка на единицу».

Таким образом, buggy-shop использует описанную выше комбинированную структуру. Репозиторий хранится в рамках стандартного StateBackend в качестве временных данных, доступных только для текущей нити, а собранные заметки находятся в папке /memories/ в постоянном StoreBackend. Это один агент с четким решением относительно того, что должно сохраняться после завершения диалога. Разделение временного рабочего состояния и постоянных знаний — ключевой подход, а маршрутизация по префиксам путей — способ ее реализации.

Где выполняется код и где хранятся файлы

До сих пор речь шла исключительно о файлах, и многим агентам они вообще не нужны. Однако агенту, задачей которого является проверка исправления с помощью pytest, требуется оболочка, поскольку выполнение команд — это не операция файловой системы. Бэкенд в виде песочницы обеспечивает это: помимо файловой системы он предоставляет инструмент execute для выполнения команд, причем все происходит в изолированном контейнере, чтобы агент не мог повредить хост-систему.

Агенты могут быть подключены к песочнице двумя способами. Первый заключается в помещении агента внутрь песочницы, где он работает с её файловой системой. Это позволяет сделать локальную разработку максимально похожей на производственную среду, но вынуждает хранить ключи API внутри песочницы. Второй способ рассматривает песочницу как инструмент: агент остаётся на вашем сервере и обращается к песочнице только для выполнения кода. Учётные данные остаются снаружи, песочницу можно удалять, а при её сбое состояние агента остаётся неповреждённым. Deep Agents поддерживает оба подхода, что является явным противопоставлением инструментам, которые разрешают только первый вариант.

Имейте в виду, что выполнение кода и хранение файлов — это два разных вопроса. Файловая система решает один из них, песочница — другой, и многим серьёзным агентам нужны оба решения.

Настройки, которые нельзя пропустить

Как только StoreBackend начинает обслуживать более одного пользователя, требуется фабрика пространств имён. Если её нет, произойдёт не сбой, но появится утечка памяти. Бэкенд должен знать, чьи файлы он хранит, и он узнаёт это из пространства имён — небольшой функции, которая принимает момент выполнения и возвращает кортеж с именем владельца. Разделение по пользователям обеспечивает каждому отдельное хранилище:

from deepagents.backends import StoreBackend
backend = StoreBackend(
    namespace=lambda rt: (rt.server_info.user.identity,),
)

Если не указывать пространство имён и использовать устаревшие стандартные диапазоны, помощник будет действовать автоматически, в результате чего все пользователи одного агента будут делиться одним и тем же набором хранилища и смогут читать предположительно приватную память друг друга.

Эта ошибка проходит все тесты на вашем собственном компьютере, поскольку вы единственный пользователь. Она проявляется как проблема с конфиденциальностью в тот день, когда входит в систему второй человек. Считайте использование механизма формирования пространства имён обязательным для любых проектов, кроме локальных утилит с одним пользователем. В документации указано, что это становится обязательным, поэтому напишите код сейчас, чтобы позже не заниматься миграцией.

По умолчанию также предусмотрена более простая система хранения. StateBackend сохраняет полное состояние агента на каждом шаге, что позволяет без проблем возобновлять диалог. Однако из-за этого большие файлы, хранящиеся в состоянии, сериализуются снова и снова на каждом шаге, что тихо снижает производительность и занимает дополнительное пространство. Решением является тот же композитный подход: большие или долговечные данные следует сохранять в специальном хранилище, а StateBackend использовать только для небольших, временных данных.

Чек-лист на сегодня

  • Узнайте, какой бэкенд использует ваш агент. Если при вызове функции create_deep_agent не указан аргумент backend=, используется StateBackend, и агент забывает всё между диалогами. Убедитесь, что это именно то, что вы хотели.
  • Определите, что должно сохраняться после завершения разговора. Отдельно перечислите рабочие файлы и долговечные знания. Все, что находится в колонке «долговечные», должно храниться в StoreBackend.
  • Используйте шаблон композиции. Создайте CompositeBackend, который по умолчанию использует StateBackend, а путь /memories/ направляет в StoreBackend. Сам объект store передается в функцию create_deep_agent, а не в бэкенд.
  • Для каждого многопользовательского StoreBackend создайте фабрику пространств имён. Свяжите её с идентификатором пользователя и сделайте это до того, как войдёт второй пользователь.
  • Проверяйте крупные файлы в состоянии. Если агент создаёт большие файлы, направляйте их вне StateBackend, чтобы они не сериализовывались заново на каждом шаге.
  • Бэкенд становится частью принятия решений

    Рассматривайте бэкенд как вариант выбора, и вы с самого начала избежите проблемы перекусов днем. Вы можете описать пять встроенных решений по их сроку службы, отправлять разные данные в зависимости от срока службы с помощью CompositeBackend, и понимать, что многопользовательское хранилище не может обойтись без фабрики пространств имён. Агент, спроектированный таким образом, имеет чёткий план для своих файлов: временное хранилище для рабочего репозитория и постоянная память для того, что он сохраняет.

    Возможность долгосрочного хранения данных, когда агент записывает их в собственную память, должна заставить вас задуматься, и это правильно. Агент, способный записывать файлы, может записать неверный файл, удалить правильный или выполнить команду, которой вы совсем не хотели. Файловая система — это как руки для такого агента. Далее идет «поводок», то есть декларативные права доступа, ограничивающие файлы, к которым он может обращаться, а также утверждение человека, заставляющее агента сначала спросить разрешения. «Поводок» будет полезен только в том случае, если вы заранее и намеренно определили место хранения файлов.