Uruchamianie agentów Running Deep w lokalnym backendzie OpenSandbox
Jak połączyć OpenSandbox z Deep Agents za pośrednictwem BaseSandbox, skonfigurować synchroniczny i asynchroniczny kod, oraz bezpiecznie uruchomić agent do analizy danych w lokalnej przestrzeni sandbox Docker.
Źródła: Upgrade Your Deep Agent With a Local Open-Source Sandbox
Dlaczego agenci potrzebują bezpiecznego miejsca do uruchamiania kodu
Asystenci czatowi takie jak ChatGPT, Claude, Gemini i Qwen przyjmują pliki w formie obrazów, PDF-ów oraz arkuszy kalkulacyjnych. Wiele zapytań dotyczących tych plików, np. opisanie obrazu, streszczenie PDF-a lub wydobycie fragmentu z dokumentu, mieści się w naturalnych możliwościach modelu językowego. Jeśli jednak podasz mu plik .csv i poprosisz o pełną analizę statystyczną wraz z wykresami, zwykłe rozumowanie już nie wystarczy – konieczna jest faktyczna manipulacja danymi, wykonywanie obliczeń oraz tworzenie wykresów.
Model językowy sam nie potrafi wykonać tych obliczeń ani narysować wykresu, ale może napisać skrypt w Pythonie, który wykorzystuje biblioteki pandas, NumPy i Matplotlib do tego celu. Po uruchomieniu skryptu zadaniem modelu jest przekształcenie uzyskanych wyników w czytelną raportację. To właśnie narzędzia typu agentic umożliwiają wykonywanie takich skryptów.
Jednak bezpośrednie uruchamianie kodu napisanego przez AI na własnym komputerze to zła decyzja. Uruchamianie bez nadzoru stanowi poważne zagrożenie dla bezpieczeństwa i stabilności: może zmienić konfigurację systemu, usunąć kluczowe pliki lub ujawnić dane osobowe. Dlatego istnieją piaskownice – bezpieczne, izolowane środowiska, w których agent może uruchamiać swój kod bez dostępu do podstawowego systemu operacyjnego. Ten materiał koncentruje się na piaskownicach dostępnych w bibliotece Deep Agents Python.
Czym jest piaskownica i dlaczego agenci jej potrzebują
Tutaj „sandbox” oznacza zamykane środowisko, zwykle pojemnik, w którym kod jest wykonywany bez żadnego kontaktu z resztą maszyny. Jego system plików i procesy są oddzielne, a nic nie łączy się z plikami, danymi dostępowymi ani siecią hosta, chyba że dostęp został specjalnie udzielony. Programiści mogą to traktować jako wygodę, ale dla autonomicznego agenta jest to niemal wymóg, a powody są praktyczne:
- To agent wybiera kod, a nie użytkownik. Nawet dobrze zachowujący się agent czasami usunie niewłaściwy plik lub wykona polecenie, którego nikt nie zamierzał, a bez izolacji szkody dotkną rzeczywistej maszyny.
- Iniekcja poleceń jest rzeczywistością. Jeśli agent czyta niezaufany treść, taką jak strona internetowa lub plik od kogoś innego, sprytnie sformułowana ukryta instrukcja może próbować zmienić jego dalsze działanie. Sandbox ogranicza skutki uszkodzeń do tymczasowego kontenera.
- Czyste środowiska pomagają. Każde zadanie jest łatwiejsze do zrozumienia, gdy rozpoczyna się w znanych, pustych warunkach i nie odziedzicza stanu z poprzednich wykonywań.
- Agenci równoległe. Dzięki izolacji
pip installjednego agenta nie może kolidować z tym drugiego, a awaria sandboxu nie wpłynie na cały proces. - Limity zasobów. Agent w nieskończonej pętli lub taki, który dwukrotnie ładuje cały zbiór danych do pamięci, powinien zużywać zasoby tymczasowego kontenera, a nie twojego komputera.
Dlatego właśnie frameworki przeznaczone do tworzenia agentów, w tym Deep Agents, traktują sandbox jako koncepcję pierwszorzędną. Deep Agents dostarcza abstrakcję o nazwie BaseSandbox oraz oficjalne integracje z kilkoma dostawcami chmurowych sandboxów. Wskazując agentowi jeden z nich, otrzymuje on rzeczywiste, izolowane środowisko do uruchamiania kodu.
Wszyscy ci dostawcy są hostowani w chmurze i pobierane są od nich opłaty za minutę lub w zależności od ilości używanych zasobów. To nadaje się do środowiska produkcyjnego, ale jest to więcej, niż potrzebne do lokalnego budowania, testowania lub demonstracji agenta.
OpenSandbox jako opcja lokalna
OpenSandbox to bezpłatny, otwarty sandbox działający w Twoim własnym Dockerze. Oferuje te same podstawowe funkcje co wersje płatne:
- Rozpoczęcie izolowanego kontenera.
- Uruchamianie w nim poleceń.
- Czytanie i zapisywanie plików w nim.
- Zniszczenie go później.
Zainstaluj lokalnie serwer warstwy sterującej, skieruj go na Docker, a pojawi się lokalny punkt końcowy, który tworzy i zarządza sandboxami na żądanie. Nikt nie sprzedaje go jako produktu hostowanego. To infrastruktura, którą sam zarządzasz, nadająca się do lokalnego rozwoju, testowania oraz prezentacji takich jak ta. Pozostaje już tylko połączenie jej z Deep Agents, które nie zawiera integracji z OpenSandbox.
Umowa BaseSandbox
BaseSandbox to klasa abstrakcyjna, a integracja sprowadza się do czterech jej elementów.
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]: ...
executeuruchamia polecenie w sandboxzie i zwracaExecuteResponse.idto unikalny identyfikator dla backendu sandboxu.upload_fileswysyła kilka plików do sandboxu.download_filespobiera kilka plików z niego.
Gdy te cztery elementy już istnieją, Deep Agents sam tworzy funkcje ls, read_file, write_file, edit_file, glob oraz grep na bazie execute(). Nie trzeba już nic więcej dla nich programować. To jest pełna powierzchnia integracji, co oznacza, że kwalifikuje się każdy sandbox zdolny do uruchamiania poleceń w shellu oraz przenoszenia plików do środka i na zewnątrz. Schemat wydaje się prosty, dopóki nie rozpocznie się rzeczywiste programowanie i nie pojawią się błędy.
Jak działa OpenSandbox
OpenSandbox wykorzystuje kontenery Docker do tworzenia i zarządzania środowiskami testowymi. Jego interfejs SDK w języku Python umożliwia tworzenie i zarządzanie takimi środowiskami, wykonywanie poleceń w wierszu poleceń, obsługę plików oraz monitorowanie zasobów. Aby to uruchomić, konieczne jest zainstalowane i działające Docker, a także sam serwer:
# Generate a starter config
uvx opensandbox-server init-config ~/.sandbox.toml --example docker
# Start the server
uvx opensandbox-server
Następnie SDK łączy się z uruchomionym serwerem i udostępnia wszystko, co może robić OpenSandbox.
Klasa integracyjna
To szkielet, który należy wypełnić, kierując się abstrakcją BaseSandbox. Metody dodawane są po jednej.
create()
class MinimalOpenSandboxBackend(BaseSandbox):
def __init__(self, sandbox: Sandbox, runner: AsyncRunner):
self._sandbox = sandbox
self._runner = runner
__init__ jest prosty. Zawiera dwa obiekty, od których zależy wszystko inne: żywe połączenie Sandbox z serwerem oraz AsyncRunner, który je obsługuje. Żaden z nich nie jest tworzony ręcznie, ponieważ oba powstają za pomocą metody fabrycznej. Klasa ta ma również więcej metod niż wymaga to BaseSandbox – w szczególności metodę klasową .create(). Powód staje się jaśniejszy, gdy zobaczymy, w jaki sposób obiekt sandbox powstaje w rzeczywistości.
Połączenie Sandbox oraz tło wykonawcze muszą istnieć przed tym, jak __init__ będzie mógł je wykorzystać, i dlatego create() stanowi odrębny krok. Oto kod.
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)
Dwa elementy wyróżniają się. domain="localhost:8080" łączy to z serwerem uruchomionym wcześniej za pomocą uvx opensandbox-server. timeout=timedelta(minutes=30) pełni rolę zabezpieczenia: nawet jeśli zapomnisz wywołać kill(), sandbox i tak zostanie zakończony, zamiast pozostać w tle. Zwróć również uwagę na IMAGE i ENTRYPOINT – muszą być kompatybilne z używaną wersją OpenSandbox.
O wykonywaczu: Sandbox.create jest koretą, jednak BaseSandbox.create musi zwrócić zwykły obiekt zamiast czegoś, na co trzeba czekać. Dopóki nie zostaną ujawnione szczegóły, traktuj runner.run(coro) jako „poczekaj, aż ta asynchroniczna funkcja się zakończy, a następnie daj mi to, co zwróciła”. To wyjaśnia, dlaczego Sandbox.create(...) jest przekazywane do runner.run(...), zamiast być bezpośrednio oczekiwane. Sposób działania AsyncRunner zostanie omówiony nieco później.
id
Proces tworzenia został zakończony, więc pozostałe metody obsługują już działające środowisko sandbox.
@property
def id(self) -> str:
return self._sandbox.id
Zwraca identyfikator podstawowej przestrzeni sandbox. BaseSandbox wymaga go jako właściwości, a jest on przydatny w logach.
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)
Wydaje się proste, ale w rzeczywistości nie. OpenSandbox dostarcza wyniki poleceń w postaci listy fragmentów, po jednym na linii, przy czym znak nowej linii jest już usunięty. Dlatego odpowiedź musi zostać prawidłowo odbudowana, aby agent mógł nią później zarządzać. Funkcja execute() również nie jest wywoływana bezpośrednio – ls, glob i grep w Deep Agentsach opierają się na niej i czytają wyniki linijka po linijce. Jeśli dostaną nieprawidłową odpowiedź, nic nie znajdą i cicho zwrócą puste wyniki. ls zwraca [], nie ma żadnego śladu błędu wskazującego na błędny join, a sandbox wydaje się pusty, mimo że tak nie jest.
Ma to większe znaczenie, niż się wydaje, ponieważ execute() służy nie tylko do bezpośredniego użycia. Jest podstawą dla ls, glob i grep w Deep Agents, a wszystkie one analizują wyniki linijka po linijce. Źle sformatowana odpowiedź sprawia, że nic nie zostanie dopasowane i funkcje te w milczeniu zwracają puste wyniki. ls wtedy zwraca [] bez żadnych informacji o błędzie wskazujących na problematyczną funkcję join, a sandbox wygląda na pusty, mimo że tak nie jest.
Pozostałe kwestie są prostsze: stdout i stderr są łączone, pomijając ten z pustymi danymi, a brak kodu wyjścia jest traktowany jako sukces (0) zamiast pozostać na poziomie 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]
Każda para (path, bytes) jest umieszczana w obiektu WriteEntry z zwykłymi uprawnieniami (właściciel może czytać i pisać, inni mogą tylko czytać), a cała partia jest wysyłana za pomocą jednej wywołania 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
Decydującym elementem jest użycie read_bytes zamiast read_file. SDK oferuje obie opcje, ale read_file zakłada tekst w formacie UTF-8. Sandbox uruchamiający kod LLM nie może wiedzieć, czy następny żądany plik to plik CSV, czy świeżo wygenerowana wykres, dlatego read_bytes jest jedynym bezpiecznym wyborem w obu przypadkach.
kill()
def kill(self) -> None:
self._runner.run(self._sandbox.kill())
self._runner.shutdown()
Procedura rozbiórki wykonuje dwie ważne czynności. Najpierw zamyka kontener sandbox, a następnie wyraźnie wyłącza wątek w tle uruchamiacza. Jeśli pominąć drugi krok, wątek wraz z pętlą zdarzeń w jego wnętrzu pozostaje aktywny nawet po zniknięciu sandboxu.
To jest cała klasa: dwa metody cyklu życia (create, kill) otaczające cztery wymagane przez BaseSandbox metody (id, execute, upload_files, download_files).
Most synchroniczny/asynchroniczny
Jedna kwestia pozostała niewyjaśniona: self._runner.run(...) – to właśnie on sprawia, że każda asynchroniczna wywołanie SDK wygląda dla BaseSandbox jak synchroniczna.
BaseSandbox jest synchroniczny. Funkcje execute(), upload_files() oraz download_files() to zwykłe wywołania funkcji bez użycia await. SDK OpenSandbox natomiast jest asynchroniczne: Sandbox.create(), sandbox.commands.run() oraz sandbox.files.write_files() to wszystko koretyny. AsyncRunner został stworzony po to, aby zamknąć tę lukę.
Podejście jest proste. Tworzony jest jeden pętla zdarzeń, przekazywany on jest własnemu wątkowi w tle i utrzymywany w aktywności tak długo, jak długo istnieje sandbox. Wszystko inne wykorzystuje jedynie funkcję run(), która przesyła koretynę do tej pętli za pomocą run_coroutine_threadsafe, blokuje kod wywołujący aż do jej zakończenia i zwraca wynik. W przypadku funkcji execute() wyrażenie self._runner.run(some_coroutine) wygląda dokładnie jak zwykłe wywołanie funkcji.
Jedna długo działająca pętla, zamiast nowej przy każdym wywołaniu, pozwala również połączeniu funkcjonować jako jedna ciągła sesja, a nie jako coś, co jest rozbierane i odbudowywane przy każdym wywołaniu execute(). Funkcja shutdown() zajmuje się sprzątaniem poprzez zatrzymanie pętli i czyste zakończenie wątku, więc kill() po prostu ją wywołuje.
Agent do analizy danych w sandboxzie
Aby przetestować integrację, notatnik Jupyter rozpoczyna od ustawienia zmiennych środowiskowych i stałych, importowania pakietów, a co najważniejsze – tworzenia obiektu MinimalOpenSandboxBackend. Wymaga to klucza API, który sam musisz stworzyć i umieścić w pliku ~/.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)
Następnie mamy model do czatowania. Modeli lokalne za pośrednictwem Ollamy również mogłyby zadziałać, ale model musi nadal radzić sobie z zadaniami typu agentic. W tym przypadku użyto modelu 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,
)
Następnie sam agent.
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,
)
Wszystko, co zostało dotychczas zbudowane, znajduje odzwierciedlenie w argumencie backend=backend. Na podstawie tego jednego obiektu Deep Agents generuje funkcje ls, read_file, write_file, glob oraz grep sam, wykorzystując do tego celu specjalną funkcję execute() jako bazę.
Następnie zostają załadowane rzeczywiste dane, bezpośrednio za pomocą funkcji upload_files(). Zbiór danych to fałszywy zbiór obejmujący 1000 klientów.
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}")
Następnie agent otrzymuje rzeczywiste zadanie.
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."
})
Na tym etapie agent działa samodzielnie. Dzięki swoim narzędziom i infrastrukturze może pisać kod w pandas i seaborn, uruchamiać go w sandboxzie za pomocą execute(), przechowywać wykresy w systemie plików sandboxu i kontynuować iteracje, aż powstanie kompletny raport w formacie markdown.
Pozostaje jeden problem: część raportu znajduje się wewnątrz sandboxu. Jego plik markdown odwołuje się do wykresów za pomocą lokalnych ścieżek sandboxu, które nie istnieją na serwerze hostowym. Aby uczynić go czytelnym, konieczne są dwa kroki: pobranie obrazków wykresów za pomocą download_files() oraz przepisanie ścieżek obrazków w pliku markdown tak, aby wskazywały one na lokalne kopie. Mniej problematycznym podejściem jest zwracanie przez agenta ustrukturyzowanych wyników, na przykład pola z raportem oraz wyraźnej listy ścieżek do obrazków, zamiast wyszukiwania linków w swobodnie skonstruowanym tekście markdown. Postprocesing polega wtedy jedynie na pobieraniu każdej wymienionej ścieżki i jej zastąpieniu.
Po tym oto co sandbox wygenerował na podstawie surowego pliku 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

### 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

---
## 4. Temporal Analysis (Subscription Trends)
... (Trimmed to keep blog (estimated read time short))
Zbliżenie na wygenerowany raport:
Od notatnika do pakietu PyPI
Notatnik pokazuje, jak wszystko się łączy, ale pakiet, który każdy może zainstalować za pomocą pip, jest bardziej przydatny. Stworzenie deepagents_opensandbox_backend oznaczało naprawę wszystkiego, co zawodziło dopiero wtedy, gdy inni ludzie uruchamiali ten kod w środowiskach, których autor nigdy nie testował. Publiczna część pozostała bez zmian – te same metody BaseSandbox oraz te same funkcje create() i kill(). Zmiany zaszły wewnątrz:
- Jeden wspólny pętla. Tła teraz dzielą się jednym modułem
_BackgroundLoopi nie uruchamiają już osobnych wątków. - Standardowe ostrzeżenie bezpieczeństwa. API nie wymaga autoryzacji, więc funkcja
create()teraz wywołuje błądInsecureSandboxWarning, gdy nie podanoapi_key, podczas gdy wcześniej łączyło się bez żadnych komunikatów. - Rozwiązanie dla Docker Desktop. W środowisku Docker Desktop bezpośrednie połączenie z portem kontenera często kończy się timeoutem. Ustawienie
use_server_proxy=Truekieruje ruch przez serwer, co pomaga uniknąć tego problemu. - Walidacja ścieżek. Funkcje
upload_filesidownload_filesod razu odrzucają ścieżki względne, dzięki czemu serwer nie może w tajemnicy przekierować ich do niewłaściwych katalogów. - Jasniejsze błędy. Klient sprawdza informacje o pliku, aby odróżnić „brak uprawnień” od „plik nie został znaleziony” i przyporządkowuje każdy z nich odpowiedniemu kodowi błędu, zamiast przekazywać mylące, surowe błędy.
- Funkcjonalne limity czasowe. Argument
timeoutwcześniej był ignorowany. Teraz jest przekazywany serwerowi w celu zatrzymania niekontrolowanych poleceń, zamiast pozostawiać je bez opieki po rezygnacji klienta. - Usunięte ręcznie napisane metody asynchroniczne. Prawdziwe wersje asynchroniczne, takie jak
aexecute, powodowały ciche, puste wyniki w elementachToolNodeLangGraph, dlatego zostały usunięte, a kod teraz korzysta z normalnych metod synchronicznych uruchamianych w puli wątków.
To rozdzielenie oznacza różnicę pomiędzy wersją demonstracyjną działającą na jednym laptopie a pakietem gotowym do zainstalowania za pomocą polecenia pip install przez każdego. Dołączony jest zestaw testów zgodności, który jest wykonywany na podstawie standardowych testów w środowisku sandbox dostarczanym przez sam Deep Agents, a proces instalacji wygląda następująco:
!pip install deepagents-opensandbox-backend
To ta sama integracja opisana powyżej, z tylko bardziej zaokrąglonymi krawędziami.
Podobne artykuły
- Te samodzielnie hostowalne platformy agentów open-source, posortowane według rzeczywistych potrzeb biznesu — Porównanie dziesięciu platform agentów AI open-source przeznaczonych do zastosowań biznesowych: enterprise no-code, Activepieces, Kestra, Windmill, Dify, Langflow, Coze Studio, MaxKB, AnythingLLM i RAGFlow.
- Tworzenie agenta wsparcia LEPA przy użyciu LangGraph — Połączenie węzłów SupportState, intake i classify, warunkowych krawędzi oraz elementów START/END, aby prośba o wsparcie szkolne mogła zostać wyjaśniona lub skierowana do odpowiednich dokumentów.