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

Запуск агентов Running Deep в локальном бэкенде OpenSandbox

Как подключить OpenSandbox к Deep Agents через BaseSandbox, синхронизировать синхронный и асинхронный код, а также безопасно запустить агента для анализа данных в локальной среде Docker Sandbox.

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

Источники: Upgrade Your Deep Agent With a Local Open-Source Sandbox

Почему агентам нужно безопасное место для запуска кода

Чат-ассистенты вроде ChatGPT, Claude, Gemini и Qwen принимают загрузки в виде изображений, PDF-файлов и таблиц. Многие запросы к этим файлам — например, описание картинки, краткое содержание PDF или извлечение фрагмента из документа — находятся в рамках возможностей языковой модели. Однако если передать ей файл .csv и попросить провести полный статистический анализ с графиками, простого логического анализа уже недостаточно. Для этого требуется реальная обработка данных, вычисления и отрисовка графиков.

Языковая модель сама не может выполнять эти математические операции или рисовать, но она может написать Python-скрипт, использующий pandas, NumPy и Matplotlib для выполнения этих задач. После запуска скрипта задачей модели становится преобразование полученных результатов в читаемый отчет. Именно инструменты агентов позволяют выполнять такие скрипты.

Однако запуск кода, написанного с помощью ИИ, непосредственно на собственном компьютере — плохая идея. Запуск без надзора представляет серьезную угрозу для безопасности и стабильности: он может изменить конфигурацию системы, удалить важные файлы или обнаружить личные данные. Поэтому существуют песочницы — защищенные изолированные среды, в которых агент может запускать свой код, не имея доступа к операционной системе. В данном материале рассматриваются песочницы для библиотеки Deep Agents Python.

Что такое песочница и почему агентам она нужна

Здесь понятие «песочница» означает изолированную среду, обычно контейнер, в котором код выполняется без какого-либо контакта с остальной частью машины. У него собственная файловая система и процессы, и ничто не связывает его с файлами, учетными данными или сетью хоста, если только специально не предоставлен доступ. Разработчики могут рассматривать это как удобство, но для автономного агента это практически обязательно, и причины этого кроются в практических аспектах:

  • Код выбирает агент, а не пользователь. Даже хорошо поведующийся агент иногда может удалить неверный файл или выполнить команду, которой никто не желал, и без изоляции ущерб отразится на реальной машине.
  • Внедрение промптов — реальная угроза. Если агент читает ненадежный контент, такой как веб-страница или файл от кого-то другого, хитро сформулированная скрытая инструкция может попытаться изменить его дальнейшие действия. Песочница ограничивает ущерб рамками временного контейнера.
  • Чистые начальные условия помогают. Каждая задача проще анализировать, когда она начинается в известной, пустой среде и не наследует состояние от предыдущих запусков.
  • Параллельные агенты. Благодаря изоляции команда pip install одного агента не может столкнуться с аналогичной командой другого, а сбой в песочнице не повлияет на весь процесс.
  • Ограничения ресурсов. Агент, застрявший в бесконечном цикле, или тот, который дважды загружает целый набор данных в память, должен исчерпывать ресурсы временного контейнера, а не ресурсы вашего ПК.
  • Именно поэтому фреймворки, предназначенные для программирования агентов, включая Deep Agents, рассматривают песочницу как концепцию первого класса. Deep Agents поставляет абстракцию под названием BaseSandbox, а также официальные интеграции с несколькими облачными поставщиками песочниц. Если направить агента к одному из них, у него появляется реальное, изолированное место для запуска кода.

    Все эти поставщики работают в облаке и взимают плату за минуту работы или за объем использования. Это подходит для производственных целей, но является избыточным для локальной разработки, тестирования или демонстрации агента.

    OpenSandbox как локальная альтернатива

    OpenSandbox — это бесплатная, open-source среда выполнения песочницы, работающая на вашем собственном Docker. Она предлагает те же основные возможности, что и платные варианты:

    • Запуск изолированного контейнера.
    • Выполнение команд внутри него.
    • Чтение и запись файлов там.
    • Удаление контейнера после завершения работы.

    Запустите сервер управляющего слоя локально, настроив его на Docker, и появится локальный конечный пункт, который создает и управляет сандбоксами по требованию. Его никто не продает в виде хостингового продукта. Это инфраструктура, которую вы управляете сами, и она подходит для локальной разработки, тестирования и демонстраций вроде этой. Остаётся только подключить его к Deep Agents, в котором отсутствует интеграция с OpenSandbox.

    Контракт BaseSandbox

    BaseSandbox — это абстрактный класс, и интеграция сводится к четырём его элементам.

    class BaseSandbox(ABC):
        def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse: ...
        @property
        def id(self) -> str: ...
        def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]: ...
        def download_files(self, paths: list[str]) -> list[FileDownloadResponse]: ...
    
    1. execute запускает команду в сандбоксе и возвращает объект ExecuteResponse.
    2. id — уникальный идентификатор для бэкенда сандбокса.
    3. upload_files отправляет несколько файлов в сандбокс.
    4. download_files извлекает несколько файлов из сандбокса.

    Как только появляются эти четыре компонента, Deep Agents самостоятельно создаёт функции ls, read_file, write_file, edit_file, glob и grep на основе execute(). Для них больше не требуется писать код. Это полная интеграционная среда, что означает, что любой сандбокс, способный выполнять команды оболочки и перемещать файлы, подходит для использования. На первый взгляд всё кажется простым, но при реальной разработке появляются ошибки.

    Как работает OpenSandbox

    OpenSandbox использует контейнеры Docker для создания и управления сандбоксами. Его Python SDK позволяет создавать и управлять средами сандбоксов, выполнять команды оболочки, обрабатывать файлы и отслеживать ресурсы. Для настройки необходимо, чтобы Docker был установлен и работал, а также сам сервер:

    # Generate a starter config
    uvx opensandbox-server init-config ~/.sandbox.toml --example docker
    
    # Start the server
    uvx opensandbox-server
    

    После этого SDK подключается к работающему серверу и предоставляет доступ ко всем возможностям OpenSandbox.

    Класс интеграции

    Это скелет, который необходимо заполнить, следуя абстракции BaseSandbox. Методы добавляются по одному.

    create()

    class MinimalOpenSandboxBackend(BaseSandbox):
        def __init__(self, sandbox: Sandbox, runner: AsyncRunner):
            self._sandbox = sandbox
            self._runner = runner
    

    __init__ — это простой метод. В нём хранятся два объекта, от которых зависит всё остальное: активное соединение Sandbox с сервером и AsyncRunner, управляющий им. Ни один из этих объектов не создаётся вручную, поскольку оба формируются с помощью метода-фабрики. У класса также оказывается больше методов, чем требуется BaseSandbox — в частности, метод класса .create(). Причина становится яснее, если рассмотреть, как на самом деле создаётся объект sandbox.

    Соединение Sandbox и фоновый запускающий процесс должны существовать до того, как __init__ сможет их использовать, и именно поэтому create() представляет собой отдельный шаг. Вот соответствующий код.

    from opensandbox import Sandbox
    from opensandbox.config import ConnectionConfig
    
    IMAGE = "opensandbox/code-interpreter:v1.1.0"
    ENTRYPOINT = ["/opt/code-interpreter/code-interpreter.sh"]
    
    @classmethod
    def create(cls, api_key: str | None = None) -> "MinimalOpenSandboxBackend":
        runner = AsyncRunner()
        config = ConnectionConfig(domain="localhost:8080", api_key=api_key)
        sandbox = runner.run(
            Sandbox.create(IMAGE, entrypoint=ENTRYPOINT, connection_config=config, timeout=timedelta(minutes=30))
        )
        return cls(sandbox, runner)
    

    Выделяются два важных момента. domain="localhost:8080" связывает это с сервером, запущенным ранее с помощью команды uvx opensandbox-server. Параметр timeout=timedelta(minutes=30) служит защитным механизмом: даже если забыть вызвать kill(), сандбокс всё равно автоматически завершится, вместо того чтобы продолжать работать в фоновом режиме. Также обратите внимание на параметры IMAGE и ENTRYPOINT — они должны быть совместимы с используемой версией OpenSandbox.

    Об исполнителе: Sandbox.create — это корутину, однако BaseSandbox.create должен возвращать обычный объект, а не результат ожидания. Пока не станут известны детали, рассматривайте runner.run(coro) как «ожидайте завершения этого асинхронного вызова, затем верните мне то, что он вернул». Это объясняет, почему Sandbox.create(...) передаётся в runner.run(...), а не ожидается напрямую. То, как внутри работает AsyncRunner, будет рассмотрено чуть позже.

    id

    Процесс создания завершён, поэтому оставшиеся методы работают с уже запущенной песочницей.

    @property
    def id(self) -> str:
        return self._sandbox.id
    

    Он возвращает идентификатор основной песочницы. Класс BaseSandbox требует его в качестве свойства, а он также удобен для записи в логи.

    execute()

    from deepagents.backends.protocol import ExecuteResponse
    
    def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
        execution = self._runner.run(self._sandbox.commands.run(command))
        stdout = "\n".join(c.text for c in execution.logs.stdout)
        stderr = "\n".join(c.text for c in execution.logs.stderr)
        output = "\n".join(p for p in (stdout, stderr) if p)
        return ExecuteResponse(output=output, exit_code=execution.exit_code or 0)
    

    На первый взгляд кажется простым, но на деле это не так. OpenSandbox возвращает результаты команд в виде списка блоков, по одному на строку, причем символы новой строки уже удаляются. Поэтому ответ необходимо правильно пересоздавать, чтобы агент мог им позже воспользоваться. Функция execute() также вызывается не напрямую: функции ls, glob и grep в Deep Agents основаны на ней и считывают вывод по строкам. Если передать им некорректный ответ, они ничего не найдут и тихо вернут пустые результаты. Функция ls возвращает [], отладочная информация не указывает на ошибку в функции join, и кажется, что сандбокс пуст, хотя на самом деле это не так.

    Это важнее, чем кажется, потому что функция execute() предназначена не только для прямого использования. Она является основой для функций ls, glob и grep в Deep Agents, причем все они обрабатывают вывод по одной строке за раз. Плохо сформатированный ответ приводит к тому, что они не находят ничего подходящего и молча возвращают пустые результаты. В таком случае функция ls возвращает [] без информации о причине ошибки, связанной с функцией join, в результате чего сандбокс кажется пустым, хотя на самом деле он не пуст.

    Остальное проще: стандартный вывод (stdout) и ошибочный вывод (stderr) объединяются, пропуская пустые значения, а отсутствие кода завершения считается успехом (0), вместо того чтобы оставаться None.

    upload_files()

    from opensandbox.models import WriteEntry
    from deepagents.backends.protocol import FileUploadResponse
    
    def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
        entries = [WriteEntry(path=path, data=data, mode=644) for path, data in files]
        try:
            self._runner.run(self._sandbox.files.write_files(entries))
            return [FileUploadResponse(path=p) for p, _ in files]
        except Exception as exc:
            return [FileUploadResponse(path=p, error=str(exc)) for p, _ in files]
    

    Каждая пара (путь, байты) оборачивается в объект WriteEntry с обычными правами доступа (владелец может читать и писать, остальные могут только читать), и вся группа данных отправляется с помощью одного вызова write_files().

    download_files()

    from deepagents.backends.protocol import FileDownloadResponse
    
    def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
        results = []
        for path in paths:
            try:
                content = self._runner.run(self._sandbox.files.read_bytes(path))
                results.append(FileDownloadResponse(path=path, content=content))
            except Exception as exc:
                results.append(FileDownloadResponse(path=path, error=str(exc)))
        return results
    

    Важным моментом является использование read_bytes вместо read_file. SDK предоставляет оба варианта, но read_file предполагает наличие текста в формате UTF-8. Сандбокс, запускающий код LLM, не может определить, является ли запросимый файл CSV или только что отрендеренной диаграммой, поэтому read_bytes — единственный безопасный вариант в обоих случаях.

    kill()

    def kill(self) -> None:
        self._runner.run(self._sandbox.kill())
        self._runner.shutdown()
    

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

    Вот и вся классификация: два метода жизненного цикла (create, kill) окружают четыре метода, требуемые BaseSandbox (id, execute, upload_files, download_files).

    Мост синхронного и асинхронного взаимодействия

    Остался один нераскрытый момент: self._runner.run(...) — элемент, который делает каждый асинхронный вызов SDK синхронным с точки зрения BaseSandbox.

    BaseSandbox является синхронным. Функции execute(), upload_files() и download_files() — это обычные вызовы функций без использования await. Однако SDK OpenSandbox асинхронный: Sandbox.create(), sandbox.commands.run() и sandbox.files.write_files() — все это корутины. Для преодоления этой разницы существует AsyncRunner.

    Подход прост. Создается один цикл событий, который передается в отдельный фоновый поток и остается активным до тех пор, пока сандбокс существует. Все остальное использует только функцию run(), которая отправляет корутину в этот цикл с помощью run_coroutine_threadsafe, блокирует код-запрос до её завершения и возвращает результат. Для вызова execute() строка self._runner.run(some_coroutine) выглядит точно как обычный вызов функции.

    Один долгоживущий цикл вместо нового с каждым вызовом позволяет соединению функционировать как единая непрерывная сессия, а не разрушаться и воссоздаваться при каждом вызове execute(). Функция shutdown() обеспечивает очистку, останавливая цикл и корректно завершая поток, поэтому kill() просто вызывает её.

    Агент анализа данных в песочнице

    Для тестирования интеграции Jupyter Notebook сначала задаёт переменные окружения и константы, импортирует пакеты, а самое главное — создаёт объект MinimalOpenSandboxBackend. Для этого требуется API-ключ, который вы сами создаёте и задаёте в файле ~/.sandbox.toml.

    import asyncio
    import nest_asyncio
    import threading
    from datetime import timedelta
    from pathlib import Path
    
    from deepagents import create_deep_agent
    from deepagents.backends.protocol import ExecuteResponse, FileDownloadResponse, FileUploadResponse
    from deepagents.backends.sandbox import BaseSandbox
    from langchain.chat_models import init_chat_model
    
    from opensandbox import Sandbox
    from opensandbox.config import ConnectionConfig
    from opensandbox.models import WriteEntry
    
    # nest_asyncio for running async functions in Jupyter.
    nest_asyncio.apply()
    
    IMAGE = "opensandbox/code-interpreter:v1.1.0"
    ENTRYPOINT = ["/opt/code-interpreter/code-interpreter.sh"]
    
    backend = MinimalOpenSandboxBackend.create(api_key="SANDBOX_API_KEY")
    print("Sandbox ready:", backend.id)
    

    Далее идет модель чата. Местные модели через Ollama тоже могут работать, но модель всё равно должна справляться с задачами агента. В данном случае используется модель Gemini Flash.

    llm = init_chat_model(
        model="gemini-3.5-flash",
        model_provider="google_genai",
        api_key=os.environ["GOOGLE_API_KEY"],
        max_tokens=14750,
        max_retries=5,
    )
    

    Затем сам агент.

    agent = create_deep_agent(
        model=llm,
        system_prompt=(
            "You are a Python coding assistant with sandbox access. "
            "You specialize in performing data analysis and data visualization with python,"
            "you generate clear reports with good looking charts using seaborn."
        ),
        backend=backend,
    )
    

    Всё, что было создано до сих пор, находит своё отражение в аргументе backend=backend. Исходя из этого единственного объекта, Deep Agents генерирует функции ls, read_file, write_file, glob и саму функцию grep, используя кастомную функцию execute() в качестве основы.

    Затем загружаются реальные данные с помощью функции upload_files(). Набор данных представляет собой фиктивный набор информации о 1 000 клиентах.

    csv_bytes = Path("customers-1000.csv").read_bytes()
    results = backend.upload_files([("/workspace/customers-1000.csv", csv_bytes)])
    for r in results:
        if r.error:
            print(f"Upload failed for {r.path}: {r.error}")
        else:
            print(f"Uploaded {r.path}")
    

    Затем агент получает само задание.

    result = agent.invoke({
        "messages": "Perform a deep exploratory data analysis on the customers-1000.csv file "
                    "and summarize the findings in a markdown report with clear charts."
    })
    

    На этом этапе агент работает самостоятельно. С помощью своих инструментов и инфраструктуры он может писать код на pandas и seaborn, запускать его в песочнице с помощью функции execute(), сохранять графики в файловой системе песочницы и продолжать работу до тех пор, пока не будет создано полное отчет в формате markdown.

    Остается одна проблема: часть отчета остается внутри песочницы. Его формат markdown содержит ссылки на графики по путям, характерным для песочницы, которые отсутствуют на хост-системе. Чтобы сделать отчет читаемым, необходимо выполнить два шага: сначала скачать изображения графиков с помощью функции download_files(), а затем переписать пути к изображениям в markdown так, чтобы они указывали на локальные копии. Более надежным подходом является возврат агентом структурированного результата — например, поля с отчетом и явного списка путей к изображениям — вместо поиска ссылок в неструктурированном тексте markdown. Последующая обработка заключается просто в скачивании всех указанных путей и их замене.

    После этого приведён результат обработки сырого CSV-файла в среде песочницы.

    # Deep Exploratory Data Analysis: Customer Acquisition and Profiling
    **Dataset:** `customers-1000.csv`
    **Analysis Period:** Jan 2020 – May 2022
    
    ---
    
    ## 1. Executive Summary
    
    This report presents a comprehensive exploratory data analysis (EDA) of a customer database containing 1,000 unique records. The analysis delves into geographical distributions, sign-up temporal trends, domain & technical alignments, and name demographics to uncover actionable insights for strategic growth.
    
    ### Key Takeaways
    1. **Unprecedented Global Reach:** The customer base is extraordinarily decentralized, spanning **240 countries** across all **7 continents** (including Antarctica). No single country represents more than 1.2% of the customer base. Africa (24.7%) and Asia (22.6%) are the leading regions, followed by Europe (18.5%) and North America (16.1%).
    2. **Stable Acquisition Trends:** Customer subscriptions are remarkably stable, averaging roughly **34-35 new customers per month** across 2020 and 2021. This consistency is maintained across all continents year-over-year, indicating a highly standardized, globally distributed customer acquisition channel.
    3. **Mid-Week and Weekend Consistency:** Subscriptions are evenly spread across the days of the week, with a minor peak on Friday and Saturday, and a minor trough on Thursday.
    4. **B2B / Synthetic Profile Characteristics:** The dataset shows zero domain overlap between customer email domains and company websites (0.0% exact match across 923 unique domains). Combined with the near 1-to-1 ratio of customers to companies, this suggests a highly B2B-centric profile (one representative per enterprise) or synthetically generated profiles with randomized fields.
    5. **Standardized TLD Footprint:** The `.com` top-level domain (TLD) dominates both emails (61.2%) and corporate websites (58.8%). The remaining distribution is evenly split among `.org`, `.net`, `.biz`, and `.info`.
    
    ---
    
    ## 2. Dataset Structure & Data Integrity
    
    The initial dataset contains **1,000 rows** and **12 columns**. An inspection of data integrity reveals excellent completeness:
    - **Zero Missing Values:** Every column is 100% populated.
    - **Zero Duplicates:** There are no duplicate rows, and the `Customer Id` column contains 1,000 unique identifiers.
    - **Data Types:** All columns are stored as object/string types except for `Index` (integer).
    
    ### Data Preprocessing & Feature Engineering
    To enable deep exploratory analysis, several features were engineered:
    1. **Temporal Features:** `Subscription Date` was parsed as a datetime object, allowing the extraction of `Sub_Year`, `Sub_Month`, `Sub_Month_Name`, `Sub_Day_of_Week`, and `Sub_Year_Month` (period).
    2. **Geographical Mapping:** Using the `pycountry` and `pycountry-convert` libraries, coupled with a manual fallback dictionary for territories, each of the 240 countries was successfully mapped to its respective **Continent**.
    3. **Domain & Technical Profiles:** Email domains (`Email_Domain`), email TLDs (`Email_TLD`), and website TLDs (`Website_TLD`) were extracted to analyze the technical profiling of users.
    
    ---
    
    ## 3. Geographical Analysis
    
    ### Continent-Level Distribution
    The geographic reach of this customer base is truly global. Rather than being concentrated in a single dominant market like North America or Europe, customers are spread across all continents:
    
    | Continent | Customer Count | Percentage |
    | :--- | :---: | :---: |
    | **Africa** | 247 | 24.7% |
    | **Asia** | 226 | 22.6% |
    | **Europe** | 185 | 18.5% |
    | **North America** | 161 | 16.1% |
    | **Oceania** | 107 | 10.7% |
    | **South America** | 54 | 5.4% |
    | **Antarctica** | 20 | 2.0% |
    
    #### Chart 1: Customer Distribution by Continent
    ![Customer Distribution by Continent](downloads/customer_by_continent.png)
    
    ### Country-Level Distribution (Top 15 Countries)
    The country-level distribution exhibits a heavy tail, with the 1,000 customers distributed across **240 distinct nations**. This indicates that the average number of customers per country is only **4.17**.
    
    The top countries by customer density are:
    - **Liechtenstein:** 12 customers (1.2%)
    - **Gabon:** 10 customers (1.0%)
    - **China, Bangladesh, Reunion, Nigeria, Luxembourg:** 9 customers each (0.9%)
    
    This extreme dispersion suggests a borderless, digital-first product that appeals universally across jurisdictions without localized geographic bias.
    
    #### Chart 2: Top 15 Countries by Customer Count
    ![Top 15 Countries by Customer Count](downloads/top_15_countries.png)
    
    ---
    
    ## 4. Temporal Analysis (Subscription Trends)
    
    ... (Trimmed to keep blog (estimated read time short))
    

    Урезанный вид отрендеренного отчета:

    От ноутбука к пакету PyPI

    Ноутбук показывает, как все взаимосвязано, но гораздо полезнее — это пакет, который любой может установить с помощью pip. Разработка deepagents_opensandbox_backend потребовала устранения всех проблем, которые возникали только тогда, когда другие люди запускали его в средах, которые автор никогда не пробовал. Внешний интерфейс остался прежним: те же методы BaseSandbox, а также те же функции create() и kill(). Изменения происходили внутри:

    • Один общий цикл. Теперь бэкенды используют единственный модульный объект _BackgroundLoop и не запускают отдельные потоки каждый по отдельности.
    • По умолчанию выводится предупреждение о безопасности. API не требует аутентификации, поэтому при отсутствии параметра api_key функция create() теперь выдает исключение InsecureSandboxWarning, в то время как раньше она подключалась без каких-либо сообщений.
    • Решение для Docker Desktop. В Docker Desktop прямое подключение к порту контейнера часто приводит к тайм-ауту. Установка параметра use_server_proxy=True направляет трафик через сервер, тем самым избегая этой проблемы.
    • Проверка пути. Функции upload_files и download_files сразу отклоняют относительные пути, поэтому сервер не может тайно преобразовать их в неверные директории.
    • Более четкие сообщения об ошибках. Клиент проверяет информацию о файле, чтобы отличить случаи «отказа в доступе» от «файл не найден», и присваивает каждому из них соответствующий код ошибки, вместо того чтобы передавать вводящие в заблуждение необработанные сообщения об ошибках.
    • Работающие таймауты. Ранее аргумент timeout игнорировался. Теперь он передается на сервер для прерывания работающих команд, вместо того чтобы они оставались без управления после того, как клиент прекращает свою деятельность.
    • Удалены ручно написанные асинхронные методы. Настоящие асинхронные версии, такие как aexecute, приводили к тому, что в объекте ToolNode LangGraph формировались безмолвные, пустые результаты, поэтому они были удалены, и теперь код использует обычные синхронные методы, выполняемые в пуле потоков.

    Эта разница отделяет демо-версию, работающую на одном ноутбуке, от пакета, готового к установке с помощью команды pip install для любого пользователя. Вместе с ним поставляется набор тестов на соответствие стандартам, запускаемый с использованием стандартных тестов в сандбоксе от самого Deep Agents, а процесс установки выглядит следующим образом:

    !pip install deepagents-opensandbox-backend
    

    Это та же самая интеграция, описанная выше, только острые края сглажены.

    Связанные статьи

  • AgentSRE: Агенты на основе Composable AI для автономного устранения инцидентов в AWS — Как платформа AWS превращает инцидент, обнаруженный с помощью аларма CloudWatch, в решённую проблему с использованием агента DevOps, агентов устранения проблем LangGraph, подтверждения от человека и тикетов ServiceNow.
  • Что схема не может сообщить вашему агенту: формат открытых знаний в BigQuery — Эксперимент с BigQuery и ADK, демонстрирующий, как пакеты знаний в формате Markdown в Git стабилизировали ответы агента и устранили ошибку в размере 29% от общего объёма продаж.
  • Сравнение CrewAI, AutoGen, LangGraph, OpenAI Agents SDK и smolagents — сравнение пяти фреймворков агентов с открытым исходным кодом с точки зрения оркестрации, работы с памятью, инструментов, методов оценки и возможностей мониторинга, а также типичных причин сбоев.