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

Запуск агентів Running Deep у локальному сервері OpenSandbox

Як під’єднати OpenSandbox до Deep Agents через BaseSandbox, синхронізувати та асинхронізувати код, а також безпечно запустити агента для аналізу даних у локальному Docker-сандбоксі.

Видавець: 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 — це безкоштовний, відкритий сандбокс-рантайм, який працює на вашому власному 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 не має автентифікації, тому функція create() тепер генерує попередження InsecureSandboxWarning, якщо не вказано api_key, тоді як раніше вона підключалася без жодних проблем.
    • Рішення для 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 стабілізували відповіді агента та усунули помилку GMV на рівні 29%.
  • Порівняння CrewAI, AutoGen, LangGraph, OpenAI Agents SDK та smolagents — порівняння інженера п’яти фреймворків агентів з відкритим кодом з точки зору оркестрації, пам’яті, інструментів, оцінки та можливостей спостереження, а також типів часто трапляючихся помилок.