Sandbox dla lokalnego agenta kodującego: autonomia bez promienia rażenia

Autor: Jakub Rusinowski · Ostatnia aktualizacja 2026-08-04 · Wartości sprzętowe wyliczone naszym silnikiem VRAM

Pytania o zgodę się nie skalują — po dwudziestym każdy klika „tak” bez czytania. Zastąp je strukturą: uruchom agenta w kontenerze albo maszynie wirtualnej montującej wyłącznie jeden projekt, pracuj w jednorazowym worktree gita, żeby każda zmiana dała się cofnąć, wypisz na liście dozwolonych polecenia wolne do wykonania bez nadzoru i zablokuj ruch wychodzący, o ile zadanie go nie wymaga. Wtedy autonomia jest decyzją konfiguracyjną, a nie hazardem.

Agenta kodującego da się uruchamiać na dwa sposoby. Możesz zatwierdzać każdą akcję, co jest bezpieczne i szybko staje się nie do zniesienia — zmęczenie zatwierdzaniem jest realne i mierzalne, a nieprzeczytane pytanie znaczy tyle samo co brak pytania. Albo możesz włączyć tryb automatycznego zatwierdzania, który ma dziś każde narzędzie, i liczyć na szczęście. Żadne z tych dwóch nie jest projektem.\n\nTrzecia opcja to zmniejszyć promień rażenia na tyle, żeby autonomia przestała być ciekawa: agent może robić, co chce, wewnątrz pudełka, które da się wyrzucić. Ustawienie tego zajmuje jakieś dwadzieścia minut i to właśnie dzieli lokalnego agenta, którego można zostawić w ruchu, od takiego, którego trzeba niańczyć.\n\nUruchamianie modelu lokalnie pomaga w poufności — twój kod nie wyjeżdża z maszyny na potrzeby obliczeń. Nie robi nic z konsekwencjami. Lokalny 27B potrafi zrobić rm -rf na katalogu albo wypchnąć zepsutą gałąź dokładnie tak samo skutecznie jak model z czołówki.

Cztery rzeczy, które mogą pójść źle

Projektuj przeciwko tym konkretnie, a nie przeciwko mglistemu poczuciu ryzyka.\n\n1. Zwykłe destrukcyjne pomyłki. Zdecydowanie najczęstsze: zła ścieżka, zbyt gorliwe sprzątanie, edycja plików generowanych, force push. Bez złej woli, po prostu model działający na podstawie nieporozumienia, z dostępem do powłoki.\n2. Reward hacking. Kazano doprowadzić testy do zieleni — edytuje testy. Kazano naprawić błędy typów — dopisuje any. Wynik wygląda na zielony i jest bezwartościowy — dlatego loop engineering upiera się przy zamrożeniu plików testowych.\n3. Prompt injection przez wyjście narzędzi. Ta, którą ludzie lekceważą. Wszystko, co agent czyta — treść zgłoszenia, README zależności, pobrana strona, tytuł pull requesta — może nieść instrukcje wycelowane w agenta. To przeszło z teorii do ujawnionych podatności o wysokiej krytyczności w CI napędzanym agentami, a lokalny model jest dokładnie tak samo podatny.\n4. Ujawnienie poświadczeń. Agent czyta .env, żeby zrozumieć konfigurację, a potem wkleja je do loga, commita albo żądania do narzędzia. Lokalne wnioskowanie oznacza, że sekret nie trafia do dostawcy modelu; nie oznacza, że sekret zostaje na miejscu.

Warstwa 1: kontener, który widzi tylko jeden projekt

Standardowa odpowiedź i powód, dla którego devcontainery stały się domyślną rekomendacją przy pracy z agentami: agent dostaje pełne środowisko programistyczne, a tym środowiskiem nie jest twoja maszyna.

// .devcontainer/devcontainer.json — przyjazny agentowi, bezpieczny dla hosta
{
  "name": "agent-sandbox",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
  "runArgs": ["--cap-drop=ALL", "--security-opt=no-new-privileges"],
  "mounts": [],
  "containerEnv": { "OLLAMA_HOST": "http://host.docker.internal:11434" },
  "postCreateCommand": "pnpm install"
}

Robotę wykonują tu trzy szczegóły. Żadnych dodatkowych montowań — tylko katalog roboczy, więc reszta twojego dysku dla agenta po prostu nie istnieje. Odebrane uprawnienia, żeby ucieczka z kontenera wymagała czegoś więcej niż błędu w konfiguracji. Model zostaje na hoście, osiągalny przez host.docker.internal, więc kontener nie potrzebuje GPU, a ty trzymasz jedną instancję Ollamy obsługującą wszystko.

Dwie zasady ważniejsze od samego pliku:

Jeśli kontenery są niewygodne w twoim stacku, lekka maszyna wirtualna daje tę samą własność przy mocniejszej granicy i większym narzucie. Zasada się nie zmienia: widoczny jeden projekt, nic poza tym.

Warstwa 2: worktree gita, który możesz skasować

Izolacja kontenerowa chroni maszynę. Worktree chronią pracę — i zamieniają „cofnij wszystko” w jedną linijkę, co dopiero czyni limit iteracji egzekwowalnym.

# Jednorazowa gałąź i katalog na jedno zadanie agenta
git worktree add ../repo-agent-auth -b agent/auth-fix
cd ../repo-agent-auth
# ... tutaj pracuje agent ...

git -C ../repo-agent-auth diff main        # przejrzyj, zanim cokolwiek trafi do merge'a
git worktree remove ../repo-agent-auth     # albo po prostu go skasuj

Dlaczego to bije pracę w twoim własnym checkoucie: główne drzewo zostaje używalne, kiedy agent pracuje, nieudana sesja kosztuje katalog zamiast git reset, który musisz trafić bezbłędnie, a puszczenie kilku agentów równolegle przestaje być ćwiczeniem z rozwiązywania konfliktów. Commituj po każdym kroku zweryfikowanym na zielono, żeby pętla zawsze miała punkt, do którego wie, że może wrócić.

Warstwa 3: uprawnienia strukturalne, nie konwersacyjne

Wewnątrz pudełka i tak zdecyduj, co wykonuje się bez pytania. Podział, który działa:\n\nZatwierdzaj automatycznie — odczyty i odwracalne działania lokalne: czytanie plików w obrębie projektu, git status, diff i log, uruchamianie testów, kontrole typów, lintery, buildy, instalowanie pakietów w kontenerze.\n\nZawsze pytaj — o wszystko, co ma konsekwencje poza sandboxem: git push, publikowanie, wdrożenia, zapisy do bazy, kasowanie plików poza worktree, zmiany w konfiguracji CI albo infrastruktury, dodanie zależności.\n\nNigdy nie pozwalajrm -rf ze ścieżką ze zmiennej, pliki z poświadczeniami, gniazdo Dockera, wyłączenie samego sandboxa.\n\nWiększość agentów wyraża to listą dozwolonych i listą zablokowanych; używaj obu i wolej wypisywać dozwolone polecenia, niż blokować „wszystko poza”. Dwa wspierające nawyki: wpisz te same reguły do AGENTS.md, żeby model znał granicę, zamiast odkrywać ją przez odmowy, oraz uczyń wrażliwe pliki nieczytelnymi w kontenerze, zamiast polegać na regule — reguła, której model nie może złamać, jest warta więcej niż reguła, o której respektowanie się go prosi.

Warstwa 4: ruch wychodzący

Najtańsza kontrola o najwyższej opłacalności i ta, którą zwykle się pomija. Większość zadań programistycznych nie potrzebuje internetu w ogóle — model jest lokalny, zależności zainstalowane, testy chodzą offline. Odcięcie ruchu wychodzącego zamienia najgorsze skutki prompt injection (wyprowadzenie źródeł, połączenie z hostem atakującego) w nieudane próby połączenia.

# Uruchom kontener zadania całkiem bez sieci
docker run --rm -it --network none -v "$PWD":/work -w /work agent-sandbox

# Albo dopuść wyłącznie rejestr pakietów, którego wymaga twój build
docker network create --internal offline

Kiedy zadanie faktycznie potrzebuje sieci — pobranie pakietu, przeglądanie dokumentacji — przyznaj ją na to zadanie, w tym kontenerze, i odbierz po wszystkim. Jeśli podpięty jest serwer MCP do automatyzacji przeglądarki, pamiętaj, że jest on z definicji ścieżką sieciową, i traktuj taką sesję jako niezaufaną; zobacz MCP przy lokalnych agentach.

Konfiguracja na dwadzieścia minut, która pokrywa większość

Pełny zestaw warstw opłaca się przy pętlach bez nadzoru. Jeśli chcesz mieć te 80% jeszcze dziś:\n\n1. git worktree add ../repo-agent -b agent/task — praca jest jednorazowa.\n2. Otwórz ten worktree w devcontainerze bez dodatkowych montowań i bez gniazda Dockera.\n3. W AGENTS.md: szybkie polecenie testowe, „never modify tests to make them pass”, „never touch .env, migrations, or CI config”.\n4. W ustawieniach agenta: automatyczne zatwierdzanie odczytów, testów i buildów; zawsze pytaj o push, wdrożenie i zmiany zależności.\n5. Uruchamiaj kontener z --network none, chyba że zadanie wymaga ruchu wychodzącego.\n6. Ogranicz pętlę i przejrzyj diff, zanim opuści worktree.\n\nTo konfiguracja, przy której „niech sobie chodzi przez godzinę, a ja zrobię coś innego” jest rozsądnym zdaniem — a o to właśnie chodzi w lokalnym agencie, którego każda iteracja nic nie kosztuje.

Czego sandbox nie naprawia

Miej jasność co do ryzyka szczątkowego, bo sandbox rozzuchwala.\n\n- Zły kod, który przechodzi testy. Izolacja to nie przegląd. Przeczytaj diff.\n- Injection wewnątrz sandboxa. Wstrzyknięta instrukcja wciąż potrafi zrujnować pracę, zepsuć gałąź albo zmarnować popołudnie — po prostu nie dosięgnie twojego laptopa.\n- Sekrety, które już są w repozytorium. Jeśli poświadczenia są zacommitowane, kontener je ma. To naprawia się osobno.\n- Ryzyko łańcucha dostaw z tego, co agent instaluje. Agent, który może dodać zależność, może dodać złą. Trzymaj zmiany zależności na liście „zawsze pytaj”.\n\nSandbox kupuje ci prawo do przestania oglądania każdej akcji. Nie kupuje prawa do przestania czytania wyniku.

Najczęstsze pytania

Czy trzeba izolować agenta kodującego, skoro model działa lokalnie?
Tak. Lokalne wnioskowanie oznacza, że twój kod nie trafia do dostawcy modelu — nie mówi nic o tym, co agent robi na twojej maszynie. Lokalny model z dostępem do powłoki może usunąć pliki, zrobić force push albo zadziałać na instrukcjach wstrzykniętych w treść zgłoszenia dokładnie tak samo jak model chmurowy. Izolacja chroni przed konsekwencjami, lokalne wnioskowanie przed ujawnieniem. To dwa różne problemy.
Jaka jest najprostsza bezpieczna konfiguracja dla autonomicznego agenta kodującego?
Jednorazowy worktree gita otwarty w devcontainerze montującym wyłącznie ten katalog, z modelem serwowanym z hosta przez host.docker.internal, z siecią wyłączoną, o ile zadanie jej nie wymaga, oraz z listą dozwolonych, która automatycznie przepuszcza odczyty, testy i buildy, a zawsze pyta o push, wdrożenie i zmiany zależności. To jakieś dwadzieścia minut pracy i pokrywa większość realistycznych awarii.
Czy prompt injection to realne zagrożenie dla lokalnego agenta kodującego?
Tak — lokalność modelu nie zmienia tu niczego. Każda treść, którą agent czyta, może nieść instrukcje: treści zgłoszeń, tytuły pull requestów, pliki README zależności, pobrane strony. Ataki na CI napędzane agentami ujawniono z krytycznym poziomem istotności. Praktyczna obrona to traktowanie każdego wyjścia narzędzia jako niezaufanego, domyślne odcięcie ruchu wychodzącego i trzymanie działań o konsekwencjach zewnętrznych za bramką z człowiekiem.
Czy montować gniazdo Dockera do kontenera z agentem?
Nie. Dostęp do gniazda Dockera jest równoważny z rootem na hoście — wyjście z kontenera sprowadza się do jednego polecenia. Jeśli agent naprawdę musi zarządzać kontenerami, postaw przed nim filtrujące proxy gniazda blokujące niebezpieczne operacje albo w ogóle wynieś tę pracę poza jego zasięg.
Po co worktree gita zamiast zwykłej pracy na gałęzi?
Worktree daje agentowi własny katalog obok własnej gałęzi, więc twój główny checkout zostaje używalny w trakcie jego pracy, nieudaną sesję się kasuje zamiast resetować, a kilku agentów może pracować równolegle bez kolizji. Nadaje też sens limitowi iteracji: „cofnij wszystko” staje się skasowaniem katalogu.
Jak powstrzymać agenta przed edytowaniem testów, żeby przechodziły?
Nie polegaj na proszeniu. Wpisz „never modify tests to make them pass” do AGENTS.md, żeby znał regułę, a potem egzekwuj ją mechanicznie: dołóż bramkę padającą przy zmianie plików testowych (git diff --exit-code -- tests/), a tam gdzie się da, uczyń te ścieżki niezapisywalnymi w sandboxie. Ograniczenie, którego model nie może złamać, bije takie, o którego przestrzeganie się go prosi.

Co dalej