Головна / Статті

Файлова система вашого агента — це рішення: пояснення бекендів 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. Об’єкт LangGraph store передається функції 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, а також розуміти, що багатокористувацьке зберігання не може існувати без фабрики імен просторів. Агент, спроєктований у такий спосіб, має план щодо своїх файлів: тимчасове зберігання для робочого репозиторію та довготривале зберігання для того, що він збирає.

    Міцне зберігання даних, у яке агент записує інформацію, має змусити вас замислитися, і це є правильним підходом. Агент, який може записувати файли, може записати неправильний файл, видалити правильний чи виконати команду, якої ви зовсім не хотіли. Файлова система — це наче руки для цього агента. Далі йде «лінець», тобто декларативні права доступу, які обмежують, до яких файлів він може отримати доступ, а також людське схвалення, яке змушує його спочатку запитати дозволу. «Лінець» допомагає лише тоді, коли ви вже навмисно визначили місце зберігання файлів.